同时具有“空”和“也许”的概念是否有意义?


11

在C#中为Web API创建客户端时,我遇到了一个问题,该问题null是它代表两个不同的东西:

  • 一无所有,例如foo可能有也可能没有bar
  • 未知:默认情况下,API响应仅包含属性的子集,您必须指出所需的其他属性。因此,未知表示未从API请求该属性。

经过一番搜索,我发现了Maybe(或Option)类型,它在功能语言中的用法以及如何通过迫使用户考虑可能缺少值的方式来“解决” null取消引用问题。然而,所有的资源,我遇到谈到更换可能。我确实找到了三值逻辑的一些提及,但是我并不完全理解它,而且多数时候提到这是在“这是一件坏事”的背景下。

我现在想知道将nullMaybe的概念分别表示未知什么都不有意义。这是我读过的三值逻辑,还是别的名字?还是在Maybe中嵌套Maybe的预期方式?


9
拥有它没有道理null。这是一个彻底破裂的想法。
Andrej Bauer

7
不要使语义可能不同于foo的maybe-foo。也许是monad,并且monad的法则之一就是,M M x并且M x应该具有相同的语义。
埃里克·利珀特

2
您可能会考虑查看Visual Basic的早期版本的设计,该版本没有内容(无对象引用),空值(数据库空语义),空值(未初始化的变量)和缺失(未传递可选参数)。这种设计很复杂,并且在许多方面都不一致,但是有一个原因不能将这些概念相互混淆。
埃里克·利珀特

3
Maybe aa+1Maybe Maybe aa+1+1UserInput a

12
@EricLippert:“ M (M x)并且M x应该具有相同的语义” 是错误的。以M = List列表为例:列表列表与列表不同。当M是monad时,存在从到的转换(即monad乘法)M (M x)M x解释它们之间的关系,但是它们没有“相同的语义”。
Andrej Bauer

Answers:


14

null作为默认的现值处处是只是一个真正的坏主意,所以忘了这一点。

您应该始终拥有最能准确描述您的实际数据的概念。如果您需要一个表示“未知”,“没有”和“值”的类型,则应具有确切的含义。但是,如果它不符合您的实际需求,那么您就不应该这样做。最好看看其他人使用了什么以及他们提出了什么,但是您不必盲目地跟随他们。

设计标准库的人会尝试猜测常见的使用模式,通常他们会做得很好,但是如果您需要特定的东西,则应该自己定义。例如,在Haskell中,您可以定义:

data UnknownNothing a =
     Unknown
   | Nothing
   | Value a

甚至可能是您应该使用更易于记忆的更具描述性的内容的情况:

data UserInput a =
     Unknown
   | NotGiven
   | Answer a

您可以定义10种用于不同方案的类型。缺点是您将没有可用的预先存在的库函数(如的库函数Maybe),但这通常是一个细节。添加自己的功能并不难。


当您看到这样拼写出来时,这是非常有意义的。我最感兴趣的是想法,现有的库支持只是一个好处:)
Stijn

如果在许多语言中创建此类类型的障碍更低。:/
拉斐尔

如果只有人们停止使用1960年代的语言(或1980年代的转世语言)。
安德烈·鲍尔

@AndrejBauer:什么?但是理论家就没有什么可抱怨的了!
Yttrill

我们不抱怨,我们高高在上。
安德烈·鲍尔

4

据我所知,nullC#中的值是某些变量的可能值,具体取决于其类型(是吗?)。例如,某个类的实例。对于其余类型(如intbool等),您可以通过用int?bool?代替声明变量来添加此异常值(这正是Maybe构造函数的作用,正如我将在下面描述的那样)。

Maybe函数式编程的类型构造函数为给定的数据类型添加了这个新的异常值。因此,如果Int是整数类型或Game游戏状态类型,则将Maybe Int所有整数加一个空值(有时称为nothing)。相同Maybe Game。在这里,null值没有附带任何类型。您在需要时添加它。

