简体   繁体   中英

How to reuse a type variable in an inner type declaration

As part of my Haskell learning process, I like to explicitly type out the type declarations for functions. I would like to be able to do so for functions defined in a where clause, but I don't know how to specify, that a type variable in a where clause should denote the same type as some type variable in the outer type declaration. For instance, the following code:

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: a -> a
    bar a = f a

yields this error:

src\Test.hs:7:14:
    Couldn't match expected type `a' against inferred type `a1'
      `a' is a rigid type variable bound by
          the type signature for `foo' at src\Test.hs:3:8
      `a1' is a rigid type variable bound by
           the type signature for `bar' at src\Test.hs:6:11
    In the first argument of `f', namely `a'
    In the expression: f a
    In the definition of `bar': bar a = f a

How can I express that the first argument to bar should be of the same type as the second argument to foo, so that I can apply f to it?

Thanks.

I think you can do this in general with ScopedTypeVariables which GHC supports. This certainly compiles:

{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall a. (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: a -> a
    bar a = f a

Note the "forall a."

There is another workaround. Instead of referencing f within the inner function bar , extend bar to accept f as a first argument and use partial application in the parent.

foo :: (a -> a) -> a -> a
foo f arg = (bar f) arg
  where
    bar :: (a -> a) -> a -> a
    bar f a = f a

It does not require ScopedTypeVariables or explicit type checking code as the other answers.

Explanation

For clarity let's change the type parameter in bar to b and also rename its argument.

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    bar :: b -> b
    bar x = f x

Haskell complains because bar is annotated as b -> b (for any arbitrary type b ), but fx is attempting to apply an argument of type b to a function of type a -> a (for a specific, bound a ). In other words, the inner function is not as general as its type annotation advertises.

Passing f to bar means that for the expression (bar f) , the type variable b is bound to the same type as a .

Even simpler

And finally, without changing anything else, if you're willing to omit the type signature for the inner function bar , Haskell will infer its type exactly the way you want. That is, since bar applies f from from the parent function foo , the type of bar will reuse the type parameter a from the type of foo .

foo :: (a -> a) -> a -> a
foo f arg = bar arg
  where
    -- Type: bar :: a -> a
    bar a = f a

如果您不想使用ScopedTypeVariables扩展,那么对另一个问题的回答会显示使用技巧。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM