Francesco Mazzoli | 28 Sep 18:36 2012
Picon

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
MigMit | 29 Sep 11:04 2012
Picon

Re: Class constraints with "free" type variables and fundeps

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 <f <at> mazzo.li> wrote:

> I would expect this to work, maybe with some additional notation (a la
> ScopedTypeVariables)
> 
>    {-# LANGUAGE FunctionalDependencies #-}
(Continue reading)

Francesco Mazzoli | 29 Sep 11:17 2012
Picon

Re: Class constraints with "free" type variables and fundeps

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/c3b11111fa3014983acf41f9248c9507d7404424/Language/Distance/Search/Class.hs>,
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:
> 
(Continue reading)

Gábor Lehel | 29 Sep 19:49 2012
Picon

Re: Class constraints with "free" type variables and fundeps

On Fri, Sep 28, 2012 at 6:36 PM, Francesco Mazzoli <f <at> mazzo.li> 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

(Continue reading)

MigMit | 29 Sep 20:08 2012
Picon

Re: Class constraints with "free" type variables and fundeps


On Sep 29, 2012, at 9:49 PM, Gábor Lehel <illissius <at> gmail.com> wrote:

> On Fri, Sep 28, 2012 at 6:36 PM, Francesco Mazzoli <f <at> mazzo.li> 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:
(Continue reading)

Francesco Mazzoli | 29 Sep 20:47 2012
Picon

Re: Class constraints with "free" type variables and fundeps

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:
> 
> http://hackage.haskell.org/trac/ghc/ticket/7100

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
(Continue reading)


Gmane