如何将函数应用于haskell中构造函数内的值?
How to apply a function to a value inside a constructor in haskell?
我将我的构造函数定义为
data MuOp = N (Name, Name)
| QN (QName, QName)
| QO (QOp, QOp)
| E (Exp, Exp)
| D (Decl, Decl)
| L (Literal, Literal)
| G (GuardedRhs, GuardedRhs)
我对元组进行了一些操作,例如
same :: MuOp -> Bool
same (N (a,b)) = a == b
same (QN (a,b)) = a == b
same (QO (a,b)) = a == b
same (E (a,b)) = a == b
same (D (a,b)) = a == b
same (L (a,b)) = a == b
same (G (a,b)) = a == b
然而,为多个函数重复它看起来很难看。有什么方法可以定义一些函数,例如 apply
where
apply :: ((a,a) -> c) -> MuOp -> c
apply f (N (a,b)) = f (a, b)
apply f (QN (a,b)) = f (a, b)
apply f (QO (a,b)) = f (a, b)
apply f (E (a,b)) = f (a, b)
apply f (D (a,b)) = f (a, b)
apply f (L (a,b)) = f (a, b)
apply f (G (a,b)) = f (a, b)
所以我只能说same = apply (\(a,b) -> a == b)
或fEq c -> apply (\(a,b) -> a == c)
我当前的定义产生了这个错误。
Couldn't match expected type ‘a’ with actual type ‘Name’
‘a’ is a rigid type variable bound by
the type signature for apply :: ((a, b) -> c) -> MuOp -> c
at src/Test/MuCheck/MuOp.hs:28:10
Relevant bindings include
f :: (a, b) -> c (bound at src/Test/MuCheck/MuOp.hs:29:7)
apply :: ((a, b) -> c) -> MuOp -> c
(bound at src/Test/MuCheck/MuOp.hs:29:1)
In the expression: a
In the first argument of ‘f’, namely ‘(a, b)’
仔细考虑一下您对 apply
的定义:所有 b
变量在每种情况下都是同一类型吗?请记住,虽然 apply
是一个多态函数,但调用者可以决定类型变量 a
将是什么(错误消息中的 a.k.a. "a rigid type variable")。
比如看这两个案例:
apply :: ((a, a) -> c) -> MuOp -> c
apply f (N (a, b)) = f (a, b)
apply f (QN (a, b)) = f (a, b)
...
第一行a
和b
都是Name
类型,但是第二行a
和b
都是类型QName
。因为 caller 可以决定类型变量 a
是什么,所以你不能在 Name
和 QName
之间任意选择。
解决方案是使用 rank-2 类型:
{-# Language Rank2Types #-}
apply :: (forall a . Eq a => (a, a) -> c) -> MuOp -> c
apply f (N (a, b)) = f (a, b)
apply f (QN (a, b)) = f (a, b)
...
之前隐含的量词 forall a .
已明确显示并推入函数箭头的左侧。 (另外,添加了 Eq
约束,但这是一个次要的细节。)通过这样做,您允许 callee(即 apply
)实例化具有满足 Eq
约束的任何类型的类型变量 a
。
我将我的构造函数定义为
data MuOp = N (Name, Name)
| QN (QName, QName)
| QO (QOp, QOp)
| E (Exp, Exp)
| D (Decl, Decl)
| L (Literal, Literal)
| G (GuardedRhs, GuardedRhs)
我对元组进行了一些操作,例如
same :: MuOp -> Bool
same (N (a,b)) = a == b
same (QN (a,b)) = a == b
same (QO (a,b)) = a == b
same (E (a,b)) = a == b
same (D (a,b)) = a == b
same (L (a,b)) = a == b
same (G (a,b)) = a == b
然而,为多个函数重复它看起来很难看。有什么方法可以定义一些函数,例如 apply
where
apply :: ((a,a) -> c) -> MuOp -> c
apply f (N (a,b)) = f (a, b)
apply f (QN (a,b)) = f (a, b)
apply f (QO (a,b)) = f (a, b)
apply f (E (a,b)) = f (a, b)
apply f (D (a,b)) = f (a, b)
apply f (L (a,b)) = f (a, b)
apply f (G (a,b)) = f (a, b)
所以我只能说same = apply (\(a,b) -> a == b)
或fEq c -> apply (\(a,b) -> a == c)
我当前的定义产生了这个错误。
Couldn't match expected type ‘a’ with actual type ‘Name’
‘a’ is a rigid type variable bound by
the type signature for apply :: ((a, b) -> c) -> MuOp -> c
at src/Test/MuCheck/MuOp.hs:28:10
Relevant bindings include
f :: (a, b) -> c (bound at src/Test/MuCheck/MuOp.hs:29:7)
apply :: ((a, b) -> c) -> MuOp -> c
(bound at src/Test/MuCheck/MuOp.hs:29:1)
In the expression: a
In the first argument of ‘f’, namely ‘(a, b)’
仔细考虑一下您对 apply
的定义:所有 b
变量在每种情况下都是同一类型吗?请记住,虽然 apply
是一个多态函数,但调用者可以决定类型变量 a
将是什么(错误消息中的 a.k.a. "a rigid type variable")。
比如看这两个案例:
apply :: ((a, a) -> c) -> MuOp -> c
apply f (N (a, b)) = f (a, b)
apply f (QN (a, b)) = f (a, b)
...
第一行a
和b
都是Name
类型,但是第二行a
和b
都是类型QName
。因为 caller 可以决定类型变量 a
是什么,所以你不能在 Name
和 QName
之间任意选择。
解决方案是使用 rank-2 类型:
{-# Language Rank2Types #-}
apply :: (forall a . Eq a => (a, a) -> c) -> MuOp -> c
apply f (N (a, b)) = f (a, b)
apply f (QN (a, b)) = f (a, b)
...
之前隐含的量词 forall a .
已明确显示并推入函数箭头的左侧。 (另外,添加了 Eq
约束,但这是一个次要的细节。)通过这样做,您允许 callee(即 apply
)实例化具有满足 Eq
约束的任何类型的类型变量 a
。