13

What is the type inferred by a Haskell type synthesizer when unifying the types c -> a -> b and (a -> b) -> c?

Can someone explain me how can I solve it?

Thanks!

dfeuer
  • 48,079
  • 5
  • 63
  • 167
yonutix
  • 1,964
  • 1
  • 22
  • 51
  • 11
    You can ask ghci: `:t [undefined :: c -> a -> b, undefined :: (a -> b) -> c]` – Cirdec Sep 03 '15 at 07:09
  • 1
    @Cirdec very nice - although it will not give the complete answer (it does not really get the *hint* that those `a`s and `b`s are meant to be the same ;)) - anyway: great comment (maybe even worth an answer?) – Random Dev Sep 03 '15 at 08:07
  • @Cirdec I think you should turn this into an answer - it answers (well, the result does) not only OP's question, but also whole class of questions. As for Carsten's comment - wouldn't forall solve this issue? – joozek Sep 03 '15 at 09:12
  • @joozek, @carsten I added [an answer](http://stackoverflow.com/a/32378966/414413) explaining that those are distinct type variables and that adding `forall`s will not solve the problem. – Cirdec Sep 03 '15 at 14:52
  • @Carsten I have added an answer showing how to give GHC the hint that those `a`s and `b`s are meant to be the same. – Daniel Wagner Sep 03 '15 at 20:59
  • @DanielWagner nice :D – Random Dev Sep 04 '15 at 04:06

3 Answers3

15

This seems to be some kind of exercise/homework so I will not spoil everything but give you some hints first:

  • the type c -> a -> b is actually c -> (a -> b)
  • so you have to unify c -> (a -> b) with (a -> b) -> c, that is:
    • c with a -> b (first part)
    • a -> b with c (second part)

now what could that (try to get rid of c ;) ) be now?

PS: I am assuming you want those types a, b, .. to be the same

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Random Dev
  • 51,810
  • 9
  • 92
  • 119
9

In other answers, we have seen how to perform the unification by hand, and how to ask ghci some limited unification questions when we do not need to connect type variables in the two types we want to unify. In this answer, I show how to use existing tooling to answer the question you asked as I understand you to intend it.

The trick is to use type-equality constraints to ask GHC to unify two types, then expose the results as a tuple type. The type equality constraint kicks off the unifier; when unification is done, the type variables in our tuple type will be simplified according to what was learned during unification.

Thus, your question looks like this, for example:

> :set -XTypeFamilies
> :{
| :t undefined -- a dummy implementation we don't actually care about
| :: ((a -> b) -> c) ~ (c -> a -> b) -- the unification problem
| => (a, b, c) -- how we ask our query (what are the values of a, b, and c after unification?)
| :}
<snip -- a copy of the above text>
  :: (a, b, a -> b)

From this, we learn that for any types a and b, we can choose a ~ a, b ~ b, and c ~ a -> b as a solution to the unification problem. Here is another query you might wonder: after unification, what is the simplified type of (a -> b) -> c? You could run the previous query, and substitute in a, b, and c by hand, or you could ask ghci:

> :t undefined :: ((a -> b) -> c) ~ (c -> a -> b) => (a -> b) -> c
undefined :: ((a -> b) -> c) ~ (c -> a -> b) => (a -> b) -> c
  :: (a -> b) -> a -> b

The only thing I changed in this command is the "query" part. The result tells us that (a -> b) -> c becomes (a -> b) -> a -> b after unification. Note well that the a and b in the result type are not guaranteed to be exactly the same as the a and b in the query -- though probably in GHC that will always be the case.

Another quick trick worth mentioning is that you can use Proxy to turn an arbitrarily-kinded type variable into a * type for use in a tuple; thus, for example:

> :t undefined :: f a ~ (b -> c) => (a, b, c, f)

<interactive>:1:42:
    Expecting one more argument to ‘f’
    The fourth argument of a tuple should have kind ‘*’,
      but ‘f’ has kind ‘* -> *’
    In an expression type signature: f a ~ (b -> c) => (a, b, c, f)
    In the expression: undefined :: f a ~ (b -> c) => (a, b, c, f)
> :m + Data.Proxy
> :t undefined :: f a ~ (b -> c) => (a, b, c, Proxy f)
undefined :: f a ~ (b -> c) => (a, b, c, Proxy f)
  :: (c, b, c, Proxy ((->) b))
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 1
    Using a tuple to capture the types that are the result of unification is very clever. – Cirdec Sep 04 '15 at 04:56
  • Is there any reason for the `let x :: ... x = undefined in x` wrapper? ghci 7.8.3 is happy to accept the query `:t undefined :: ((a -> b) -> c) ~ (c -> a -> b) => (a, b, c)` – Cirdec Sep 04 '15 at 05:00
  • @Cirdec That's a nice simplification! I'll include it in my answer a little bit later today. – Daniel Wagner Sep 04 '15 at 16:40
6

You can ask ghci

:t [undefined :: c -> a -> b, undefined :: (a -> b) -> c]

It will need to unify the types to figure out what type the elements of the list are. We can unify any number of types this way; even 0, try it!

The type variables on the left in c -> a -> b are distinct from the type variables on the right in a -> b -> c. GHC will rename type variables to keep them distinct, but it will try to preserve the original names. It does this by adding numbers to the end of the type variable names. The answer to this query includes some of the type variables a, a1, b, b1, c, and c1. If you don't want the type variables to be distinct, you can read off the answer ignoring the added numbers.

If you do want the type variables to be distinct, it can be a bit tricky to tell what ghc is doing because you don't know which type variables where renamed to what. In practical coding, this can be a problem when trying to understand type errors. In both cases there is a simple solution: rename the type variables with distinctive names yourself so that ghc doesn't need to rename them.

:t [undefined :: c1 -> a1 -> b1, undefined :: (a2 -> b2) -> c2]

We're done with what vanilla Haskell can do, but you can get the compiler to answer questions more generally by using type equality constraints as described in Daniel Wagner's answer. The next section just describes why forall scoped types are not the general solution.

forall

Before reading this section you should think about whether it is possible to unify, for all c, c -> a -> b and (a -> b) -> c.

To the experienced haskeller, it might seem like you could keep the type variables from being distinct by introducing them in an explicit forall scope with the ScopedTypeVariables extension. I don't know an easy way to do this in ghci, but the following snipet with a hole asks the compiler to unify a -> b and a -> b.

{-# LANGUAGE ScopedTypeVariables #-}

example1 :: forall a b. ()
example1 = (undefined :: _) [undefined :: a -> b, undefined :: a -> b]

The output seems to tell us that the list is a list of a -> b.

Found hole `_' with type: [a -> b] -> ()

If we try to use this for the example problem, it doesn't work.

example2 :: forall a b c. ()
example2 = (undefined :: _) [undefined :: c -> a -> b, undefined :: (a -> b) -> c]

The compiler politely tells us why

Couldn't match type `c' with `a -> b'

It is not true that for all types c, c is a function. Some example types that aren't functions include Int, Bool, and IO a.


I use (undefined :: _) instead of _ when asking what the type that goes in a hole is. If you just use _ ghc doesn't type check all of the expression. The compiler may lead you to believe a hole is possible to fill when it is in fact impossible. In the output for example2 there is also the following, extremely misleading line

Found hole `_' with type: [c -> a -> b] -> ()
Community
  • 1
  • 1
Cirdec
  • 24,019
  • 2
  • 50
  • 100