为什么实例仅通过其头部进行匹配?

2024-01-10

我将首先介绍一个具体的问题(StackOverflow 的人就是这样)。 假设您定义了一个简单类型

data T a = T a

这种类型是一个Functor, Applicative and a Monad。忽略自动派生,要获取这些实例,您必须编写每个实例,即使Monad暗示Applicative,这意味着Functor。 更重要的是,我可以定义一个这样的类

class Wrapper f where
    wrap   :: a -> f a
    unwrap :: f a -> a

这是一个相当强的条件,它绝对意味着Monad,但我写不出来

instance Wrapper f => Monad f where
    return = wrap
    fa >>= f = f $ unwrap fa

因为出于某种原因,这意味着“一切都是Monad (every f),仅当它是Wrapper”,而不是“一切都是Wrapper is a Monad".

同样,你不能定义Monad a => Applicative a and Applicative a => Functor a实例。

您不能做的另一件事(这可能只是相关的,我真的不知道)是让一个类成为另一个类的超类,并提供子类的默认实现。当然,这很棒class Applicative a => Monad a,但是我仍然需要定义Applicative在我可以定义之前实例Monad one.

这不是咆哮。我写了很多,因为否则很快就会被标记为“太宽泛”或“不清楚”。问题归结为标题。 我知道(至少我很确定)这有一些理论上的原因,所以我想知道这里到底有什么好处。

作为一个子问题,我想问是否有可行的替代方案仍然保留所有(或大部分)这些优点,但允许我写的内容。

添加: 我怀疑答案之一可能是“如果我的类型是Wrapper,但我不想使用Monad这意味着什么?”。对此我问,为什么编译器不能选择最具体的一个?如果有一个instance Monad MyType,肯定比instance Wrapper a => Monad a.


这里有很多问题。但我们一次只拿一个。

第一:为什么编译器在选择使用哪个实例时不查看实例上下文?这是为了保持实例搜索高效。如果你要求编译器只考虑实例头满足的实例,那么你实际上最终要求你的编译器在所有可能的实例中进行回溯搜索,此时你已经实现了 Prolog 的 90%。另一方面,如果您采取这样的立场(如 Haskell 所做的那样),即在选择要使用的实例时只查看实例头,然后简单地强制执行实例上下文,则不会出现回溯:在每时每刻,只有您可以做出的一个选择。

下一步:为什么不能让一个类成为另一个类的超类,并提供子类的默认实现?此限制没有根本原因,因此 GHC 提供此功能作为扩展。你可以写这样的东西:

