Reputation: 522
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
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