Reputation: 31
A sort-of solution is to write out a bunch of instances by hand,
class Halve a where
halve :: a -> a
instance Halve Int where
halve = (`div` 2)
instance Halve Integer where
halve = (`div` 2)
instance Halve Float where
halve = (/ 2)
instance Halve Double where
halve = (/ 2)
-- ... etc.
However some instances will inevitably be missed, such as user-defined ones. My attempted real solution is this non-working code:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class Halve a where
halve :: a -> a
instance Integral a => Halve a where
halve = (`div` 2)
instance Fractional a => Halve a where
halve = (/ 2)
which gives the error
Duplicate instance declarations:
instance Integral a => Halve a
-- Defined at ...
instance Fractional a => Halve a
-- Defined at ...
I haven't found how to express this correctly, if it can be done.
Upvotes: 3
Views: 96
Reputation: 153102
One lightweight way would be to define two new types, thus:
newtype FracWrap a = F a deriving Fractional
newtype IntWrap a = I a deriving Integral
instance Fractional a => Halve (FracWrap a) where halve = (/2)
instance Integral a => Halve (IntWrap a) where halve = (`div`2)
Then, for foo
which takes a Halve
-constrained argument, one has the relatively lightweight foo.F
and foo.I
to indicate whether one wants to use the Fractional
or Integral
version of foo
.
This is relatively nice in terms of API design, as the API designer need not offer two version of everything; just one Halve
-specific version of everything, and two adapters.
Upvotes: 2
Reputation: 62848
Try making the function to halve a value be a specific input to your code.
foobar :: (Ord x) => (x -> x) -> x -> x -> ...
foobar halve x1 x2 = ...
It's kind of annoying, but hopefully you only have to specify this once, at the very top of your algorithm. Any child functions can just receive the parameter passed on.
Another alternative would be to do something like
data Halvable x = Halvable {value :: x, halve :: x -> y}
halfInt :: Integral x => x -> Halvable x
halfInt x = Halvable x (`div` 2)
halfFrac :: Fractional x => x -> Halvable x
halfFrac x = Halvable x (/ 2)
Upvotes: 2
Reputation: 48611
What you're asking for is pretty much horrible. What is the meaning of this class? It's also impossible to do it with significantly less boilerplate than your working partial solution. The problem is that GHC, even with overlapping or incoherent (barf) instances will always commit to an instance based on the part of the instance head to the right of the =>
.
GHC never acts on the belief that a type is not an instance of a class except to produce an error message.
Upvotes: 3
Reputation: 101989
You can't:
data Hell = YouCantDoThat
instance Integral Hell
instance Fractional Hell
There is no rule saying that an Integral
cannot be a Fractional
or viceversa. And if that's the case how do you choose?
This is exactly what the error is telling you.
Upvotes: 4