Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type errors with Existential types in Haskell

I am struggling with existential types in my program. I think I'm trying to do something very reasonable however I cannot get past the typechecker :(

I have a datatype that sort of mimics a Monad

data M o = R o | forall o1. B (o1 -> M o) (M o1)

Now I create a Context for it, similar to that described in Haskell Wiki article on Zipper, however I use a function instead of a data structure for simplicity -

type C o1 o2 = M o1 -> M o2

Now when I try to write a function that splits a data value into its context and subvalue, the typechecker complains -

ctx :: M o -> (M o1 -> M o, M o1)
ctx (B f m) = (B f, m) -- Doesn't typecheck

Error is -

Couldn't match type `o2' with `o1'
  `o2' is a rigid type variable bound by
       a pattern with constructor
         B :: forall o o1. (o1 -> M o) -> M o1 -> M o,
       in an equation for `ctx'
       at delme1.hs:6:6
  `o1' is a rigid type variable bound by
       the type signature for ctx :: M o -> (M o1 -> M o, M o1)
       at delme1.hs:6:1
Expected type: M o2
  Actual type: M o1
In the expression: m
In the expression: (B f, m)

However, I can work around it like so -

ctx (B f m) = let (c,m') = ctx m in ((B f) . c, m') -- OK

Why does this second definition typecheck but not the first one?

Also, if I try to convert ctx to a complete function by checking for R, I again get a typecheck error -

ctx (R o) = (id, R o) -- Doesn't typecheck

Error -

Couldn't match type `o' with `o1'
  `o' is a rigid type variable bound by
      the type signature for ctx :: M o -> (M o1 -> M o, M o1)
      at delme1.hs:7:1
  `o1' is a rigid type variable bound by
       the type signature for ctx :: M o -> (M o1 -> M o, M o1)
       at delme1.hs:7:1
In the first argument of `R', namely `o'
In the expression: R o
In the expression: (id, R o)

How can I work around this error?

Any help is appreciated!

like image 473
Anupam Jain Avatar asked Nov 25 '11 21:11

Anupam Jain


People also ask

What is an existential type?

Existential types, or 'existentials' for short, are a way of 'squashing' a group of types into one, single type. Existentials are part of GHC's type system extensions.

What does forall mean in Haskell?

forall is something called "type quantifier", and it gives extra meaning to polymorphic type signatures (e.g. :: a , :: a -> b , :: a -> Int , ...). While normaly forall plays a role of the "universal quantifier", it can also play a role of the "existential quantifier" (depends on the situation).

What is existential type Swift?

Existentials in Swift allow defining a dynamic value conforming to a specific protocol. Using primary associated types, we can constrain existentials to certain boundaries. The Swift team introduced the any keyword to let developers explicitly opt-in to a performance impact that might otherwise not be visible.


1 Answers

Let's look at the failing cases first. Both of these fail for the same reason, which is clearer once you add in the implicit forall in the type signature:

ctx :: forall o o1. M o -> (M o1 -> M o, M o1)

i.e. your function must not only work for a some o1, but for any o1.

  1. In your first case,

    ctx (B f m) = (B f, m)
    

    we know that f :: (o2 -> M o) and m :: M o2, for some type o2, but we have to be able to offer any type o1, so we can't assume that o1 ~ o2.

  2. In the second case,

    ctx (R o) = (id, R o)
    

    Here, we know that o :: o, but again, the function has to be defined for any o1, so we can't assume that o ~ o1.

Your workaround only seems to work because it's calling itself, similar to an inductive proof. But without a base case, it's just circular reasoning, and you cannot write the base case for this function, because there is no way to construct an M o1 from an M o for any combination of o and o1 without using a bottom value.

What you'll probably need to do, is to define another existential type for the context, instead of using just a tuple. Not sure if it'll work for your needs, but this compiles1, at least:

data Ctx o = forall o1. Ctx (M o1 -> M o) (M o1)

ctx :: M o -> Ctx o
ctx (B f m) = case ctx m of Ctx c m' -> Ctx (B f . c) m'
ctx (R o)   = Ctx id (R o) 

1 Try using a let instead of case for a funny GHC error :)

like image 176
hammar Avatar answered Sep 27 '22 20:09

hammar