Typeclass with an ‘or’ restriction.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Greetings, We can currently do something like
class (Num a, Eq a) => Foo a where bar :: a -> a -> Bool bar = (==)
This means that our `a' has to be an instance of Num and Eq. Apologies for a bit of an artificial example. Is there a way however to do something along the lines of:
class Eq a => Foo a where bar :: a -> a -> Bool bar = (==)
class Num a => Foo a where bar :: a -> a -> Bool bar _ _ = False This would allow us to make an instance of Num be an instance of Foo or an instance of Eq to be an instance of Foo.
The compiler currently complains about multiple declarations. Is there currently a way to achieve this? The main issue I can see with this is that given an instance of both, Num and Eq, it wouldn't be possible to pick the correct default implementation. Purely a theoretical question. - -- Mateusz K. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAEBAgAGBQJRjP0hAAoJEM1mucMq2pqX30wP/0d0TQHs4S3G5TBw+T1baI6n g/5k/YlPmSgS3FaO8JMQsb2uqL8dGPZGUN7d2ohwcigtXS88KWH4u4rTjrs1+p8o ktS+vIE4kEEedTAX6wmP2Zn+rvK2zFGboCafaX/a/IxT5CbwYZ97RrWCjzz1jlPs S/VlhNHcTQ7Cf/0pa0xJ1kbao+vBHiWtWjxcdzCT/6zS86+jm9vz8qrYT3TWUd3y AJXPBjRuXeyz+RDv18yrth7hMlAvaeoWzmC4gbGFHC68/Oq2l8kz+4dt6ApN+mIy l73wNjrU185YoZ2dkuKTIph8/BadgkD+9ktkgZZ/NlqxElc596BdbOcMfVk4rz4A 0nWqaLa4QctIthghJ1UNfKS8lQzkVVRT6e03LYdgPkqJm1HQxkjHL/WieV2NEoRf 1K9S4SVW8Aq/ML/Gmx782Z3jMECfnYWntf9gSOwFASB64tVej1iUxb7UsfxJAL1t ysf9MjcbZsHe3M/JAq4f8HtHoZoiIG/TTjD0yo74owssJDfOTDqmYMriyelcnUf6 hDCPZyUqLPMTNVx07T+gwfXJoE1HK20hzVe2o1dPBZ8Kb2KJbNg0+sUSB3/v6O8e EJ6w7aQ3OnZUACd1i2uLZiphMF8d8va4T8eTFUyROODHvaPv6netzr8gPcIXhpVO 5OwyMfO54cinZYen7+HR =iAYY -----END PGP SIGNATURE-----

Hi Mateusz, It's not directly possible to write a class with a choice of superclasses; as you point out, it's not really clear what that would mean. One workaround, though it might not be sensible in practice, is the following.
{-# LANGUAGE ConstraintKinds, GADTs #-}
First, reify the constraints we are interested in as types that pack up the corresponding dictionary. Thanks to ConstraintKinds, it's possible to do this once and for all.
data Dict c where Dict :: c => Dict c
Now we can describe types with either Num or Eq dictionaries (or both) as a class. The proxy argument makes it easy to specify the type, in the absence of explicit type application.
class NumOrEq a where numOrEq :: proxy a -> Either (Dict (Num a)) (Dict (Eq a))
Something like your Foo class can then be defined like this:
class NumOrEq a => Foo a where bar :: a -> a -> Bool bar x y = case numOrEq [x] of Left Dict -> False Right Dict -> x == y
When giving an instance for NumOrEq, you must choose which dictionary to pack up if both are available.
instance NumOrEq Int where numOrEq _ = Left Dict
instance NumOrEq Bool where numOrEq _ = Right Dict
instance Foo Int instance Foo Bool
And with all that, we have:
bar 3 (3 :: Int) == False bar True True == True
Now I'm wondering why we would want that in the first place. Hope this helps, Adam On 10/05/13 14:58, Mateusz Kowalczyk wrote: | Greetings, | | We can currently do something like |> class (Num a, Eq a) => Foo a where bar :: a -> a -> Bool bar = |> (==) | | This means that our `a' has to be an instance of Num and Eq. Apologies | for a bit of an artificial example. | | Is there a way however to do something along the lines of: |> class Eq a => Foo a where bar :: a -> a -> Bool bar = (==) | |> class Num a => Foo a where bar :: a -> a -> Bool bar _ _ = False | This would allow us to make an instance of Num be an instance of Foo | or an instance of Eq to be an instance of Foo. | | The compiler currently complains about multiple declarations. Is there | currently a way to achieve this? | | The main issue I can see with this is that given an instance of both, | Num and Eq, it wouldn't be possible to pick the correct default | implementation. | | Purely a theoretical question. -- The University of Strathclyde is a charitable body, registered in Scotland, with registration number SC015263.
participants (2)
-
Adam Gundry
-
Mateusz Kowalczyk