【问题标题】:Why do Maybe/Optional types use a Just/Some type instead of the actual type?为什么 Maybe/Optional 类型使用 Just/Some 类型而不是实际类型?
【发布时间】:2017-01-27 17:10:40
【问题描述】:

在 Idris 中,Maybe 类型定义如下:

data Maybe a = Just a | Nothing  

Haskell 中的定义类似:

 data Maybe a = Just a | Nothing
     deriving (Eq, Ord)

这是机器学习版本:

datatype 'a option = NONE | SOME of 'a

使用JustSome 有什么好处?
为什么不定义没有它们的类型?

示例:

data Maybe a = a | Nothing

【问题讨论】:

  • data Maybe a = a | Nothing 不是有效的 Haskell——它不会编译。该定义将有效地暗示Maybe a 是任何值的联合Nothing,但这毫无用处,因为Nothing 也是“任何值”。 (Haskell 的类型系统根本不支持这样的无标签联合,因为这需要一些“子类型”的概念,而 Haskell 没有。)
  • 不,Maybe a 将是 aNothing 的并集,而 Nothing 通常不是 a 类型的值,因此像 Maybe Int 这样的类型不会不要没用。更正 Haskell 不支持这一点。

标签: haskell types ml idris maybe


【解决方案1】:

那么有什么区别

Maybe a

Maybe (Maybe a)

?

NothingJust Nothing 之间应该有区别。

