Disambiguating Types Using Available Class Instances
Given the following code:
import Data.Word
data T = T deriving (Eq, Show)
class C a where f :: a -> ()
instance C T where f _ = ()
instance C Word16 where f _ = ()
main = return $ f 0x16
GHC complains that it cannot deduce what type the literal 0x16
should be in error:
No instance for (Num a0) arising from the literal ‘22’
The type variable ‘a0’ is ambiguous
It's easy to see why that would be - Haskell allows numeric literals to be of any type that has an instance Num
, and here we can't eliminate which type is for the literal 0x16
(or 22).
It is also clear as a person reads this what I intended to do - there is only one available instance of the class C
that satisfies the constraint Num
, so obviously I intended to use it as 0x16
it should be treated as Word16
.
There are two ways I know to fix this, either annotate the literal with its type:
main = return $ f (0x16 :: Word16)
or define a function that essentially does this annotation for you:
w16 x = x :: Word16 main = return $ f (w16 0x16)
I tried the third way, sticking default (Word16)
to the top of the file in the hope that Haskell would choose this as the default type for numeric literals, but I think I don't understand what the keyword is default
supposedly because it didn't work.
I understand that typeclasses are public, so just because you can make an assumption in the above context that Word16
is the only numeric instance C
that might not be executing in some other module. But my question is, is there some mechanism by which I can assume / enforce this property so f
that Haskell can also resolve the type of its numeric argument to Word16
without explicit annotations on the calling site
The context is that I am implementing EDSL, and I would rather not include manual type hints when I know my parameters will be either Word16
or some other non-numeric type. I'm open to some dirty types / extensions if it makes EDSL more natural! Although, if the decisions do involve naughty pragmas, I definitely appreciate the hints of what I should be wary of when using them.
source to share
A quick fix with "mischievous pragmas" with GHC 7.10:
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
class C a where f :: a -> ()
instance C T where f _ = ()
instance {-# INCOHERENT #-} (w ~ Word16) => C w where f _ = ()
And with GHC 7.8:
{-# LANGUAGE TypeFamilies, FlexibleInstances, IncoherentInstances #-}
class C a where f :: a -> ()
instance C T where f _ = ()
instance (w ~ Word16) => C w where f _ = ()
Here the GHC essentially chooses an arbitrary most specific instance that remains after trying to unify chapters and instance constraints.
You should only use this if
- You have a fixed set of instances and don't export the class.
- For all use cases of a class method, there is a single possible most specific instance (subject to restrictions).
Many people advise against using IncoherentInstances
, but I think it can be pretty fun for DSL-s if we follow the above considerations.
source to share
For anyone else interested default
(I know I was!)
https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-750004.3
Section 4.3.4:
In situations where an ambiguous type is encountered, the ambiguous type variable v has no effect if:
- v appears only in constraints of the form C v, where C is a class and
- at least one of these classes is a numeric class (i.e. Num or a subclass of Num) and
- all of these classes are defined in the prelude or standard library.
So this explains why your proposal is default
completely ignored; C
is not a standard library class type.
(As for why this & hellip rule can't help you there. Presumably so as not to break arbitrary custom code.)
source to share