为什么在Clojure中使用Maybe/Option并不是那么普遍?

mis*_*tor 40 monads functional-programming clojure option maybe

为什么Clojure尽管如此强调功能范式,却不使用Maybe/ Optionmonad来表示可选值?Option在Scala中使用非常普遍,Scala是我经常使用的函数式编程语言.

ama*_*loy 48

Clojure不是静态类型的,因此不需要严格的this/that/haskell中必需的任何类型声明(并且,我收集,Scala).如果要返回一个字符串,则返回一个字符串; 如果你改为nil,那也没关系.

"Functional"并不完全对应于"严格的编译时键入".它们是正交概念,Clojure选择动态类型.事实上,很长一段时间以来我无法想象如何实现许多高阶函数,map并且仍然保留静态类型.既然我对Haskell有一点点(很少)的经验,我可以看到它是可能的,而且往往非常优雅.我怀疑,如果你玩Clojure一段时间,你会有相反的经历:你会发现类型声明不一定能给你一种你习惯用的功能语言.

  • @missingfaktor:不,我认为amalloy有点动态类型的语言经常使用一些值(例如`nil`)来表示"没有这样的东西",但这个值是不同的类型(在Scheme中,它是一个列表;在Ruby中,它是`NilClass`的唯一实例;我从未使用过Clojure,但我认为它就像在这方面的Scheme一样.没有静态类型系统,就没有必要将结果提升为"可能"; 这是隐含的,可以这么说.我个人倾向于喜欢Haskell/Scala的方式,但动态类型语言中的其他方法没有任何问题. (28认同)
  • 你可能没有得到我的问题.我的问题与类型声明完全无关. (6认同)
  • 根据我的经验,无论所讨论的语言是静态类型还是动态类型,`Maybe` /`Option`都是有用的. (4认同)

aga*_*man 24

在Clojure中,nil punning提供了Scala和Haskell从Option&Maybe获得的大部分功能.

**Scala**                **Clojure**
Some(1) map (2+_)        (if-let [a 1] (+ 2 a))

Some(1) match {          (if-let [a 1]
  case Some(x) => 2+x      (+ 2 a)
  case None    => 4        4)
}
Run Code Online (Sandbox Code Playgroud)

Scala的Option和Haskell的Maybe都是Applicative的实例.这意味着您可以在理解中使用这些类型的值.例如,Scala支持:

for { a <- Some(1)
      b <- Some(2)
} yield a + b
Run Code Online (Sandbox Code Playgroud)

Clojure for macro提供了对seq的理解.与monadic comprehensions不同,此实现允许混合实例类型.

虽然Clojure的for不能用于在多个可能的nil值上编写函数,但它的功能实现起来却微不足道.

(defn appun [f & ms]
  (when (every? some? ms)
    (apply f ms)))
Run Code Online (Sandbox Code Playgroud)

并称之为:

(appun + 1 2 3)    #_=> 6
(appun + 1 2 nil)  #_=> nil
Run Code Online (Sandbox Code Playgroud)


Ton*_* K. 17

重要的是要记住,Monad概念与类型无关!类型系统可以帮助您实施规则(但即使是Haskell也无法强制执行所有规则,因为其中一些规则(Monad Laws)无法通过类型系统完全表达.

Monads是关于组合的,这是我们每天在每种编程语言中都做的非常重要的事情.在所有情况下,Monad跟踪一些关于正在发生的事情的"额外背景"......把它想象成一个保持当前价值的盒子.函数可以应用于此值,额外的上下文可以演变为正交关注点.

Maybe类型是关于将长的计算序列链接在一​​起而不必对失败说什么(这是"额外的上下文").这是一种将"错误处理"从计算中移出并进入Monad的模式.你可以在一个Maybe上串起一系列的计算,一旦失败,其余的都被忽略,最后的结果是"什么都没有".如果它们都成功,那么你的最终结果是monad持有结果值.

这允许您编写更少纠缠的代码.

正如@deterb指出的那样,Clojure支持Monads.


Vag*_*rdi 13

也许/选项是一种类型.它与函数式编程无关.是的,除了功能之外,一些语言(Scala,haskell,ocaml)也提供了一个非常强大的类型系统.人们甚至会说haskell是一个带有TYPES的编程.

其他(clojure,lisp)在类型方面提供的不多,即使它们是功能完备的语言.他们的重点是不同的,并且可能/选项类型不适合.它只是不会给你很多动态语言.例如,对序列(列表,向量,映射)进行操作的许多clojure函数将完全接受null(nil)并将其视为空结构.

(数nil)会给你0.就像(count [])

Clojure不能被称为"带类型的编程",因此Maybe类型在其中没有多大意义.


jne*_*ira 7

好吧有一个Maybe monad,但是它使用nil作为Nothing,捕获计算的抽象(如果input = nil返回nil,则无法使用输入计算任何内容)以避免空指针错误,但它没有静态编译时安全性.还有fnil也有类似的任务,修补nil默认值和- ?>.我认为clojure方式更倾向于返回引发错误或nil的默认值.


det*_*erb 5

使用@amalloy 并评论说 Clojure 作为一种语言,不需要可选的返回值。

我在 Scala 方面做得不多,但 Clojure 不需要知道关于返回类型的严格细节就可以处理一个值。这几乎就像嵌入了一个 Maybe monad 并成为正常 Clojure 评估的一部分,因为许多操作,如果在 上执行nil,则返回nil

我快速浏览了 Clojure-Contrib 库,他们有一个monad 包,您可能想查看一下。另一个让我真正了解如何在 Clojure 中使用 Monads 的项目是Cosmin 关于 Clojure 中的 Monads 的教程。正是这一点帮助我了解如何将 Scala 中更明确说明的功能作为动态 Clojure 语言的一部分进行处理。