haskell - 是否可以通过另一个类型构造函数派生 Traversable 实例?
问题描述
假设我们有一些类Foo
,这样的实例Foo f
为我们提供了实现 和 所需Functor f
的Foldable f
一切Traversable f
。为了避免重叠的实例,可以在 newtype 包装器之间Foo
和下面见证这种关系:Functor, Foldable, Traversable
type Foo :: (Type -> Type) -> Constraint
class Foo f
where
{- ... -}
type FoonessOf :: (Type -> Type) -> Type -> Type
newtype FoonessOf f a = FoonessOf (f a)
instance Foo f => Functor (FoonessOf f)
where
fmap = _
instance Foo f => Foldable (FoonessOf f)
where
foldMap = _
instance Foo f => Traversable (FoonessOf f)
where
traverse = _
现在假设我们有一些类型构造函数:
data Bar a = Bar {- ... -}
这样有一个:
instance Foo Bar
where
{- ... -}
我们想装备它的“ -ness”Bar
所暗示的实例。Foo
既然Bar a
是Coercible
to FoonessOf Bar a
,我们希望能够推导出via
实例FoonessOf Bar
:
deriving via (FoonessOf Bar) instance Functor Bar
deriving via (FoonessOf Bar) instance Foldable Bar
这适用于类型类,例如Functor
和Foldable
不幸的是,当我们尝试对 做同样的事情时Traversable
,事情就出错了:
[typecheck -Wdeferred-type-errors] [E] • Couldn't match representation of type ‘f1 (Foo Bar a1)’
with that of ‘f1 (Bar a1)’
arising from a use of ‘ghc-prim-0.6.1:GHC.Prim.coerce’
NB: We cannot know what roles the parameters to ‘f1’ have;
we must assume that the role is nominal
• In the expression:
ghc-prim-0.6.1:GHC.Prim.coerce
@(Foo Bar (f a) -> f (Foo Bar a)) @(Bar (f a) -> f (Bar a))
(sequenceA @(Foo Bar)) ::
forall (f :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep)
(a :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep).
Applicative f => Bar (f a) -> f (Bar a)
In an equation for ‘sequenceA’:
sequenceA
= ghc-prim-0.6.1:GHC.Prim.coerce
@(Foo Bar (f a) -> f (Foo Bar a)) @(Bar (f a) -> f (Bar a))
(sequenceA @(Foo Bar)) ::
forall (f :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep)
(a :: TYPE ghc-prim-0.6.1:GHC.Types.LiftedRep).
Applicative f => Bar (f a) -> f (Bar a)
When typechecking the code for ‘sequenceA’
in a derived instance for ‘Traversable Bar’:
To see the code I am typechecking, use -ddump-deriv
In the instance declaration for ‘Traversable Bar’
——————————————————————————————————————————————————————————————————————————————
...
所以我的问题是:
- 是否可以通过实例提出其他一些派生方案
Traversable Bar
? - 是否有可能对
Traversable
可以通过新类型派生的类进行一些修改?
解决方案
我怀疑1.的答案是:不,情况无法挽救,也不可能获得Traversable
using的实例DerivingVia
。
就 2. 而言,尝试在更简单的上下文中重现问题很有用。考虑以下:
-- Remember to turn on ScopedTypeVariables!
data A = A
newtype B = B A
a :: forall f. f A -> f A
a = id
b :: forall f. f B -> f B
b = coerce $ a @f
看起来这应该可行,但是唉:
[typecheck -Wdeferred-type-errors] [E] • Couldn't match representation of type ‘f A’ with that of ‘f B’
arising from a use of ‘coerce’
NB: We cannot know what roles the parameters to ‘f’ have;
we must assume that the role is nominal
• In the expression: coerce $ a @f
In an equation for ‘b’: b = coerce $ a @f
• Relevant bindings include
b :: f B -> f B
问题与类型构造函数参数的“角色”以及角色推断的工作方式有关。出于我们的目的,角色有两种类型:“代表性”和“非代表性”。同样出于我们的目的,两者之间的差异可以近似为以下内容:F :: Type -> Type
如果存在 的实例,则类型构造函数具有“代表性”角色的参数Representational F
,其中:
type Representational :: (Type -> Type) -> Constraint
type Representational f = forall x y. Coercible x y => Coercible (f x) (f y)
否则,参数 ofF
是非代表性的。
类型检查器允许您在不同的地方注释类型参数的角色(尽管很奇怪,不是那种)。可悲的是,没有办法注释更高种类的类型变量的角色。然而,我们能做的就是Representational f
直接要求:
b' :: forall f. Representational f => f B -> f B
b' = coerce $ a @f
现在进行类型检查。这提出了一种调整Traversable
类型类以使其可通过强制导出的可能方法。
现在让我们看看Traversable
操作的类型sequenceA
:
class Traversable t
where
sequenceA :: forall f. Applicative f => forall a. t (f a) -> f (t a)
{- ... -}
注意:又是那个讨厌的东西forall f
,意思f
是有一个名义角色的类型参数。
要做DerivingVia
的是尝试coerce
在:
sequenceA @T1 :: forall f. Applicative f => forall a. T1 (f a) -> f (T2 a)
和:
sequenceA @T2 :: forall f. Applicative f => forall a. T2 (f a) -> f (T2 a)
尽管T1
( FoonessOf Bar
) 和T2
( Bar
) 是“参数化”可强制的,但这种强制将失败,因为整个操作的强制最终将分解为类型检查器抱怨的强制:
Couldn't match representation of type
‘f1 (Foo Bar a1)’
with that of
‘f1 (Bar a1)’
正如我们所讨论的,这不起作用,因为f
' 参数被认为具有名义作用。
与我们上面的简化示例一样,修复很简单:只需要求Representational f
:
type Traversable' :: (Type -> Type) -> Constraint
class Traversable' t
where
traverse :: (Representational f, Applicative f) => (a -> f b) -> t (f a) -> f (t b)
现在我们终于可以Traversable'
通过以下方式获得一个实例FoonessOf Bar
:
instance Foo f => Traversable' (FoonessOf f)
where
traverse = _
deriving via (FoonessOf Bar) instance Traversable' Bar
推荐阅读
- javascript - 2018如何分页前/后分页表()?
- angular - 避免组件封装或改变封装器的属性
- python - 大于总体的样本的python随机样本
- sql - 如何在 3 条记录之间获取中值?- SQL
- python - Python Stripe 源已成功创建,但 sepa_debit 的“没有此类令牌”
- javascript - 发布请求而不是调用
- c# - 检查两组点是否与源点位于不同的半球
- polymer - 聚合物 3 创建元素运行时并在插槽内注入 html
- javascript - Javascript数组排序并制定与日期范围相同的时间表
- google-maps - Google Places api 给我错误 Daily Quota Exceeded