[Haskell-cafe] Problem with result-type context restrictions in typeclasses.

DNM dnmehay at gmail.com
Wed Sep 30 01:25:04 EDT 2009


Dan, thanks again for the response.

I changed my code to use type families to let each Cls instance (actually a
more complicated instance in my code) determine which Bar instance type it
will return, but this didn't seem to work.  The problem is that the client
of the typeclass instance methds ('useThisStuff', which calls on 'toNum' and
'foo' in the contrived example) expects some guarantee that (Ret c) is going
to be an instance of Bar.  The actual client code I'm using complains when
it sees that the associated type doesn't guarantee that an instance of the
appropriate class is instantiated.  I don't see any way to guarantee this
without adding a context restriction in the class-level definition of Ret c,
something like:

class Cls c where
   type Ret c :: (Bar *) => * -- or a better name
   foo :: c -> Ret c

which isn't legal Haskell.  What I want to say is "define Ret c however you
want, but make sure it is an instance of Bar" in the *class-level definition
of Ret c*, so that any client of 'Cls' will know that Ret c will be
foo-able.

Maybe I'm missing some subtlety of type families...

Any suggestions?

--D.N.


Daniel Peebles wrote:
> 
> In your class, you have:
> 
> class Cls c where
>    foo :: (Bar b) => c -> b
> 
> There's an implicit forall for b, meaning that the caller of the
> method gets to choose what it wants for b (as long as it's an instance
> of Bar). For you to be able to write such a method you'd need to write
> functions that can return any instance of Bar. One solution to this is
> to turn on the GHC extension -XTypeFamilies, and then modify your code
> as follows:
> 
> class Cls c where
>    type Ret c :: * -- or a better name
>    foo :: c -> Ret c
> 
> instance Cls G where
>    type Ret G = FU
>    foo = fuu
> 
> That should work (although I haven't tested it).
> 
> What type families do in this case is allow you to write not only
> methods associated with typeclasses, but type functions associated
> with them too. In this case you can think of Ret as a function that
> takes a type (G in the instance above) and returns another type (FU).
> Each instance can define new mappings for Ret.
> 
> Hope this helps!
> 
> Dan
> On Tue, Sep 29, 2009 at 10:48 PM, DNM <dnmehay at gmail.com> wrote:
>> Correction by the author:
>>
>>> It seems that ghc doesn't like the fact that I am saying 'foo' must
>>> return a class 'b' of typeclass 'Bar', while providing a function that
>>> returns a concrete data instance of 'Bar' (viz., FU or FI) later on
>>> when I implement 'foo' in each type classes.
>>
>> Should read:
>>
>> It seems that ghc doesn't like the fact that I am saying 'foo' must
>> return something of TYPE 'b' implementing typeclass 'Bar', while
>> providing
>> a function that returns a concrete data instance of 'Bar' (viz., FU or
>> FI)
>> later on when I implement 'foo' in each type classes.
>>
>> On Sep 29, 10:43 pm, DNM <dnme... at gmail.com> wrote:
>>> N.B. I'm a newbie to Haskell, and this problem is a bit complex, so
>>> bear with me.
>>>
>>> I'm using typeclasses to implement a sort of common interface for all
>>> things -- call them things of type 'Cls' -- that can be expected to
>>> implement a set of functions -- an 'interface' in OOP-speak.  (Yes,
>>> yes, I'm aware that typeclasses are subtly different and far superior,
>>> but my Haskell-ese is still a bit rudimentary.)
>>>
>>> Essentially, I want to have a typeclass that expects its instances to
>>> have an accessor function that results in something that is an
>>> instance of another typeclass whose instances can perform some
>>> operation.   The ghc type-checker doesn't seem to like my code,
>>> though, and I can't seem to figure out why.
>>>
>>> To make it concrete, I've typed up some dummy typeclasses and a dummy
>>> function that uses their instances to illustrate what I mean, as well
>>> as the form of the ghc(i) error.
>>>
>>> ------------- BEGIN CODE ------------------
>>> class Cls c where
>>>     foo :: (Bar b) => c -> b
>>>
>>> class Bar b where
>>>     toNum :: b -> Int
>>>
>>> -- | One implementation of Cls
>>> data D = D {fu :: FU}
>>> data FU = FU {num :: Int}
>>>
>>> instance Cls D where
>>>     foo = fu
>>> instance Bar FU  where
>>>     toNum f = (num f) + 47
>>>
>>> -- | Another implementation of Cls
>>> data E = E {fi :: FI}
>>> data FI = FI {nuum :: Int}
>>>
>>> instance Cls E where
>>>     foo = fi
>>> instance Bar FI where
>>>     toNum f = (nuum f) + 100
>>>
>>> -- | Yet another (this one re-uses FI)
>>> data F = F {fii :: FI}
>>>
>>> instance Cls F where
>>>     foo = fii
>>>
>>> -- | And one last one, just to stress that
>>> --   I really need to implement multiple
>>> --  instances of Cls.
>>> data G = G {fuu :: FU}
>>>
>>> instance Cls G where
>>>     foo = fuu
>>>
>>> -- | Good. Now, the function 'useThisStuff' need
>>> --   not know anything about it's payload
>>> --   other than that it its args are Cls's
>>> --   (hence they are foo'able things that
>>> --   can be used to construct an Int answer).
>>> useThisStuff :: (Cls x, Cls y) => x -> y -> Int
>>> useThisStuff x y =
>>>     (toNum $ foo x) + (toNum $ foo y)
>>> ------------- END CODE --------------------
>>>
>>> When I type this up in a file and try to load it in ghci, I get the
>>> following error message(s):
>>>
>>> ------------- BEGIN ERROR MSG ----------
>>> Prelude> :load Typeclasses.hs
>>> [1 of 1] Compiling Typeclasses      ( Typeclasses.hs, interpreted )
>>>
>>> Typeclasses.hs:14:10:
>>>     Couldn't match expected type `b' against inferred type `FU'
>>>       `b' is a rigid type variable bound by
>>>           the type signature for `foo' at Typeclasses.hs:4:16
>>>       Expected type: D -> b
>>>       Inferred type: D -> FU
>>>     In the expression: fu
>>>     In the definition of `foo': foo = fu
>>>
>>> Typeclasses.hs:23:10:
>>>     Couldn't match expected type `b' against inferred type `FI'
>>>       `b' is a rigid type variable bound by
>>>           the type signature for `foo' at Typeclasses.hs:4:16
>>>       Expected type: E -> b
>>>       Inferred type: E -> FI
>>>     In the expression: fi
>>>     In the definition of `foo': foo = fi
>>>
>>> Typeclasses.hs:31:10:
>>>     Couldn't match expected type `b' against inferred type `FI'
>>>       `b' is a rigid type variable bound by
>>>           the type signature for `foo' at Typeclasses.hs:4:16
>>>       Expected type: F -> b
>>>       Inferred type: F -> FI
>>>     In the expression: fii
>>>     In the definition of `foo': foo = fii
>>>
>>> Typeclasses.hs:39:10:
>>>     Couldn't match expected type `b' against inferred type `FU'
>>>       `b' is a rigid type variable bound by
>>>           the type signature for `foo' at Typeclasses.hs:4:16
>>>       Expected type: G -> b
>>>       Inferred type: G -> FU
>>>     In the expression: fuu
>>>     In the definition of `foo': foo = fuu
>>> Failed, modules loaded: none.
>>> ------------- END ERROR MSG ------------
>>>
>>> It seems that ghc doesn't like the fact that I am saying 'foo' must
>>> return a class 'b' of typeclass 'Bar', while providing a function that
>>> returns a concrete data instance of 'Bar' (viz., FU or FI) later on
>>> when I implement 'foo' in each type classes.  Repeated for
>>> convenience:
>>>
>>> class Cls c where
>>>     foo :: (Bar b) => c -> b
>>> ...
>>> -- (e.g.)
>>> data G = G {fuu :: FU}
>>> instance Cls G where
>>>     foo = fuu
>>>
>>> Does anyone have any clue as to what I'm doing wrong (language
>>> extensions that I may need, etc.)?
>>>
>>> Is is because I'm using context restrictions on the *result* type of a
>>> typeclass method?  I've written other typeclasses with methods that
>>> say, essentially:
>>>
>>> class A a where
>>>     blah :: (MonadPlus m) => a -> a -> m a
>>>
>>> with no issues. The restriction there is not on the return type a, but
>>> rather on some monadic 'wrapper' around it.  This may be why that code
>>> works.
>>>
>>> Please advise.  Any help is greatly appreciated.
>>>
>>> --D.N. (Dennis)
>>> _______________________________________________
>>> Haskell-Cafe mailing list
>>> Haskell-C... at haskell.orghttp://www.haskell.org/mailman/listinfo/haskell-cafe
>> _______________________________________________
>> Haskell-Cafe mailing list
>> Haskell-Cafe at haskell.org
>> http://www.haskell.org/mailman/listinfo/haskell-cafe
>>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
> 
> 

-- 
View this message in context: http://www.nabble.com/Problem-with-result-type-context-restrictions-in-typeclasses.-tp25674141p25675199.html
Sent from the Haskell - Haskell-Cafe mailing list archive at Nabble.com.



More information about the Haskell-Cafe mailing list