IMO,对于任何一种编程语言,最后一种方法都是更好的方法。


是的,您对C#的理解是正确的。因此,我可以根据您的回答推断出您建议嵌套Maybes并null完全删除它吗?
Stijn

2
我的意见是关于一种语言应该如何。我真的不了解C#编程的最佳实践。在您的情况下,最好的选择是定义一个数据类型,如@Andrej Bauer所述,但我认为您无法在C#中做到这一点。
2016年

@Euge:可以用经典的OO样式子类型和子类型多态消息分派来近似(近似)代数和类型。例如,这就是在Scala中完成的方式,另外还增加了允许封闭类型的方式。类型成为抽象的超类,类型构造函数成为具体的子类,通过将案例移入具体子类中的重载方法或isinstance测试中,可以近似于构造函数的模式匹配。Scala也具有“适当的”模式匹配,实际上C♯最近也获得了简单的模式。
约尔格W¯¯米塔格

我为Scala提到的“附加特性”是,除了“标准”修饰符“虚拟”(对于可以继承final的类)和(对于不能继承的类)之外sealed,对于只能在同一编译单元内扩展的类。这意味着编译器可以静态地知道所有可能的子类(前提是所有子类都是它们自己sealedfinal),因此可以对模式匹配进行详尽的检查,这对于带有运行时代码加载和无限制继承的语言是不可能的。
约尔格W¯¯米塔格

当然,您可以在C#中使用这些语义来设计类型。请注意,C#不允许嵌套内置的也许类型(在C#中称为Nullable)。 int??在C#中是非法的。
埃里克·利珀特

3

如果您可以定义类型联合,例如X or Y,并且您有一个Null仅表示null值(而没有别的)的名称类型T,那么对于任何类型,T or Null实际上与并没有太大区别Maybe T。与唯一的问题null是,当语言对待一个类型TT or Null含蓄,使得null每一个类型(有他们的语言,除了基本类型)的有效值。例如,在Java中,采用a的函数String也可以接受null

如果将类型视为值or的集合和集合的并集,则(T or Null) or Null表示值的集合,T ∪ {null} ∪ {null}与相同T or Null。有些编译器执行这种分析(SBCL)。如评论中所述,您无法轻松地区分Maybe TMaybe Maybe T何时将类型视为域(另一方面,该视图对于动态类型的程序非常有用)。但是您也可以将类型保留为代数表达式,其中(T or Null) or Null表示Maybes的多个级别。


2
实际上,Maybe (Maybe X)与并非同一件事很重要Maybe XNothing与...不同Just Nothing。与之Maybe (Maybe X)合并Maybe X将无法进行Maybe _多态处理。
吉尔斯(Gilles)'所以

1

您需要找出您想要表达的内容,然后找到一种表达方式。

首先,您在问题域中具有值(例如数字,字符串,客户记录,布尔值等)。其次,您在问题域值之上还具有一些附加的通用值:例如,“无”(已知值不存在),“未知”(不知道值存在或不存在),而未提及的是“东西”(肯定有一个值,但我们不知道是哪个)。也许您可以想到其他情况。

如果您希望能够表示所有值以及这三个附加值,则可以创建带有“ nothing”,“ unknown”,“ something”和“ value”用例的枚举,然后从那里开始。

在某些语言中,某些情况更简单。例如,在Swift中,每种类型T都有一个“可选T”类型,其值可能为nil加上所有T的值。这使您可以轻松处理许多情况,如果您没有“ nothing”和“值”。如果您具有“未知”和“值”,则可以使用它。你可以,如果你需要办理“无”,“未知”和“价值”的使用。其他语言可能使用“ null”或“也许”,但这只是另一个词。

在JSON中,您有带有键/值对的字典。在这里,字典中可能只是缺少一个键,或者它的值可能为null。因此,通过仔细描述事物的存储方式,您可以表示通用值加上两个附加值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.