[Haskell-cafe] Functional dependencies and type inference
Simon Peyton-Jones
simonpj at microsoft.com
Tue Aug 23 06:36:21 EDT 2005
Thomas
Strange and interesting example! If I had a pound for every time functional dependencies had messed up my brain, I'd be a rich man. Apart from anything else, your example is another case of the "critical example"
http://www.haskell.org/pipermail/haskell-cafe/2005-August/010974.html
as you mention. It's also jolly strange that GHC infers the (un-writable) type
bar :: (forall r. (Dep Int r) => Int -> r) -> Bar -> Char
but you can write neither that type nor
bar :: (Int -> Bool) -> Bar -> Char
so in fact GHC won't type the bogus type-cast. Nevertheless, those are artefacts of GHC's not-yet-right implementation of fundeps.
I think the real problem here is that the "newtype-deriving" mechanism isn't conservative enough. Given
class C a where { op :: ty }
newtype T = MkT S
the idea is that if S is an instance of a class C, then T can be, witnessed by the same functions.
That amounts to saying that
ty[S/a] is interchangeable with ty[T/a]
and that isn't really true when fundeps get in on the act.
I can't say I really understand it fully, but that's as far as I can take it today.
Question: what restrictions on the newtype-deriving mechanism would make it sound?
Simon
| -----Original Message-----
| From: Thomas Jäger [mailto:thjaeger at gmail.com]
| Sent: 23 August 2005 02:02
| To: Simon Peyton-Jones
| Cc: Einar Karttunen; haskell-cafe at haskell.org
| Subject: Re: [Haskell-cafe] Functional dependencies and type inference
|
| Simon,
|
| I believe there may be some nasty interactions with generalized
| newtype-deriving, since we can construct two Leibniz-equal types which
| are mapped to different types using fundeps:
|
| class Foo a where
| foo :: forall f. f Int -> f a
|
| instance Foo Int where
| foo = id
|
| newtype Bar = Bar Int deriving Foo
|
| -- 'Equality' of Int and Bar
| eq :: forall f. f Int -> f Bar
| eq = foo
|
| class Dep a b | a -> b
|
| instance Dep Int Bool
| instance Dep Bar Char
|
| newtype Baz a = Baz { runBaz :: forall r. Dep a r => a -> r }
|
| conv :: (forall f. f a -> f b) ->
| (forall r. Dep a r => a -> r) -> (forall r. Dep b r => b -> r)
| conv f g = runBaz $ f (Baz g)
|
| bar = conv eq
|
| Here, after type erasure, 'bar' is the identity function . Ghc infers
|
| bar :: (forall r. (Dep Int r) => Int -> r) -> Bar -> Char
|
| This is not valid as an explicit type signature, but presumably the
| proposal implies that we could type bar as
|
| bar :: (Int -> Bool) -> Bar -> Char
|
| instead. Now
|
| \x -> bar' (const x) (Bar 0) :: Bool -> Char
|
| would become an unsafe coercion function from Bool to Char.
|
|
| Thomas
|
| On 8/11/05, Simon Peyton-Jones <simonpj at microsoft.com> wrote:
| > Einar
| >
| > Good question. This is a more subtle form of the same problem as I
| > described in my last message. In fact, it's what Martin Sulzmann calls
| > "the critical example". Here is a boiled down version, much simpler to
| > understand.
| >
| > module Proxy where
| >
| > class Dep a b | a -> b
| > instance Dep Char Bool
| >
| > foo :: forall a. a -> (forall b. Dep a b => a -> b) -> Int
| > foo x f = error "urk"
| >
| > bar :: (Char -> Bool) -> Int
| > bar g = foo 'c' g
| >
| >
| > You would think this should be legal, since bar is just an instantation
| > of foo, with a=Char and b=Bool. But GHC rejects it. Why?
More information about the Haskell-Cafe
mailing list