Since ghc-8.0 we have a very nice extension called TypeApplications
. Which allows us instead of:
λ> show (5 :: Int)
"5"
do so something like that:
λ> :set -XTypeApplications
λ> show @Int 5
"5"
Which is really cool. It becomes a bit more involved when we add more type variables, but there are rules that can be used to determine the exact order, and they are very well documented:
showFooBar :: (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
So in the function above we would first supply a
and then b
:
λ> showFooBar @Int @Double 3 4
"3 and 4.0"
That's great, but what if I'd like to change the order? No problem there, we can use ExplicitForAll
extension (or some other that imply it) to specify it:
{-# LANGUAGE ExplicitForAll #-}
showFooBar :: forall b a . (Show a, Show b) => a -> b -> String
showFooBar a b = show a ++ " and " ++ show b
And now we reversed the order of types that we are going to apply:
λ> showFooBar @Int @Double 3 4
"3.0 and 4"
The problem is that I can't seem to figure out how to achieve the same affect for functions that are part of a type class. Consider this example:
{-# LANGUAGE MultiParamTypeClasses #-}
class (Show a, Show b) => FooBar a b where
fooBarClassFunc :: a -> b -> String
I can't put forall
on a function now (eg. fooBarClassFunc :: forall a b . a -> b -> ..
, cause that changes the meaning of the function and obviously does not compile.
So, the question is, how do you change the order of type variables for the purpose of TypeApplication
inside the type class methods?
Edit
Just in case, I have tried InstanceSigs
extension, and it completely ignores the order of forall
type variables as far as TypeApplications
are concerned, which is a good thing, otherwise we would end up with behavior that is determined by the instance, rather than the class.
how do you change the order of type variables for the purpose of
TypeApplication
inside the type class methods?
@luqui's answer is good enough, I would think. But why not this:
class (Show b, Show a) => FooBar b a where
fooBarClassFunc :: a -> b -> String
You only have one method, so the only consideration driving the order of parameters to the class is for the purpose of TypeApplication
inside the methods.
If you have two or more methods for which you want the order of TypeApplication
to be different (@chi's point, but why?), then for the other methods either luqui's suggestion, or (equivalently) an other class with a superclass constraint and a default implementation.
class (Show a, Show b, FooBar b a) => OtherFooBar a b where
otherFooBarClassFunc :: a -> b -> String
otherFooBarClassFunc = otherFooBarClassFunc' -- default
instance {-# NOOVERLAPPABLE #-} OtherFooBar a b where {} -- take default
(Assuming otherFooBarClassFunc'
is defined in the main class; and that's where the real instance definition goes on.)
There is a lot to be said for one method per class, of course.
{-# NOOVERLAPPABLE #-}
wot we do not 'ave is my little in-joke.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With