8

What I want is to write something like this:

let (a,b) = if *condition* then (first, second) else (second, first)

I found out that I cannot write even this:

let (a,b) = (first,second)

It fails with an error:

 <interactive>:7:5:                                                                                                                          
Could not deduce (Arrow a0)                                                                                                             
from the context (Arrow a)
  bound by the inferred type for `a':
             Arrow a => a b c -> a (b, d) (c, d)
  at <interactive>:7:5-26
The type variable `a0' is ambiguous
When checking that `a' has the inferred type
  a :: forall (a :: * -> * -> *) b c d.
       Arrow a =>
       a b c -> a (b, d) (c, d)
Probable cause: the inferred type is ambiguous

<interactive>:7:5:
Could not deduce (Arrow a0)
from the context (Arrow a)
  bound by the inferred type for `b':
             Arrow a => a b c -> a (d, b) (d, c)
  at <interactive>:7:5-26
The type variable `a0' is ambiguous
When checking that `b' has the inferred type
  b :: forall (a :: * -> * -> *) b c d.
       Arrow a =>
       a b c -> a (d, b) (d, c)
Probable cause: the inferred type is ambiguous
Savenkov Alexey
  • 678
  • 4
  • 11
  • 5
    Simon Peyton-Jones has a great email to one of the mailing lists (which I am now having trouble finding) which discusses the subtleties of typechecking pattern bindings like this. It is so subtle, in fact, that the Haskell98 Report overlooked the issue entirely, and the committee spent some time deliberating on how typechecking should be done for pattern bindings before writing the Haskell 2010 Report. I would write an answer, except I don't trust myself to get the subtleties right without SPJ's email in front of me... – Daniel Wagner Jun 13 '16 at 18:55
  • At a guess, the problem here is that the righthand side of the binding gets the inferred type `forall a1 a2. (Arrow a1, Arrow a2) => (a1 ..., a2 ...)` as opposed to `(forall a1. Arrow a1 => ..., forall a2. Arrow a2 => ...)`, so the forall is moved *out* of the tuple, and either of the values in the pair are not fully polymorphic themselves. – Tikhon Jelvis Jun 13 '16 at 19:57

2 Answers2

2

Very shortly, you try to construct Impredicative type which GHC cannot infer. You can do:

λ Control.Arrow > let (a,b) = (first, second) :: Arrow a => (a b b -> a (b, b) (b, b), a b b -> a (b, b) (b, b))
λ Control.Arrow > :t a
a :: Arrow a => a b b -> a (b, b) (b, b)
λ Control.Arrow > :t b
b :: Arrow a => a b b -> a (b, b) (b, b)

or

:set -XImpredicativeTypes 
λ Control.Arrow > let (a,b) = (first, second) :: (Arrow a => a b b -> a (b, b) (b, b), Arrow a => a b b -> a (b, b) (b, b))
λ Control.Arrow > :t a
a :: Arrow a => a b b -> a (b, b) (b, b)
λ Control.Arrow > :t b
b :: Arrow a => a b b -> a (b, b) (b, b)

but you cannot do:

λ Control.Arrow > let (a,b) = (first, second) :: (Arrow a, Arrow a') => (a b b -> a (b, b) (b, b), a' b b -> a' (b, b) (b, b))

To isolate the issue, this works:

λ Control.Arrow > let p = (first, second) :: (Arrow a, Arrow a') => (a b b -> a (b, b) (b, b), a' b b -> a' (b, b) (b, b));
λ Control.Arrow > :t p
p :: (Arrow a', Arrow a) =>
     (a b b -> a (b, b) (b, b), a' b b -> a' (b, b) (b, b))

but when you try to bind that to pattern:

λ Control.Arrow > let (a, b) = p

it fails. Constraints are outside of the pair type, and are redundant for other halves of the pair, as

λ Control.Arrow > :set -XImpredicativeTypes 
λ Control.Arrow > let p = (first, second) :: (Arrow a => a b b -> a (b, b) (b, b), Arrow a => a b b -> a (b, b) (b, b))
λ Control.Arrow > let (a, b) = p

works.


Simple example:

λ Prelude Data.Monoid > :t (mappend, ())
(mappend, ()) :: Monoid a => (a -> a -> a, ())
λ Prelude Data.Monoid > let (a, b) = (mappend, ())

<interactive>:12:5:
    No instance for (Monoid a0)
      arising from the ambiguity check for ‘b’
    The type variable ‘a0’ is ambiguous
    When checking that ‘b’ has the inferred type ‘()’
    Probable cause: the inferred type is ambiguous

One have to carry constraints over, but there is no a in the type of (), i.e. Monoid a => () is ambigious type.


Note: let (a,b) = ((+), (*)) seems to work. I have no idea why and how Num is treated specially:

λ Prelude Data.Monoid > let x = () ::  Num a => ()
λ Prelude Data.Monoid > :t x
x :: ()
λ Prelude Data.Monoid > let x = () :: Monoid m => ()

<interactive>:12:9:
    No instance for (Monoid m0)
    ...
phadej
  • 11,947
  • 41
  • 78
1

It looks like you are running monomorphism restriction. This is just a limitation of Haskell's type inference and you can get around it by adding an explicit type signature.

import Control.Arrow

foo :: (Arrow a, Arrow a1) => (a b c -> a (b, d) (c, d), a1 b1 c1 -> a1 (d1, b1) (d1, c1))
foo = (first, second)

This code typechecks fine with the type signature for foo but gives that "ambiguous variable" compilation error if you remove it.

BTW, the type signature I used was the one inferred by :t (first, second) in GHCI. Since you want (first, second) and (second, first) to have the same type, you will probably want to use a more specific type in your annotation, such as the following one:

foo :: (Arrow a) => (a b b -> a (b, b) (b, b), a b b -> a (b, b) (b, b))
hugomg
  • 68,213
  • 24
  • 160
  • 246
  • 1
    He has `(a, b) = ...`, not `foo = ...`, which makes a difference. This is not the monomorphism restriction. Turn it off and see for yourself. I can guarantee you didn't do this, because the code you posted doesn't even have the right import. – Daniel Wagner Jun 13 '16 at 18:52
  • @DanielWagner: Sorry about the import. That was just a typo and the code that I was actually running had the correct "Control.Arrow" at the top all along :) – hugomg Jun 13 '16 at 18:54
  • 3
    Nevertheless your answer is incorrect, because of the other objection I raised: `(a, b) = ...` and `foo = ...` do not behave the same. – Daniel Wagner Jun 13 '16 at 18:57
  • Its hard to be 100% certain without a more complete example from the OP. The error message from the `foo = ` case is very similar to what is in the question. – hugomg Jun 13 '16 at 19:00
  • 1
    This example is wrong, but it actually helped me see why this is a difficult problem. Take your (valid) definition of `foo` and then run `:t fst foo`–it won't work because `(Arrow a, Arrow a1) => (..., ...)` is different from `(Arrow a => ..., Arrow a1 => ...)`. – Tikhon Jelvis Jun 13 '16 at 19:55