haskell - 表示满足多个约束的任何类型的实例
问题描述
我怀疑所有适用的、可折叠的幺半群都可以以相同的方式遍历。换句话说,对于t :: * -> *
满足Applicative
和的任何类型,Foldable
并且所有实例化都t a
满足Monoid
,都有 的自由实例Traversable
。
这是我将如何实施sequenceA
:
sequenceA :: (Applicative t, Foldable t, Monoid (t a), Applicative f) =>
t (f a) -> f (t a)
sequenceA = foldl (liftA2 $ \b a -> mappend b (pure a)) (pure mempty)
例如,我们可以使用它来将包含函数的列表遍历到将产生列表的函数中(因为[]
它是可应用的、可折叠的,并且对于所有类型都是一个幺半群[a]
):
sequenceA [\a -> 2 * a, \a -> 2 + a] $ 5
-- [10, 7]
不幸的是,我无法弄清楚如何Traversable
使用sequenceA
. 这是我尝试过的:
instance (Applicative t, Foldable t, Monoid (t a)) => Traversable t where
sequenceA = foldl (liftA2 $ \b a -> mappend b (pure a)) (pure mempty)
如果我尝试在没有任何扩展的情况下编译它,我会得到:
<interactive>:3:55: error:
• Illegal instance declaration for ‘Traversable t’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
• In the instance declaration for ‘(Traversable t)’
我在 Haskell 中表达这个实例的正确方法是什么?
如果递归添加编译器错误提到的任何扩展可能会解决问题,我尝试了这一点并将结果粘贴到此处。如果有任何相关的错误消息,请告诉我,我会将它们直接移动到问题正文中。
解决方案
你的猜想是错误的。考虑类型
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Data.Semigroup
import Data.Monoid hiding ((<>))
newtype FL a = FL [a] deriving (Functor, Foldable, Applicative)
instance Semigroup (FL a) where
FL as <> FL bs = FL $ as ++ drop (length as) bs
instance Monoid (FL a) where
mempty = FL []
mappend = (<>)
这是基于Alternative
. 显然是 的标识,并且关联性不太明显,因此它是一个有效的实例。但是,它不会导致使用您的定义的有效实例。您的定义适用于列表(以及其他一些类型,例如and ),因为它们具有非常特殊的结构。ZipList
mempty
mappend
mappend
Monoid
Traversable
Data.Sequence.Seq
Data.Vector.Vector
特别地,[a]
是(以一些惰性考虑为模)具有并满足特定通用属性的自由幺半群:a
pure
foldMap
无论何时
m
是 aMonoid
,f :: a -> m
是foldMap f
唯一的幺半群同态 使得foldMap f . pure = f
。
在其他情况下,例如FL
上面,您通常不会最终满足 的恒等律Traversable
,尽管我想您可能会发现它在某些较弱的条件下起作用。
无论如何,如果你想搞砸你的想法怎么办?最简单的事情是用约束替换Monoid (t a)
约束Alternative t
。然后你可以使用empty
instead ofmempty
和(<|>)
instead of mappend
,并且类型(尽管不是法律)应该可以解决。
如果你真的坚持下去Monoid
,你必须从Data.Constraint.Forall
. 你想说的是,对于每个a
, Monoid (t a)
。您可以将其表示为ForallF Monoid t
。但是您不能只Monoid
在该约束下使用方法,因为GHC
. 相反,您必须使用instF
:
{-# LANGUAGE TypeOperators, ScopedTypeVariables, InstanceSigs, ... #-}
import Data.Constraint
import Data.Constraint.Forall
instance (Applicative t, Foldable t, ForallF Monoid t)
=> Traversable t where
sequenceA :: forall f a. Applicative f => t (f a) -> f (t a)
sequenceA =
case instF :: ForallF Monoid t :- Monoid (t a) of
Sub Dict -> foldl (liftA2 $ \b a -> mappend b (pure a)) (pure mempty)
当然,一旦你遇到了所有这些麻烦,你会发现你的实例与其他Traversable
实例重叠,这几乎是一场彻底的灾难。 Traversable
附录:正如Benjamin Hodgson 所指出的,有一个公认的GHC 提案来支持量化(和暗示)约束。当它实现时(可能很快),您将能够简单地编写forall a. Monoid (t a)
为约束,并且事情应该更容易解决。
推荐阅读
- amazon-web-services - 我的应用程序如何发现 AWS App Mesh(具有动态端口的 ECS/Fargate)上的 *local* envoy sidecar 代理的地址/端口?
- jenkins - 将密码参数传递给 Jenkinsfile 中的下游作业时,“脚本不允许使用字段 hudson.util.Secret 值”
- python - Tkinter、Multiprocessing 和 cx_Freeze 的组合创建了一个不需要的窗口
- python - 在 Python 2.7 中使用 PyInstaller - 包括模块
- python - sklearn 中的 load_digits() 有什么作用?
- google-sheets - Google 表格日期查询不适用于特定列
- java - java中数组的简单交换给出了奇怪的结果
- c# - XamarinForms 中标签的 TextColor 属性在 Android 上不起作用
- android - 在使用 FTP 上传之前即时压缩图像
- sql - 解析sql中的不规则分隔符