【讨论】:

  • 出于某种原因,每当我发现我必须输入pure Nothing :) 时,我都会觉得很有趣。
  • @Reid 因为将Maybe 应用于类型会创建具有一个附加值的新类型。这意味着应用它两次应该会创建一个具有两个附加值的新类型。
  • 当然,Haskell(和 Idris 等)的语义表明 NothingJust Nothing 是不同的,但这只是在推回问题。
  • 如果您认为失败是一种计算效应(并且在失败之前没有其他可观察的情况发生),那么将失败视为与稍后失败相同是完全合理的。
  • 这出现在书中Land of Lisp(find-if #'null '(2 4 nil 6)) 正确返回满足null 谓词(nil)的元素,但如果没有元素满足谓词,则结果相同! Haskell 更清晰:find (== Nothing) [Just 2, Just 4, Nothing, Just 6]Just Nothing 成功(因为它在列表中找到了 Nothing)但 find (== Nothing) [Just 2, Just 4, Just 6]Nothing 失败。
【解决方案2】:

问题在于,如果Maybe 是按照您建议的方式定义的,即data Maybe a = a | Nothing,则无法区分a 值和Maybe a 值(以及Maybe (Maybe a))。

所以你可能会问,为什么我们需要有这样的区别?有什么好处?举一个具体的例子,假设我们有一个带有integer NOT NULL 列的SQL 表。我们将在 haskell 中用Int 来表示。现在,如果我们稍后通过删除NOT NULL 约束来更改数据库模式以使列成为可选,我们将不得不将列的haskell 表示更改为Maybe IntIntMaybe Int 之间的明显区别将使重构我们的haskell 代码以适应新模式变得非常容易。编译器会抱怨诸如从数据库中提取一个值并将其视为Int(它可能不是整数,它可能是NULL)。

【讨论】:

    【解决方案3】:

    我不确定在这种情况下谈论“好处”是否正确。您在这里所拥有的只是在 Haskell 和 ML 中实现类型的方式的结果——基本上是 Hindley-Milner 代数类型系统。这种类型系统本质上假设每个值都属于一个 single 类型(撇开 Haskell 的数字塔和底部,这些不在讨论范围内。)换句话说,没有 子类型 em>,这是有原因的 - 否则类型推断将无法确定。

    当您定义类型Maybe a 时,您想要加入一个附加值到a 表示的类型。但是你不能直接这样做——如果你可以,那么a 的每个值都属于两种不同的类型——原始的aMaybe a。相反,所做的是将a 嵌入 在一个新类型中 - 你有一个规范注入 a -> Just a。换句话说,Maybe aaNothing 的联合同构,您无法在 HM 类型系统中直接表示。

    因此,我认为这种区分有益的论点是站不住脚的——没有它,你就不能拥有一个仍然是 ML 或 Haskell 或任何熟悉的基于 HM 的系统的系统。

    【讨论】:

    • Haskell 类型推断是否可判定?
    • 套用搭便车者指南,大部分是可判定的。
    • 控方休息。
    • @augustss 通常,我的意思是 GHC 几年后会做的事情,但今天,多态递归会做得很好。
    • 但是用于多态递归的 Haskell 类型“推断”是可确定的,因为您必须给出类型签名。 :)
    【解决方案4】:

    允许任何值为null(“十亿美元错误”)的关键问题是接收T 类型值的接口无法声明它们是否可以处理@987654323 @,而提供一个的接口无法声明它们是否会产生null。这意味着当您将T 传递给T 时,所有 可用于T 的操作基本上“可能无法正常工作”,这对于据称由编译时类型检查。

    Maybe/Optional 解决方案是说 T 类型 不包含 null 值(在从一开始就有这个的语言中,这是字面意思;在采用一个可选类型稍后不删除对null 的支持,那么这只是一个需要纪律的约定)。所以现在所有类型说他们接受T的操作应该在我传递T时工作,无论我从哪里得到T(如果你还没有设法设计为“使非法状态无法表示”,那么当然会有其他原因导致对象处于无效状态并导致失败,但至少当你通过 T 时,实际上会有 一些东西 em> 那里)。

    有时我们确实需要一个可以是“T 或什么都没有”的值。毕竟,在当时普遍存在null 似乎是一个好主意,这这样很常见。输入Maybe T 类型。但是为了避免回到完全相同的旧陷阱,我得到一个可能为空的 T 值并将其传递给无法处理 null 的东西,我们需要 @987654339 上的任何操作@ 可以直接用于Maybe T。尝试这样做会导致类型错误是整个练习的重点。所以我的T 值不能直接是Maybe T 的成员;我需要将它们包裹在内部Maybe T,这样如果我有Maybe T,我就不得不编写处理两种情况的代码(并且仅在实际拥有T 的情况我可以调用在T 上工作的操作)。

    这是否使JustSome 之类的词出现在源代码中,以及这是否实际上是通过内存中的额外装箱/间接实现的(某些语言确实将Maybe T 表示为可空指针到T 内部),所有这些都是无关紧要的。但是Just a 的情况必须不同于简单地具有a 值。

    【讨论】:

    • 这个论点在接近尾声的某个地方分崩离析,因为具有子类型的语言可以具有真正的可选(或更一般地,联合)类型。您认为TMaybe T 必须不同,但这并不意味着T 类型的值也不能具有Maybe T 类型。
    【解决方案5】:

    构造函数(JustSome)的好处是它提供了一种区分数据类型分支的方法。也就是说,它避免了歧义。

    例如,如果我们依赖于类型推断,那么下面 x 的类型似乎相当简单——String

    x = "Hello"
    

    但是,如果我们允许您定义Maybe,我们怎么知道xStringMaybe String 还是Maybe (Maybe String) 等等。

    同时考虑一个数据类型有两种以上的情况:

    data Tree a
      = Empty
      | Node (Tree a) (Tree a)
      | Leaf a
    

    如果我们只是删除构造函数(Empty 除外),按照您对 Maybe 的建议,我们最终会得到:

    data Tree a
      = Empty
      | (Tree a) (Tree a)
      | a
    

    我希望你能看到歧义变得更糟。

    【讨论】:

      【解决方案6】:

      考虑一下这个有点相当于 C++/Java'ish 伪代码中的 Maybe...

      template<class T>
      abstract class Maybe<T> { ... }
      
      template<class T>
      class Just<T> : Maybe<T> {
          // constructor
          Just<T> (T val) { ... }
      
          ...
      }
      
      template<class T>
      class Nothing<T> : Maybe<T> {
          // constructor
          Nothing () { ... }
      
          ...
      }
      

      这并不特定于 Maybe,它可以应用于任何 ADT。现在究竟会发生什么

      data Maybe a = a | Nothing
      

      模型成 ? (假设它的合法语法)。

      如果您要编写一个 switch 语句,以针对类型进行“模式匹配”,您将匹配什么(开关在 TYPE 上而不是值上),类似这样(不一定是有效代码):

      switch (typeof (x)) {
          case Just<a> : ...
          case Nothing<a> : ...
          default : ... // Here you dont have any 'a' to get the inner type
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-06
        • 2021-01-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多