[英]polymorphic function on existential type
所以说我有一节课:
class C a where
reduce :: a -> Int
现在我想将其打包成数据类型:
data Signal = forall a. (C a) => Signal [(Double, a)]
由于存在量化,我可以在信号上调用C方法,但信号不公开类型参数:
reduceSig :: Signal -> [(Double, Int)]
reduceSig (Signal sig) = map (second reduce) sig
现在因为C有很多方法,我自然而然的下一步就是拉出'reduce'函数,这样我就可以替换任何方法:
mapsig :: (C a) => (a -> a) -> Signal -> Signal
mapsig f (Signal sig) = Signal (map (second f) sig)
输入错误! 无法推断(a1~a)。 进一步思考,我认为它的含义是'f'是某个C实例的函数,但我无法保证它与信号中的C实例相同 ,因为类型参数是隐藏的! 我想要它,我明白了。
那么这是否意味着不可能推广reduceSig? 我可以忍受这个,但我已经习惯于在haskell中自由地分解函数了,不得不编写样板文件感觉很奇怪。 另一方面,我想不出任何方式来表示一个类型等于Signal内部的类型,而不是给Signal一个类型参数。
你需要表达什么是f
,像reduce
在使用reduceSig
,可以适用于任何类型的实例C
,而不是目前的类型,其中f
工作在一个单一的类型,它是实例C
。 这可以这样做:
mapsig :: (forall a. (C a) => a -> a) -> Signal -> Signal
mapsig f (Signal sig) = Signal (map (second f) sig)
您需要使用RankNTypes
扩展名,就像使用存在类型时一样; 请注意, mapsig
的实现是相同的,该类型刚刚被推广。
基本上,这种类型的, mapsig
得到决定哪一个函数被调用上; 使用您之前的类型, mapsig
的调用者可以决定哪个不起作用,因为只有mapsig
知道正确的a ,即Signal
内的一个。
但是, mapsig reduce
不起作用,原因很简单: reduce :: (C a) => a -> Int
,你不知道a是Int! 您需要为mapsig
提供更通用的类型(具有相同的实现):
mapsig :: (C b) => (forall a. (C a) => a -> b) -> Signal -> Signal
即, f
是一个函数,它采用任何类型的C
实例,并生成一个类型为C
的实例(该类型在mapsig
调用时被固定mapsig
调用者选择;即,值为mapsig f
可以在任何信号被调用,它总是会产生一个信号以相同的一个结果(这并不是说,你可以从外部检查此)。)
Existentials和rank-N类型确实很棘手,所以这可能需要一些时间来消化。 :)
作为附录,值得指出的是,如果C
所有函数看起来像a -> r
r,那么你最好创建一个记录 ,即转向
class C a where
reduce :: a -> Int
foo :: a -> (String, Double)
bar :: a -> ByteString -> Magic
data Signal = forall a. (C a) => Signal [(Double, a)]
mapsig :: (C b) => (forall a. (C a) => a -> b) -> Signal -> Signal
成
data C = C
{ reduce :: Int
, foo :: (String, Double)
, bar :: ByteString -> Magic
}
data Signal = Signal [(Double, C)]
mapsig :: (C -> C) -> Signal -> Signal
这两种信号类型实际上是等价的! 前一种解决方案的好处只有在您有其他使用C
数据类型时才会出现, 而不会对其进行存储量化,因此您可以拥有使用特定知识和特定C
实例操作的代码。 如果您这个类的主要用例是通过存在量化,那么您可能首先不需要它。 但我不知道你的程序是什么样的:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.