Haldean Brown
Haldean Brown

Reputation: 12721

Polymorphic functions as parameters in Haskell

I have an ADT with two constructors; one that wraps a Double and one that wraps an Integer. I would like to create a function that takes a unary function on the Num typeclass and returns a function that applies that unary function to the contents of my ADT.

I've tried this:

data X = Y Integer
       | Z Double
wrap :: Num n => (n -> n) -> X -> X
wrap f (Y i) = Y $ f i
wrap f (Z i) = Z $ f i

But the compiler informs me that it can't match the type variable n to the type Double in the second definition of wrap:

test.hs:5:22: error:
    • Couldn't match expected type ‘n’ with actual type ‘Double’
      ‘n’ is a rigid type variable bound by
        the type signature for:
          wrap :: forall n. Num n => (n -> n) -> X -> X
        at test.hs:3:9
    • In the first argument of ‘f’, namely ‘i’
      In the second argument of ‘($)’, namely ‘f i’
      In the expression: Z $ f i
    • Relevant bindings include
        f :: n -> n (bound at test.hs:5:6)
        wrap :: (n -> n) -> X -> X (bound at test.hs:4:1)

If I remove the second definition (so I only define wrap on the integer constructor Y), I get the same error, but on the first definition instead (and with actual type 'Integer' instead of double).

If instead I remove the type signature, the first definition causes it to infer the type (Integer -> Integer) -> X -> X for wrap, which (expectedly) causes the second definition to fail to type check.

This seems like such a simple problem that I must be missing something glaringly obvious. Thanks in advance for your help and patience!

Upvotes: 3

Views: 207

Answers (1)

Haldean Brown
Haldean Brown

Reputation: 12721

Thanks to @user1937198, I figured out that the implicit qualification of n was happening in the wrong scope; I was saying that I wanted to take a function that took any type that satisfied Num and mapped that type onto the same type, when what I wanted to say was I needed a function that took all types that satisfied Num. With RankNTypes the code becomes:

{-# LANGUAGE RankNTypes #-}
data X = Y Integer
       | Z Double
wrap :: (forall n. Num n => n -> n) -> X -> X
wrap f (Y i) = Y $ f i
wrap f (Z i) = Z $ f i

and all is good again. Thanks!

Upvotes: 4

Related Questions