Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rank N Polymorphism / unification issue #238

Closed
johnpmayer opened this issue Sep 4, 2013 · 10 comments
Closed

Rank N Polymorphism / unification issue #238

johnpmayer opened this issue Sep 4, 2013 · 10 comments
Labels

Comments

@johnpmayer
Copy link
Contributor

This is a neat one I think; consider the following module:

module Rec where

type Ext a = { a | x : Int }

data Foo = Bar (Ext { y : Int })
         | Baz (Ext { z : Int })

mapExtFoo : (Ext a -> Ext a) -> Foo -> Foo
mapExtFoo f foo = case foo of
  Bar bar -> Bar <| f bar
  Baz baz -> Baz <| f baz

As both "branches" of the Foo data structure contain things that are Ext, if we have a function that maps Ext to Ext, we should be able to apply it independently to both sides, and thus apply it to Foo as a whole.

I think the issue here is that the f argument wants to unify to two different types, but notice that it can uniquely unify within the scope of each case branch. Compiler output below:

[1 of 1] Compiling Rec                 ( Rec.elm )
Type error on line 12:
Something weird is happening with this value:

        baz

   Expected Type: {a | z : Int}
     Actual Type: {}


Type error on line 12:
Something weird is happening with this value:

        baz

   Expected Type: {}
     Actual Type: {a | y : Int}


Type error on line 12:
Something weird is happening with this value:

        Baz <| (f baz)

   Expected Type: {a | z : Int}
     Actual Type: {}


Type error on line 12:
Something weird is happening with this value:

        Baz <| (f baz)

   Expected Type: {}
     Actual Type: {a | y : Int}


Type error between lines 10 and 12:
Cannot unify rigid type variable 'a'.
It is likely that a type annotation is not general enough.

        case foo of
          Bar bar -> Bar <| (f bar)
          Baz baz -> Baz <| (f baz)

   Expected Type: a
     Actual Type: {y : Int}


Type error between lines 10 and 12:
Cannot unify rigid type variable 'a'.
It is likely that a type annotation is not general enough.

        case foo of
          Bar bar -> Bar <| (f bar)
          Baz baz -> Baz <| (f baz)

   Expected Type: a
     Actual Type: {y : Int}
@johnpmayer
Copy link
Contributor Author

Note that it still fails when I remove the type annotation; it just doesn't print those last two errors.

@johnpmayer
Copy link
Contributor Author

Huh, this is actually interesting - the following code does not compile in Haskell

foo :: (a -> a) -> (b,c) -> (b,c)
foo f (x,y) = (f x, f y)

@johnpmayer
Copy link
Contributor Author

Narrowed it down! So it looks like Elm would need to support Rank-2 types for this to work. I'll do some research. Can we keep this open, but tabled?

{-# LANGUAGE RankNTypes, UnicodeSyntax #-}

module Dispatch where

foo :: ( a. a -> a) -> (b,c) -> (b,c)
foo f (x,y) = (f x, f y)

bar = foo id

@johnpmayer
Copy link
Contributor Author

@evancz
Copy link
Member

evancz commented Sep 6, 2013

Sorry for the slow response! Yeah, we can definitely keep this open. My intuition was that a forall was needed, so it looks like we agree :)

mapExtFoo : (forall a. Ext a -> Ext a) -> Foo -> Foo

I think the type checker is able to handle this kind of thing, but these types are not parsed at the moment. I feel like this is the kind of thing that might make things undecidable or at least really expensive, but I am not expert enough to know decisively.

@johnpmayer
Copy link
Contributor Author

According to TAPL, rank 2 is decidable, otherwise rank-n is not.

Sent from my iPhone

On Sep 5, 2013, at 9:22 PM, Evan Czaplicki notifications@github.com wrote:

Sorry for the slow response! Yeah, we can definitely keep this open. My intuition was that a forall was needed, so it looks like we agree :)

mapExtFoo : (forall a. Ext a -> Ext a) -> Foo -> Foo
I think the type checker is able to handle this kind of thing, but these types are not parsed at the moment. I feel like this is the kind of thing that might make things undecidable or at least really expensive, but I am not expert enough to know decisively.


Reply to this email directly or view it on GitHub.

@evancz
Copy link
Member

evancz commented Sep 6, 2013

Why is it a flag in Haskell then? Maybe it has bad interactions with type classes?

@pthariensflame
Copy link

@evancz It's a flag for the same reason DoAndIfThenElse is a flag: because some people want strict adherence to the Haskell standard(s).

@evancz
Copy link
Member

evancz commented Sep 7, 2013

Ah, makes sense. Thanks @pthariensflame :)

@evancz
Copy link
Member

evancz commented Aug 29, 2015

I'm doing this thing where I make meta issues to track a category of related ideas. In this case, I added this to #1039. Often we have a bunch of different threads open that are mutually exclusive, or depend on each other in tricky ways. This is described more here in the "design discussion" section.

So I am going to close this, but any discussion should continue here!

@evancz evancz closed this as completed Aug 29, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants