FunDeps and type inference

Dear cafe, I want to write an evaluation function that uncurries its function argument as necessary. Examples should include: eval :: (a -> b) -> a -> b eval :: (a -> b -> c) -> a -> (b -> c) eval :: (a -> b -> c) -> (a,b) -> c and hence have both eval (+) 4 5 eval (+) (4,5) typecheck. My approach is to use a type class: class Uncurry f a b where eval :: f -> a -> b instance Uncurry (a -> b) a b where eval = ($) instance (Uncurry f b c) => Uncurry ((->) a) f) (a,b) c where eval f (a,b) = eval (f a) b This works, but not for polymorphic arguments. One must annotate function and argument with concrete types when calling, otherwise the runtime does not know what instance to use. Type inference on ($) is able to infer the type of either of f, a or b in the expression b = f $ a if the type of two is known. Thus I am tempted to add functional dependencies class Uncurry f a b | f a -> b, a b -> f, f b -> a but I get scary errors: With only the first of the three dependencies, the coverage condition fails. Adding UndecidableInstances, the code compiles. Now type inference on the return type b works, but one can not use e.g. (+) as function argument. Adding the second dependency results in the compiler rejecting the code claiming "Functional dependencies conflict between instance declarations". I can not quite see where they would, and the compiler does not tell me its counterexample. I can see that eval max (True,False) :: Bool -- by second instance declaration, -- when max :: Bool -> Bool -> Bool eval max (True,False) :: (Bool,Bool) -> (Bool,Bool) -- by first instance declaration -- when max :: (Bool,Bool) -> (Bool,Bool) -> (Bool,Bool) but this ambiguity is precisely what the dependency a b -> f should help to avoid, isn't it? Judging by the number of coverage condition posts on this list this one is easy to get wrong and the compiler messages are not always helpful. Is this a kind problem? Would anyone care to elaborate? Thanks, Olaf

Hi Olaf,
You can use ~ to let instances get selected before ghc has deduced that two
types are equal. https://gist.github.com/aavogt/1cb0ca6f1654b09111d3 is
closer to what you're looking for, except "eval (+) (4,5)" doesn't work
unless the result type is given.
Besides the ghc manual, it might also help to look at
http://okmij.org/ftp/Haskell/typecast.html
Regards,
Adam
On Tue, Apr 14, 2015 at 5:12 PM, Olaf Klinke
Dear cafe,
I want to write an evaluation function that uncurries its function argument as necessary. Examples should include:
eval :: (a -> b) -> a -> b eval :: (a -> b -> c) -> a -> (b -> c) eval :: (a -> b -> c) -> (a,b) -> c and hence have both eval (+) 4 5 eval (+) (4,5) typecheck.
My approach is to use a type class:
class Uncurry f a b where eval :: f -> a -> b instance Uncurry (a -> b) a b where eval = ($) instance (Uncurry f b c) => Uncurry ((->) a) f) (a,b) c where eval f (a,b) = eval (f a) b
This works, but not for polymorphic arguments. One must annotate function and argument with concrete types when calling, otherwise the runtime does not know what instance to use.
Type inference on ($) is able to infer the type of either of f, a or b in the expression b = f $ a if the type of two is known. Thus I am tempted to add functional dependencies
class Uncurry f a b | f a -> b, a b -> f, f b -> a
but I get scary errors: With only the first of the three dependencies, the coverage condition fails. Adding UndecidableInstances, the code compiles. Now type inference on the return type b works, but one can not use e.g. (+) as function argument. Adding the second dependency results in the compiler rejecting the code claiming "Functional dependencies conflict between instance declarations". I can not quite see where they would, and the compiler does not tell me its counterexample. I can see that eval max (True,False) :: Bool -- by second instance declaration, -- when max :: Bool -> Bool -> Bool eval max (True,False) :: (Bool,Bool) -> (Bool,Bool) -- by first instance declaration -- when max :: (Bool,Bool) -> (Bool,Bool) -> (Bool,Bool) but this ambiguity is precisely what the dependency a b -> f should help to avoid, isn't it?
Judging by the number of coverage condition posts on this list this one is easy to get wrong and the compiler messages are not always helpful. Is this a kind problem? Would anyone care to elaborate?
Thanks, Olaf _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
participants (2)
-
adam vogt
-
Olaf Klinke