可以说我上课了:
class C a b t where
f :: (a, b) -> (t a, t b)
现在有了这个定义,我可以定义实例来说:
(a,b) -> (Maybe a, Maybe b)
(a,b) -> ([a], [b])
但不是(据我了解):
(a,b) -> (a,b)
(a,b) -> ((a, a), (b, b))
相反,我可以像这样更改我的类定义:
type family T a b x
class C a b where
f :: (a, b) -> (T a b a, T a b b)
这将允许我执行上述操作,但随后我只能为每个
f
和a
声明一个b
。基本上,我希望能够在原始定义中将类型族作为
t
传递,如果已知,则由类型检查器隐式解析。我不想仅仅将其设置为f :: (a, b) -> (c, d)
,因为我想保持不变,即a
和b
对它们执行相同的操作,因此swap . f
与f . swap
是相同的类型。我在想可能需要injective type families(来自GHC 8.0),但不确定。但是也许还有另一种方式? 最佳答案
这可能会或可能不会回答您的问题,具体取决于您实际需要执行的操作。
一种标准的解决方案是使用新类型来定义新实例。例如。
newtype Pair a = Pair (a, a)
instance C a b Pair where
f = ...
但是,这将导致
f
具有类型f :: (a, b) -> (Pair a, Pair b)
而不是通缉
f :: (a, b) -> ((a, a), (b, b))
后者可以很无聊的方式恢复:
f' :: (a, b) -> ((a, a), (b, b))
f' p = case f p of (Pair x, Pair y) -> (x, y)
现在,编写诸如
f'
之类的“适配器”功能感到多余:毕竟,我们只是删除了一个newtype
包装器,而该包装器不会更改运行时表示形式。更糟糕的是,这可能效率很低:考虑将[Pair a]
转换为[(a, a)]
。为此,我们需要映射整个列表,因此我们付给O(n)根本不做任何事情。可以使用safe coercions构造一个有效的替代方法:
import Data.Coerce
f'' :: forall a b. (a, b) -> ((a, a), (b, b))
f'' = coerce (f :: (a, b) -> (Pair a, Pair b))
这允许使用
newtype
来驱动实例选择,但是当它们阻碍时将其删除。现在,如果您的目标确实是在不使用
newtype
的情况下定义新实例,则无济于事。相反,如果您想要一种简单的方法来从实例方法中删除newtype
的包装器,则可以提供帮助。关于haskell - 泛化类参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32732270/