How Type inference work in presence of Functional Dependencies

依然范特西╮ 提交于 2019-12-04 04:58:54

To avoid this problem, you can use type families instead:

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

class Fooo a where
  type Out a :: *

instance Fooo Int where
  type Out Int = Float

ff :: (Foo Int) => Int -> (Out Int)
ff = undefined

gg :: Int -> Float
gg= undefined

hh :: (Foo Int) => Int -> (Out Int)
hh = gg

Type families are nice. Use type families when you can!


I'm guessing your error is because ghc can deduce f :: Int -> Float from f :: Foo Int a => Int -> a and your class definition and instances, but it can't deduce g :: Foo Int a => Int -> a from g:: Int -> Float.

It's convenient to think of constraints as a secret dictionary parameter on the function. There's an inherent but constrained polymorphism in f that there isn't in g.

I think it's helpful to note that ghci is giving us essentially exactly the same error message as if we tried to define

j :: Int -> Float
j = undefined

k :: Eq a => Int -> a
k = j

It's obvious that this shouldn't work, because we know that k should be contrainedly polymorphic in its second argument. ghc tries to match the type Int -> a with Int -> Float and fails, complaining that it can't infer a ~ Float from the context Eq a, even though there's an instance Eq Float. In your example, it says it can't infer a ~ Float from the context Foo Int a even though there's an instance for Foo Int Float. I realise we can deduce that there's only one possible type for a, but by making the class and instance for Foo, you have defined a relation and asserted it's a functional dependency. This isn't the same as defining the function (which is why type families solves the problem - it defines the function).

ghc also complains when you write

aconst :: (Foo Int a) => a
aconst = 0.0

or even

anotherconst :: (Foo Int a) => a
anotherconst = 0.0::Float

always because it can't match the constrainedly polymorphic a it wanted with the specific type you gave it (Fractional a or Float).

You want

forall a.Foo Int a

to be the same type as Float, but it isn't. There's only one type that satisfies forall a.Foo Int a, it's Float, so ghci can take f::forall a.(Foo Int a)=>a->Float and deduce (using a dictionary for Foo) that f::Int->Float , but you're expecting ghci to take Float and find notice it's forall a.Foo Int a, but there's no dictionary for Float, it's a type, not a type class. ghci can do it one way, but not the other.

Dan's point about local information is that ghc would have to use both Foo's definition and the instance you made to deduce that Float can be rewritten forall a.(Foo Int a), and that at this point in compilation, ghc isn't using global information because it's just trying to do a match. My point is that Float matches forall a.(Foo Int a) but forall a.(Foo Int a) doesn't match Float, in the sense that "this" matches the pattern (x:xs) but (x:xs) doesn't match the pattern "this".

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!