Using a function with arguments, which can be either Ints or Doubles

I am new to Haskell, so hopefully this is not a dumb question. I have this data type:

data N = I Int | D Double deriving (Show, Eq) 

I am trying to write a function with the signature (Num a) => (a -> a -> a) -> N -> N -> N , which applies this function to numbers inside N and returns N with the result. If N both D s, it should just apply the function and return a D ; if one of I and the other is D , it must convert Int to I in Double , apply the function to two Double s and return a D ; and if both of them are I s, he must apply the function and return I Here is the (broken) code that I still have:

 widen :: N -> N -> (N, N) widen (I i) d@ (D _) = (D (fromIntegral i), d) widen d@ (D _) i@ (I _) = widen id widen xy = (x, y) numOp :: (Num a) => (a -> a -> a) -> N -> N -> N numOp op xy = case widen xy of (D x', D y') -> D $ x' `op` y' (I x', I y') -> I $ x' `op` y' 

I get an error on both numOp lines. The first one is:

 Could not deduce (a ~ Double) from the context (Num a) bound by the type signature for numOp :: Num a => (a -> a -> a) -> N -> N -> N at <line num> In the second argument of `($)', namely x' `op` y' In the expression: D $ x' `op` y' In a case alternative: (D x', D y') -> D $ x' `op` y' 

And the second:

 Couldn't match type `Double' with `Int' Expected type: Int Actual type: a In the second argument of `($), namely x' `op` y' In the expression: I $ x' `op` y' In a case alternative: (I x', I y') -> I $ x' `op` y' 

I am sure I understand what both errors mean; I think the first one says that the information in my type signature is not enough for GHC to suggest that op returns a Double , which is required by the constructor of the D value, and the second one says that since the first line implies that a Double , this line cannot use a value of type a as if it were Int . I don’t even know where to start looking for the right way to do this.

If this helps, the reason I'm trying to get it to work is that I follow. Write a textbook for yourself ; all the examples in the tutorial (especially in the Evaluation section) concern only integers, but as an exercise I would like to add the ability to support both integral and floating point numbers, so for example, (+ 1 2.5 2.5) returns 6.0 and (+ 1 2 3) returns 6 . If I think about it in the wrong way or there is an easier way to achieve it, I would like to hear suggestions.

+6
source share
1 answer

Signature

 numOp :: (Num a) => (a -> a -> a) -> N -> N -> N 

says that numOp accepts any monomorphic function of type a -> a -> a for each concrete instance of Num and two N , and a N calculated from it. So, for example, a function like

 Complex Float -> Complex Float -> Complex Float 

or

 approxRational :: RealFrac a => a -> a -> Rational 

(specialized up to a = Rational ) is the legitimate first argument.

You need a polymorphic function that can handle all Num instances as the first argument, i.e. a rank 2 type

 numOp :: (forall a. Num a => a -> a -> a) -> N -> N -> N 

(for this you need the extension of the RankNTypes language).

+7
source

All Articles