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.

+3


source to share


2 answers


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.

+5


source


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.)

+5


source







All Articles