Polymorphic functions as parameters in Haskell
我有一个带有两个构造函数的 ADT;一个package Double 和一个package Integer。我想创建一个函数,该函数在 Num 类型类上采用一元函数并返回一个将该一元函数应用于我的 ADT 内容的函数。
我试过这个:
1 2 3 4 5
| 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 |
但是编译器告诉我它不能将类型变量 n 与 wrap 的第二个定义中的类型 Double 匹配:
1 2 3 4 5 6 7 8 9 10 11 12
| 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) |
如果我删除第二个定义(所以我只在整数构造函数 Y 上定义 wrap),我会得到相同的错误,但是在第一个定义上(并且使用 actual type 'Integer' 而不是 double)。
如果我删除类型签名,第一个定义会导致它推断类型 (Integer -> Integer) -> X -> X 进行换行,这(预期)会导致第二个定义无法进行类型检查。
这似乎是一个如此简单的问题,我一定错过了一些明显的东西。提前感谢您的帮助和耐心等待!
- Num n => (n -> n) 意味着您的函数将与任何将具体类型转换为具体类型的函数一起使用,只要该类型是 Num 的实例。这并不意味着它需要一个可以处理任何 Num 实例的函数。我不确定如何在 haskell 类型系统中表达这一点。
-
啊!这正是我需要的线索,谢谢。现在写答案...
-
您只需要更改 wrap :: (forall n. Num n => n -> n) -> X -> X。
感谢@user1937198,我发现 n 的隐含限定发生在错误的范围内;我是说我想要一个函数接受任何满足 Num 的类型并将该类型映射到同一类型,而我想说的是我需要一个函数接受所有满足 Num 的类型。使用 RankNTypes 代码变为:
1 2 3 4 5 6
| {-# 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 |
一切都好起来了。谢谢!