构造函数类:为什么不提及内容的类型?

问题描述 投票:0回答:1

接着这个关于 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

haskell functor
1个回答
0
投票

例如使用多个参数类型类

啊,重读 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
,我很乐意接受“退化函子”这个术语。

还有一个更复杂的情况我见过提到过

  • 有时你想限制传入的“内容”类型[1.2];甚至
  • 限制传入和传出的“内容”类型[1.3]。

(也许您的映射函数是部分的:某些传入值无法产生输出。)这使我们离结构保留更远。

© www.soinside.com 2019 - 2024. All rights reserved.