{-# LANGUAGE DefaultSignatures #-}
class Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

    default pure :: Monad f => a -> f a
    default (<*>) :: Monad f => f (a -> b) -> f a -> f b
    pure = return
    (<*>) = ap

然后一旦你提供了instance Monad M where ...,你可以简单地写instance Applicative M没有where条款并让它正常工作。我真的不知道为什么标准库没有这样做。

最后:为什么编译器不能允许多个实例并只选择最具体的一个?这个问题的答案有点像前两个的混合体:有很好的根本原因,这不能很好地工作,但 GHC 仍然提供了一个扩展来做到这一点。这不能很好地工作的根本原因是在运行时之前无法知道给定值的最具体实例。 GHC 对此的回答是,对于多态值,选择与可用的完整多态性兼容的最具体的值。如果后来那件事变得单态,那么对你来说就太糟糕了。这样做的结果是,某些函数可能会在一个实例中对某些数据进行操作,而其他函数可能会在另一实例中对相同的数据进行操作;这可能会导致非常微妙的错误。如果经过所有讨论后您仍然认为这是一个好主意,并且拒绝从别人的错误中吸取教训,您可以打开IncoherentInstances.

我认为这涵盖了所有问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么实例仅通过其头部进行匹配? 的相关文章

  • 使用 Promise 语法编写同步代码有什么好处吗?

    有同步承诺这样的概念吗 使用 Promise 语法编写同步代码有什么好处吗 try foo bar a b bam catch e handleError e 可以写成类似的东西 但使用同步版本then foo then bar bind
  • 如何让 Show 显示函数名称?

    作为一个让我熟悉 Haskell 的简单练习 在 Youtube 上闲逛并偶然进入美国倒计时游戏节目之后 我想为数字游戏制作一个求解器 你得到 6 个数字 需要将它们与 为了得到给定的结果 到目前为止我所得到的是非常脑死亡的 let ope
  • 如何从 haskell 中的 IOError 获取 errno?

    我在 haskell 平台上 GHC 6 12 1 作为 apt get 安装在 Debian Squeeze 上 鉴于我需要在与最初引发它的线程不同的线程上使用它 如何从 IOError 中获取底层 errno 我需要这个的原因是因为我正
  • 在 Haskell 中计算移动平均线

    我正在学习 Haskell 所以我尝试实现移动平均函数 这是我的代码 mAverage Int gt Int gt Float mAverage x a fromIntegral k fromIntegral x k lt rawAvera
  • 为什么 Haskell 的默认字符串实现是一个字符链接列表?

    Haskell 默认值的事实String众所周知 实现在速度和内存方面都效率不高 据我所知 lists一般来说 在 Haskell 中实现为单链表 并且适用于大多数小型 简单数据类型 例如Int 这似乎不是一个好主意 但是对于String这
  • 无点镜头创建不进行类型检查

    在函数中test 我遍历一个列表 从它的成员生成镜头 然后打印一些数据 当我使用有针对性的呼叫风格时 这会起作用 当我使其成为无点时 它无法进行类型检查 为什么会出现这种情况 我该如何解决这个问题 在我看来 GHC 并没有保留排名较高的信息
  • 持久 selectList 导致错误“无法将类型‘BaseBackend backend0’与‘SqlBackend’匹配”

    我遇到以下编译错误 Couldn t match type BaseBackend backend0 with SqlBackend arising from a use of runSqlite The type variable bac
  • 搜索重写规则

    有什么办法可以浏览或搜索重写规则吗 当我使用像这样的标志时 ddump rule firings or ddump rule rewrites我只是得到了触发的规则的名称以及它引起的重写 但没有得到实际的规则本身 理想情况下 我想通过 GH
  • 不同功能的容器?

    我正在尝试为不同的函数实现一个容器类 我可以在其中保存函数指针并稍后用它来调用这些函数 我会尝试更准确地描述我的问题 例如 我有两个不同的测试函数 int func1 int a int b printf func1 works i i n
  • 以下两个 lambda 函数的空间复杂度

    我正在阅读以下内容 https en wikibooks org wiki Haskell Graph reduction https en wikibooks org wiki Haskell Graph reduction 其内容如下
  • Scala中有类似Java Stream的“peek”操作吗?

    在Java中你可以调用peek x gt println x 在 Stream 上 它将对每个元素执行操作并返回原始流 这与 foreach 不同 foreach 是 Unit Scala 中是否有类似的东西 最好是适用于所有 Monady
  • 使用 FoldLine 解析多个块

    对于这个简化的问题 我试图解析一个如下所示的输入 foo bar baz quux woo hoo xyzzy glulx into foo bar baz quux woo hoo xyzzy glulx 我尝试过的代码如下 import
  • 在scala 2.13中,为什么有时无法显式调用类型类?

    这是 Shapeless 2 3 3 中的一个简单示例 val book author gt gt Benjamin Pierce title gt gt Types and Programming Languages id gt gt 2
  • Traversable 类型类的用途

    有人可以向我解释一下类型类的目的是什么吗Traversable 类型类定义是 class Functor t Foldable t gt Traversable t gt where So Traversable is a Functor
  • Haskell 输入返回元组

    我想知道 IO 函数是否可以返回元组 因为我想从这个函数中获取这些元组作为另一个函数的输入 investinput IO gt Char Int investinput do putStrLn Enter Username username
  • 在 Haskell 中合并两个列表

    无法弄清楚如何合并两个列表通过以下方式在哈斯克尔 INPUT 1 2 3 4 5 11 12 13 14 OUTPUT 1 11 2 12 3 13 4 14 5 我想提出一个更懒的合并版本 merge ys ys merge x xs y
  • Data.Sequence 中的 inits 和 tails 如何工作?

    Louis Wasserman 编写了当前的实现inits and tails in Data Sequence 他表示它们非常高效 事实上 只要查看代码 我就可以看到 无论它们在做什么 它们都是以干净 自上而下的方式进行的 这往往会给惰性
  • 如何在不声明新数据的情况下更改类型(String,Int)元组的 Ord 实例?

    我正在尝试对类型列表进行排序 String Int 默认情况下 它按字符串排序 然后按整数排序 如果字符串相等 我希望它是相反的 首先比较整数 然后如果相等则比较字符串 另外 我不想切换到 Int String 我找到了一种通过定义实例来实
  • 类型级别集结合律的证明

    我试图证明类型级函数Union https hackage haskell org package type level sets 0 8 5 0 docs Data Type Set html t Union是关联的 但我不确定应该如何完
  • 迭代打印列表中的每个整数

    假设我有一个整数列表l 1 2 我想打印到stdout Doing print l产生 1 2 假设我想打印不带大括号的列表 map print l产生 No instance for Show IO arising from a use

随机推荐