Cli*_*ton 6 haskell typeclass type-families
让我说我上课了:
class C a b t where
f :: (a, b) -> (t a, t b)
Run Code Online (Sandbox Code Playgroud)
现在有了这个定义,我可以定义实例:
(a,b) -> (Maybe a, Maybe b)
(a,b) -> ([a], [b])
Run Code Online (Sandbox Code Playgroud)
但不是(据我所知):
(a,b) -> (a,b)
(a,b) -> ((a, a), (b, b))
Run Code Online (Sandbox Code Playgroud)
我可以改为改变我的类定义:
type family T a b x
class C a b where
f :: (a, b) -> (T a b a, T a b b)
Run Code Online (Sandbox Code Playgroud)
这将允许我做上面的事情,但后来我只能f为每个声明一个a和b.
基本上我希望能够像t原始定义一样传递类型系列,如果已知,则由类型检查器隐式解析.我不想只是f :: (a, b) -> (c, d)因为我想保持两者的不变量a并且b对它们做同样的事情,所以swap . f它是相同的类型f . swap.我想我可能需要内射型系列(来自GHC 8.0),但我不确定.但也许有另一种方式?
这可能会也可能不会回答您的问题,具体取决于您实际需要做什么.
标准解决方案是使用newtype来定义新实例.例如
newtype Pair a = Pair (a, a)
instance C a b Pair where
f = ...
Run Code Online (Sandbox Code Playgroud)
但是,这会导致f类型
f :: (a, b) -> (Pair a, Pair b)
Run Code Online (Sandbox Code Playgroud)
而不是通缉
f :: (a, b) -> ((a, a), (b, b))
Run Code Online (Sandbox Code Playgroud)
后者可以以无聊的方式恢复:
f' :: (a, b) -> ((a, a), (b, b))
f' p = case f p of (Pair x, Pair y) -> (x, y)
Run Code Online (Sandbox Code Playgroud)
现在,编写"适配器"函数,如f'感觉多余:毕竟我们只是删除一个newtype包装器,它不会改变运行时表示.更糟糕的是,这可能效率低下:考虑转变[Pair a]为[(a, a)].要做到这一点,我们需要映射整个列表,所以我们支付O(n)做什么都没做.
可以使用安全强制来构建有效的替代方案:
import Data.Coerce
f'' :: forall a b. (a, b) -> ((a, a), (b, b))
f'' = coerce (f :: (a, b) -> (Pair a, Pair b))
Run Code Online (Sandbox Code Playgroud)
这允许使用newtypes来驱动实例选择,并在它们挡住时将其删除.
现在,如果您的目标确实是在没有newtypes的情况下定义新实例,那么这无济于事.如果您想要一种简单的方法newtype从实例方法中删除s包装器,它可以提供帮助.