Minoru
Minoru

Reputation: 522

Guessing the correct instance of Num from the context

Suppose:

import Data.Int (Int64)

data Unknown

class Meh a where
    meh :: a -> String
instance Meh Int64 where
    meh = show
instance Meh Unknown where
    meh _ = "Unknown"

It is obvious that meh can only take either an Int64 or an Unknown.

But we all know that

meh 1

will cause an error (No instance for (Num a0) arising from the literal '1') because 1 is Num a => a and compiler doesn't know which instance of Num it should be.

However, it should (logically) be possible to infer 1 to be an Int64 because it is passed to meh and meh can only take either an Int64 or an Unknown. In other words, it should be possible to calculate the "intersection" of Num and Meh and decide which instance 1 is.

So my question is: Do you know any way (compiler extension, code workaround, anything) that makes it possible to write meh 1 where 1 is correctly inferred to be an Int64 without adding type signatures or adding specialised functions?

Upvotes: 2

Views: 81

Answers (1)

phadej
phadej

Reputation: 12123

When trying your example in GHC 7.8.3

>>> meh 1

I get:

<interactive>:2:1:
    No instance for (Meh a0) arising from a use of ‘meh’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Meh Unknown -- Defined at Meh.hs:9:10
      instance Meh Int64 -- Defined at Meh.hs:7:10
    In the expression: meh 1
    In an equation for ‘it’: it = meh 1

<interactive>:2:5:
    No instance for (Num a0) arising from the literal ‘1’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
    ...

This is because compiler tries to typecheck:

>>> meh (fromInteger (1 :: Integer))

For which you get similar errors:

<interactive>:4:1:
    No instance for (Meh a0) arising from a use of ‘meh’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Meh Unknown -- Defined at Meh.hs:9:10
      instance Meh Int64 -- Defined at Meh.hs:7:10
    In the expression: meh (fromInteger (1 :: Integer))
    In an equation for ‘it’: it = meh (fromInteger (1 :: Integer))

<interactive>:4:6:
    No instance for (Num a0) arising from a use of ‘fromInteger’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:

Type-classes are open. No-one restricts someone else defining:

newtype MyInt = MyInt Int deriving (Num)
instance Meh MyInt where
   meh = ...

Then even in your example meh 1 can be resolved to (meh :: Int64 -> String) 1, generally it can't. Arguments like "but based on the Meh instances visible in the scope" doesn't really work in Haskell, as instances are global.


If you define data (which is closed), then using {-# LANGUAGE OverloadedStrings #-} you could do:

data Meh = MehInt Int64 | MehString String

instance IsString Meh where
   fromString = MehString

Then

"foo" :: Meh

will compile, as GHC "will see"

fromString "foo" :: Meh

Similarly you can define incomplete Num instance, with only fromInteger defined. Yet I personally dislike incomplete instances, though this case is handy when defining eDSLs.

Upvotes: 2

Related Questions