Class constraints with "free" type variables and fundeps

I would expect this to work, maybe with some additional notation (a la ScopedTypeVariables) {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} class Foo a b | a -> b class Foo a b => Bar a where foo :: a -> b -> c The type family equivalent works as expected: {-# LANGUAGE TypeFamilies #-} class Foo a where type T a :: * class Bar a where foo :: a -> T a -> c I can't use type families because the `Foo' I'm using is in an external library. Is there any way to achieve what I want without adding `b' to `Bar'? -- Francesco * Often in error, never in doubt

Well, it seems that you can't do exactly what you want. So, the simplest way to do this would be not to make Foo a superclass for Bar:
class Bar a where
foo :: Foo a b => a -> b -> c
Then you would have to mention Foo everywhere.
If you really need, for some reason, to ensure that every Bar instance has a corresponding Foo instance, you can do some oleging this way:
data Void b = Void
data FooEv a where FooEv :: Foo a b => Void b -> FooEv a
class Bar a where
barFoo :: FooEv a
bar :: Foo a b => a -> b -> c
Then, whenever you need Foo methods, you can do pattern-matching:
case barFoo :: FooEv a of
FooEv (Void :: Void b) -> …
Now some "b" is in scope, and there is an instance of Foo a b.
On Sep 28, 2012, at 8:36 PM, Francesco Mazzoli
I would expect this to work, maybe with some additional notation (a la ScopedTypeVariables)
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-}
class Foo a b | a -> b
class Foo a b => Bar a where foo :: a -> b -> c
The type family equivalent works as expected:
{-# LANGUAGE TypeFamilies #-}
class Foo a where type T a :: *
class Bar a where foo :: a -> T a -> c
I can't use type families because the `Foo' I'm using is in an external library. Is there any way to achieve what I want without adding `b' to `Bar'?
-- Francesco * Often in error, never in doubt
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

At Sat, 29 Sep 2012 13:04:59 +0400, MigMit wrote:
Well, it seems that you can't do exactly what you want. So, the simplest way to do this would be not to make Foo a superclass for Bar:
class Bar a where foo :: Foo a b => a -> b -> c
Then you would have to mention Foo everywhere.
Just to clarify, I already worked my way around that problem. I want to know why I can't do it since it seems a useful feature to have. In fact, I was doing something very similar to what you're proposing before, but I think having the additional argument is better in my code, for various reasons. You can check the actual class here: https://github.com/bitonic/language-spelling/blob/c3b11111fa3014983acf41f924..., I've left the old version commented.
If you really need, for some reason, to ensure that every Bar instance has a corresponding Foo instance, you can do some oleging this way:
data Void b = Void data FooEv a where FooEv :: Foo a b => Void b -> FooEv a class Bar a where barFoo :: FooEv a bar :: Foo a b => a -> b -> c
Then, whenever you need Foo methods, you can do pattern-matching:
case barFoo :: FooEv a of FooEv (Void :: Void b) -> …
Now some "b" is in scope, and there is an instance of Foo a b.
Well, I'm sure there are endless ways to get around this, but in cases like this the resulting mess far outweighs the advantages :). -- Francesco * Often in error, never in doubt

On Fri, Sep 28, 2012 at 6:36 PM, Francesco Mazzoli
I would expect this to work, maybe with some additional notation (a la ScopedTypeVariables)
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-}
class Foo a b | a -> b
class Foo a b => Bar a where foo :: a -> b -> c
The type family equivalent works as expected:
{-# LANGUAGE TypeFamilies #-}
class Foo a where type T a :: *
class Bar a where foo :: a -> T a -> c
I can't use type families because the `Foo' I'm using is in an external library. Is there any way to achieve what I want without adding `b' to `Bar'?
I was browsing the GHC bug tracker and accidentally might have found a solution to your problem: http://hackage.haskell.org/trac/ghc/ticket/7100 Basically you have to make a type family to recapitulate the functional dependencies in the instances of Foo: type family FooFD a -- for each instance Foo A B, you have to write: -- type instance FooFD A = B class Foo a (FooFD a) => Bar a where foo :: a -> FooFD a -> c Anywhere you would use 'b', you use the type family instead. The example in the ticket also had a 'b ~ FooFD a' superclass constraint on Foo itself, which you can't add if you don't control Foo, but I'm not sure what it's necessary for - in my brief tests removing it didn't cause problems. A weakness of this approach is that you have to manually add a type instance for every instance of Foo, which may or may not be a problem in your situation.
-- Francesco * Often in error, never in doubt
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Your ship was destroyed in a monadic eruption.

On Sep 29, 2012, at 9:49 PM, Gábor Lehel
On Fri, Sep 28, 2012 at 6:36 PM, Francesco Mazzoli
wrote: I would expect this to work, maybe with some additional notation (a la ScopedTypeVariables)
{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-}
class Foo a b | a -> b
class Foo a b => Bar a where foo :: a -> b -> c
The type family equivalent works as expected:
{-# LANGUAGE TypeFamilies #-}
class Foo a where type T a :: *
class Bar a where foo :: a -> T a -> c
I can't use type families because the `Foo' I'm using is in an external library. Is there any way to achieve what I want without adding `b' to `Bar'?
I was browsing the GHC bug tracker and accidentally might have found a solution to your problem:
http://hackage.haskell.org/trac/ghc/ticket/7100
Basically you have to make a type family to recapitulate the functional dependencies in the instances of Foo:
type family FooFD a
Actually, I think it's better to use a class here: class Foo a (FooFD a) => FooProxy a where type FooFD a
-- for each instance Foo A B, you have to write: -- type instance FooFD A = B
class Foo a (FooFD a) => Bar a where foo :: a -> FooFD a -> c
Anywhere you would use 'b', you use the type family instead.
The example in the ticket also had a 'b ~ FooFD a' superclass constraint on Foo itself, which you can't add if you don't control Foo, but I'm not sure what it's necessary for - in my brief tests removing it didn't cause problems.
A weakness of this approach is that you have to manually add a type instance for every instance of Foo, which may or may not be a problem in your situation.
-- Francesco * Often in error, never in doubt
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Your ship was destroyed in a monadic eruption.
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

At Sat, 29 Sep 2012 19:49:36 +0200, Gábor Lehel wrote:
I was browsing the GHC bug tracker and accidentally might have found a solution to your problem:
Thanks, this makes me feel better. What interested me is not the workarounds, that I had already partly explored, but why the limitation is there in the first place. It turns out that it's because it'd add complexity and type families do it better. Fair enough.
Basically you have to make a type family to recapitulate the functional dependencies in the instances of Foo:
type family FooFD a
-- for each instance Foo A B, you have to write: -- type instance FooFD A = B
class Foo a (FooFD a) => Bar a where foo :: a -> FooFD a -> c
Anywhere you would use 'b', you use the type family instead.
The example in the ticket also had a 'b ~ FooFD a' superclass constraint on Foo itself, which you can't add if you don't control Foo, but I'm not sure what it's necessary for - in my brief tests removing it didn't cause problems.
A weakness of this approach is that you have to manually add a type instance for every instance of Foo, which may or may not be a problem in your situation.
I think this method, while useful to show the already shown relation between fundeps and type families, is pointless in reality. The most convenient method in practice, by far, is just to add the dependent part of the fundep explicitly to Bar. Along to the awkwardness you already pointed out of having to declare the family instance for each type, I'm introducing some unfamiliar and unnecessary type family to the user. It adds nothing but confusion. Not to mention that if I stick to fundeps I might hope to use my code with something else apart from GHC. -- Francesco * Often in error, never in doubt
participants (3)
-
Francesco Mazzoli
-
Gábor Lehel
-
MigMit