接着这个关于 monad 转换器的问题 ...
在引入构造函数类之前 [M.P.Jones 1993],Haskell
class
声明:
Type
(当时又称为 *
)。创新是让参数变得友善
Type -> Type
(又名* -> *
)。这会产生(众所周知的 [**])不幸的后果,实例声明不能谈论“内容”类型——例如将 Set
的内容限制在 Ord
中。 (为什么)class
decl 不能看起来像这样:
class Functorb (f b) where -- not currently allowed in a class decl
fmapb :: (a -> b) -> f a -> f b
instance (Ord b) => Functorb (Set b) where
fmapb f NilSet = NilSet
fmapb f xs = fromList (map f $ toList xs)
注意
fmap
的签名与 Haskell 的标准相同。这没有使用 FlexibleInstances
或重叠实例或 Haskell 中后来出现的任何东西。
(另请注意,我们不需要对“传入”
Ord
的内容类型Set
进行a
约束:我们正在使用它,而不是从中构建任何内容。)
需要
class
标头才能仅使用 tyvars;并且仍然需要 Kind 系统 [论文第 1.3 节]。
你可以使用 MultiParamTypeClass 吗? (我相信,它们在 1993 年就已经被考虑过,但尚未实施。)论文(第 1.1 节末尾)说:
定义
函数的其他尝试(例如使用多个参数类型类)也因本质上相同的原因而失败。map
[出现在上面的原因]
的主要类型...该类型不明确(类型变量map j . map i
不出现在 ⇒ 符号的右侧或假设中)。 [其中e
是e
/参数到map i
的中间结果的类型。]map j
添加: 实现评论中 @Naïm Favier 的建议,根据 GHC (8.10),该组合并不含糊:
fmapb show . fmapb ((+ 0.5). fromIntegral)
:: (Show a1, Functorb f a1, Functorb f String, Fractional a1,
Integral a2) =>
f a2 -> f String
(确保两个
fmapb
都在更改“内容”的类型。)
并产生预期的结果,应用于
Data.Set
s 的 Int
。
[补充结束]
这些天我们可能会使用类型族:
type family Constr (fb :: Type) :: (Type -> Type) where
Constr (f b) = f
type family CArg (fb :: Type) :: Type where
CArg (f b) = b
class Functorb fb where
fmapb :: (a -> CArg fb) -> ((Constr fb) a) -> fb
(但这是对 TF 的相当“愚蠢”的使用。)
[**] 例如,有 'constrained-monad'。这看起来相当可怕。 AFAICT 它就是这样设计的,因此可以在
do
块内使用 - 通过 -XRebindableSyntax
。
例如使用多个参数类型类
啊,重读 1993 年论文中的内容,我认为这意味着 without 任何“Kind 系统”/Kind 的所有类参数
Type
。比如:
class Functorb ab fa fb where
fmapb :: ab -> fa -> fb
-- not :: (a -> b) -> (f a) -> (f b)
所以
class
decl 中没有能力表达参数和结果结构中的共性;因此,撰写中无法推断中间结果的类型。
(这个模糊的中间观察也是在 2000 年的论文中做出的,来自带来功能依赖的同一个稳定。
| ab fa -> fb, ab fb -> fa, fa fb -> ab
)
定义
Functor
/Monad
而无法提及“内容”类型的“理论纯度”是一个充足的争论来源。这些抽象属性遵循“函子定律”并“保持结构”。 (这些术语在 Stackoverflow 上有很多点击。)
IO
Monad 如何设法保持结构...... emm ... 并不明显。
具体来说,
Data.Set
不能是Functor
,因为对于地图f :: a -> b
:
x /= y =/=> f x /= f y
-- f
不一定保持唯一性x < y =/=> f x < f y
-- f
不一定保序OTOH Haskell 不遵循严格的过程语义,我们在要保留的结构中的地址处更新新值。如果我能在
Data.Set
块中处理 do
,我很乐意接受“退化函子”这个术语。
还有一个更复杂的情况我见过提到过:
(也许您的映射函数是部分的:某些传入值无法产生输出。)这使我们离结构保留更远。