Вопрос

I'm trying to write an interpreter in Haskell (to be precise, I'm following the book Write Yourself a Scheme in 48 Hours). I try to add float arithmetic to the interpreter. Now I have the following code:

data LispVal = Number Integer
             | Float Float
             deriving(Show, Eq)

doNumeric :: (Num a) => (a -> a -> a) -> LispVal -> LispVal -> LispVal
doNumeric op (Number a) (Number b) = Number $ a `op` b
doNumeric op (Float a) (Number b) = Float $ a `op` (fromIntegral b)
doNumeric op (Number a) (Float b) = Float $ (fromIntegral a) `op` b
doNumeric op (Float a) (Float b) = Float $ a `op` b

In this way, I hope to construct numeric operations like addition, subtraction, multiplication and division simply by means like: doNumeric (+), doNumeric (-).. etc. so that I do not need to write code for doMul, doAdd etc.

But regarding the code above, GHC complains that:

Couldn't match type `Integer' with `Float'
Expected type: a
  Actual type: Float
In the first argument of `op', namely `a'
In the second argument of `($)', namely `a `op` (fromIntegral b)'
In the expression: Float $ a `op` (fromIntegral b)

So if I want op to take an operation that is present in Num type class, what should I do? How can I make the above code typecheck?

Это было полезно?

Решение

The problem is that you are trying to apply a function op with type a -> a -> a to a specific type. What your signature says is "op is a binary operator on a specific type a". That is, a is determined by the function that is passed in, and it may not be Integer or Float. (It's a bit odd that GHC gives you the warning on the second definition, but if you comment out the last three, you'll see it also gives it when you just define doNumeric for two Numbers.)

The way you are using op, you really need a rank 2 type:

{-# LANGUAGE RankNTypes #-}

doNumeric :: (forall a . (Num a) => a -> a -> a) -> LispVal -> LispVal -> LispVal
doNumeric op (Number a) (Number b) = Number $ a `op` b
...

What this means is that op is guaranteed to work for any Num type a, which I suspect is what you are going for. Hence you can call doNumeric with (+), but not with f :: Int -> Int -> Int.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top