当我使用MultiParamTypeClasses时,我可以创建忽略其中一个类型参数的类函数(即下面的"身份").
{-# LANGUAGE MultiParamTypeClasses #-}
data Add = Add
data Mul = Mul
class Test a b where
identity::a
instance Test Int Add where
identity = 0
instance Test Int Mul where
identity = 1
Run Code Online (Sandbox Code Playgroud)
(这是一个精简版本,当然在完整的程序中会有其他功能使用"b").
示例编译,但我永远无法访问身份!
main = do
putStrLn (show (identity::Int))
Run Code Online (Sandbox Code Playgroud)
导致"使用'身份'时没有(Test Int b0)的实例.
有没有办法获取身份?如果没有,编译器是否应该禁止我创建一个不使用所有类型参数的类函数?
如果没有,编译器是否应该禁止我创建一个不使用所有类型参数的类函数?
也许.事实上,你永远无法使用这样的类方法.但由于错误总是在编译时发生,因此并不是很危险.
在某些类似情况下工作的修复程序(不在您的情况下):
使未确定的类型变量在功能上依赖于其他变量.
{-# LANGUAGE FunctionalDependencies #-}
class Group_FD g p | g->p where
identity :: g
Run Code Online (Sandbox Code Playgroud)
也许可以这样使用
data Nat = One | Succ Nat
instance Group_FD Nat Mult where
identity = One
instance Group_FD Int Add where
identity = 0
Run Code Online (Sandbox Code Playgroud)
但显然不可能以g这种方式使用相同的元素创建多个实例.
为仅依赖于该方法的方法定义一个仅包含一个参数的单独类.然后在另一个上使这个类成为约束("超类"),以"导入"方法:
class Identity i where
identity :: i
class (Identity i) => Test i y
Run Code Online (Sandbox Code Playgroud)
但是,对于你的应用程序来说,这根本没用,因为你想要identity依赖于Phantom类型变量的行为.
为了实现您的目标,您必须以某种方式传递您想要的实例的信息.实现这一目标的一种方法是幻象:
class Group_PA g p where
identity :: p -> g
instance Group_PA Int Add where
identity _ = 0
instance Group_PA Int Mult where
identity _ = 1
Run Code Online (Sandbox Code Playgroud)
然后你可以使用它
GHCi> identity Add :: Int
0
GHCi> identity Mult :: Int
1
也许更惯用的实际上是使标志类型为空
{-# LANGUAGE EmptyDataDecls #-}
data Add
data Mult
Run Code Online (Sandbox Code Playgroud)
GHCi> identity(undefined :: Add):: Int
0
GHCi> identity(undefined :: Mult):: Int
1
这使得更清楚的是,幻像参数实际上不携带运行时信息,只是控制编译器选择的实例.
不可否认,这非常难看.
在右 ™解决方案是让NEWTYPE包装包含的模型信息.实际上,这样的包装器已经在标准库中了:Sum和Product.
| 归档时间: |
|
| 查看次数: |
699 次 |
| 最近记录: |