Does anyone know of an extension that would allow one to put type constraints on pattern matching? For example:
{Language Extension}
IsOrderable::(Eq a)=>a->IO Bool
IsOrderable x = case x of
o::(Ord a=>a) -> do
putStrLn "This equatable thing is also orderable."
return True
_ -> do
putStrLn "This equatable thing is not orderable."
return False
Note: I am asking this so I could make monads that react differently based on its input type. Specifically, I was tring to make a probability monad, but I would like to check if the input type is equatable so I could combine duplicates.
There is a way, but it isn't pretty. We'll first make the function that we want to see dispatch on type class instance.
class IsOrd a where
isOrd :: a -> IO Bool
isOrd
will eventually have two instances, corresponding two each of your case
s. Of course, the simple
instance Ord a => IsOrd a where
isOrd = putStrLn "Orderable!" >> return True
instance IsOrd a where
isOrd = putStrLn "Not orderable" >> return False
Won't work because the instance heads (IsOrd
) are the same, and the way the compiler works is that it matches the instance head whether or not the constraint holds, and only then checks the constraint.
Now comes the complicated part: it is described here and here. (The second link, Oleg Kiselyov's collecion on Type Classes, might have other stuff that's relevant, and that I'm not including here because I don't know about it yet. It's also a great resource generally!). The essence of it is getting to:
data HTrue
data HFalse
instance (Ord a) => IsOrd' HTrue a where
isOrd' _ x = putStrLn "Orderable." >> return True
instance IsOrd' HFalse a where
isOrd' _ x = putStrLn "Not orderable" >> return False
Where we've added an extra type-level boolean to the instance head so that what in your example were case
s become distinct instance heads. Pretty nifty idea!
There are other details that you have to worry about, some of which I don't fully understand, but it's easy enough to get it working anyhow: here's some code that does what you want (you'll have to thread Eq instances all along it, from IsOrd
onwards, if you want the Eq
constraint too). And here's the end result:
*Scratch> :l scratch.hs
[1 of 1] Compiling Scratch ( scratch.hs, interpreted )
Ok, modules loaded: Scratch.
*Scratch> isOrd (5::Int)
Orderable.
True
*Scratch> isOrd ('c')
Not orderable
False