4

So I was asked whether these 3 type expressions where equivalent in Haskell:

τ1 = (a -> a) -> (a -> a -> a)
τ2 = a -> a -> ((a -> a) -> a)
τ3 = a -> a -> (a -> (a -> a))

if I take away the parenthesis I'm left with this

τ1 = (a -> a) -> a -> a -> a
τ2 = a -> a -> (a -> a) -> a
τ3 = a -> a -> a -> a -> a

So it's obvious to me that they are all different from each other. However, according to the question, these two answers are wrong:

τ1 !≡ τ2 !≡ τ3 !≡ τ1
τ1 !≡ τ2 ≡ τ3

So I'm a bit confused right here, what would be the right answer and why?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • More context: this is a question from a test that shows 3 answers. The first 2 ones are the ones I showed, and the third answer is "the other answers are wrong". This is the correct answer according to the test solutions. – Jaime Fernández Nov 05 '18 at 18:38
  • I would double-check I copied the Q&A correctly. If no errors are found, I would suggest you report this Q&A to your instructor (assuming they wrote the test). – chi Nov 05 '18 at 18:42
  • Presumably this notation was defined in class or earlier in the test. Otherwise if the notation is completely undefined of course the question is unanswerable. But we don't have access to the materials you do to search for the definition, so... – Daniel Wagner Nov 05 '18 at 19:43

3 Answers3

6

Indeed, they are all distinct for the reason you mention.

We can even ask GHC to confirm it. (Below, I chose a ~ Int to obtain a closed type.)

> import Data.Type.Equality
> type T1 a = (a -> a) -> (a -> a -> a)
> type T2 a = a -> a -> ((a -> a) -> a)
> type T3 a = a -> a -> (a -> (a -> a))
> :kind! T1 Int == T2 Int
T1 Int == T2 Int :: Bool
= 'False
> :kind! T1 Int == T3 Int
T1 Int == T3 Int :: Bool
= 'False
> :kind! T2 Int == T3 Int
T2 Int == T3 Int :: Bool
= 'False
chi
  • 111,837
  • 3
  • 133
  • 218
5

The three types...

type T1 a = (a -> a) -> (a -> a -> a)
type T2 a = a -> a -> ((a -> a) -> a)
type T3 a = a -> a -> (a -> (a -> a))

... are indeed distinct. However, T1 and T2 are equivalent in the sense that there is an isomorphism between them, which amounts to changing the order of arguments:

GHCi> :info T1
type T1 a = (a -> a) -> a -> a -> a
    -- Defined at <interactive>:12:1
GHCi> :info T2
type T2 a = a -> a -> (a -> a) -> a
    -- Defined at <interactive>:13:1
GHCi> :t flip
flip :: (a -> b -> c) -> b -> a -> c
GHCi> :t (flip .)
(flip .) :: (a1 -> a2 -> b -> c) -> a1 -> b -> a2 -> c
GHCi> f = (flip .) . flip
GHCi> :t f :: T1 a -> T2 a
f :: T1 a -> T2 a :: T1 a -> T2 a
GHCi> g = flip . (flip .)
GHCi> :t g :: T2 a -> T1 a
g :: T2 a -> T1 a :: T2 a -> T1 a

We can then show that f and g are inverses (i.e. g . f = id and f . g = id):

f . g
(flip .) . flip . flip . (flip .)
(flip .) . (flip .) -- flip . flip = id
id -- (flip .) . (flip .) = \h -> \x -> flip (flip (h x)) = \h -> \x -> h x = id

g . f
flip . (flip .) . (flip .) . flip
flip . flip 
id
duplode
  • 33,731
  • 7
  • 79
  • 150
  • `:t [f . g, g . f]` reports a type. :) can we conclude from this that both are `id`, no matter what `f` and `g` themselves are? – Will Ness Nov 05 '18 at 19:27
  • @WillNess Good question, but I think that doesn't hold in general. A far removed counterexample would be `[join . return, return . join]`. Back to the present case, a shortcut would be noting the most general type for both `f` and `g` is `(a -> b -> c -> d) -> c -> b -> a -> d`, which parametricity ensures can only be implemented as `\x c b a -> x a b c`, which is its own inverse. (That only works with the most general type, as with `f :: T1 -> T2`, we can sneak stuff in -- e.g. `\x c b a -> x (a . a) b c`. – duplode Nov 05 '18 at 22:23
3

I agree with your assessment. All three types are different and you have simplified them correctly. Are you sure you're reading the question and answer right, if you think the answer provided disagrees with yours?

amalloy
  • 89,153
  • 8
  • 140
  • 205