[Haskell-cafe] Are casts required?

Steffen Schuldenzucker sschuldenzucker at uni-bonn.de
Mon Jun 6 11:22:22 CEST 2011


Hi Patrick,

On 06/06/2011 09:45 AM, Patrick Browne wrote:
> Are casts required to run the code below?
> If so why?
> Thanks,
> Pat
>
>
> -- Idetifiers for objects
> class (Integral i) =>  IDs i where
>   startId :: i
>   newId :: i ->  i
>   newId i = succ i
>   sameId, notSameId :: i ->  i ->  Bool
> -- Assertion is not easily expressible in Haskell
> -- notSameId i newId i  = True
>   sameId i j = i == j
>   notSameId i j = not (sameId i j)
>   startId = 1
>
>
> instance IDs Integer where
>
>
>
> -- are casts need here?
> sameId (newId startId::Integer) 3

I'll take this as an example. First of all, note that

WHAT YOU'VE WRITTEN IS NOT A CAST

, that is, if x is an Int, then x :: Double is a type error. What the 
'::' does is (in this situation) that it specializes the type of a 
polymorphic value.

In GHCi, omitting the ':: Integer' part, I get

*Main> let x1' = sameId (newId startId) 3

<interactive>:1:10:
     Ambiguous type variable `i' in the constraint:
       `IDs i' arising from a use of `sameId' at <interactive>:1:10-33
     Probable fix: add a type signature that fixes these type variable(s)

Let's take the above expression apart:

We have:

*Main> :t newId startId
newId startId :: (IDs i) => i

*Main> :t 3
3 :: (Num t) => t

*Main> :t sameId
sameId :: (IDs i) => i -> i -> Bool

Now, when trying to evaluating your expression, the machine ultimately 
has to know what (newId startId) and 3 are. This, of course, depends on 
the type chosen for i and t, respectively.
For example, if I define the following instance:

instance IDs Int where
     startId = 2

we have:

*Main> sameId (newId startId :: Integer) 3
False
*Main> sameId (newId startId :: Int) 3
True

, so the result type clearly depends on the types chosen.
But, lacking an explicit signature, there is no way for the machine to 
tell which types should be used, in particular as the information which 
types were chosen is completely lost in the resulting type 'Bool'.

The example above does not look as if it was created to illustrate your 
problem. Then however, note that you don't have to use a class if you 
don't expect people to overwrite your default implementations. Normal 
Functions are sufficient:

 > -- I always want this
 > {-# LANGUAGE NoMonomorphismRestriction #-}
 >
 > startId :: (Integral i) => i
 > startId = 1
 >
 > newId :: (Integral i) => i -> i
 > newId = succ
 >
 > sameId, notSameId :: (Integral i) => i -> i -> Bool
 > sameId = (==)
 > notSameId i j = not $ sameId i j

Ok, now this works even without the signatures:

*Main> sameId (newId startId) 3
False

, which is probably caused by defaulting on the top level (IIRC, an 
unresolved Integral type variable defaults to Integer. Don't have the 
documentation at hand right now.) like this:

*Main> let i3 = 3 :: (Integral x => x)
*Main> :t i3
i3 :: Integer

and the same thing happens on the (newId startId) side, too.

As one last remark, your original problem that caused the "Ambiguous 
type variable" error looks very similar to the well-known (show . read) 
problem.

-- Steffen



More information about the Haskell-Cafe mailing list