为什么Maybe / Optional类型使用Just / Some类型而不是实际类型?

时间:2016-09-19 22:10:47

标签: haskell types ml idris maybe

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

data Maybe a = Just a | Nothing  

它在Haskell中的定义类似:

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

这是ML版本:

datatype 'a option = NONE | SOME of 'a

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

示例:

data Maybe a = a | Nothing

6 个答案:

答案 0 :(得分:25)

之间会有什么区别
Maybe a

Maybe (Maybe a)

NothingJust Nothing之间应该存在差异。

答案 1 :(得分:10)

允许任何值为null("十亿美元的错误")的关键问题是接收类型T的值的接口无法声明是否可以处理null,并且提供一个的接口无法声明它们是否可以生成null。这意味着T基本上可以使用的所有操作可能无效"当你传递T时,这是一个相当大的漏洞,在所有由编译时类型检查提供的保证中。

对此的可能/可选解决方案是,T 类型不包含 null值(从一开始就有这种语言,&# 39; s literal;在以后采用Optional类型而不删除对null的支持的语言中,那只是一个需要约束的约定)。所以现在所有类型都表示接受T 的操作应该工作,当我传递T时,无论我在哪里获得T(如果你没有设法设法使非法国家无法代表"那么当然还有其他原因导致对象处于无效状态并导致失败,但至少在你通过{时{1}}实际上

有时我们确实需要一个可以是T或者没有"的值。它毕竟是这样的普遍的情况,普遍的T似乎是一个好主意。输入null类型。但是为了避免重新陷入完全相同的旧陷阱,我得到一个可能为空的Maybe T值并将其传递给无法处理T的值,我们需要 none null上的操作可直接用于T 。尝试这样做会导致类型错误,这是练习的全部要点。因此,我的Maybe T值不能直接成为T的成员;我需要将它们包含在里面一个Maybe T中,这样如果我有一个Maybe T,我就不得不编写处理两者的代码案例(仅在实际拥有Maybe T的情况下才能调用适用于T的操作。)

是否会在源代码中出现TJust之类的单词,以及这是否实际上是通过内存中的附加装箱/间接实现的(某些语言确实代表{{1作为内部Some的可空指针,所有这些都是无关紧要的。但Maybe T案例必须与仅具有T值不同。

答案 2 :(得分:7)

我不确定在这种情况下谈论“好处”是否正确。你在这里所拥有的只是在Haskell和ML中实现类型的方式的结果 - 基本上是Hindley-Milner代数类型系统。这种类型的系统基本上假定每个值都属于类型(放弃Haskell的数字塔和底部,这不在本讨论中。)换句话说,没有子类型,这是有原因的 - 否则类型推断将是不可判定的。

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

所以我不认为这种区分有益的论点是有效的 - 你不能拥有一个没有它的系统,它仍然是ML或Haskell或任何熟悉的基于HM的系统。

答案 3 :(得分:1)

问题在于,如果Maybe按照您提议的方式定义,即data Maybe a = a | Nothing,则无法区分a值与Maybe a值(和{{} 1}}就此而言)。

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

答案 4 :(得分:0)

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

例如,如果我们依赖于类型推断,那么下面的x类型看起来相当简单 - String

x = "Hello"

但是,如果我们允许您定义Maybe,我们如何知道xStringMaybe String还是Maybe (Maybe String)等。< / p>

还要考虑具有两种以上情况的数据类型:

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

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

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

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

答案 5 :(得分:0)

在C ++ / Java'ish伪代码中考虑这个有些等同于...

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
}