Почему использование Maybe / Option не столь распространено в Clojure? - PullRequest
39 голосов
/ 30 апреля 2011

Почему Clojure, несмотря на такой акцент на функциональной парадигме, не использует монаду Maybe / Option для представления необязательных значений?Использование Option широко распространено в Scala, функциональном языке программирования, которым я регулярно пользуюсь.

Ответы [ 7 ]

46 голосов
/ 30 апреля 2011

Clojure не является статически типизированным, поэтому ему не нужны строгие объявления типа this / that / what, необходимые в haskell (и, насколько я понимаю, Scala).Если вы хотите вернуть строку, вы возвращаете строку;если вместо этого вы вернете nil, это тоже нормально.

«Функциональный» не совсем соответствует «строгой типизации во время компиляции».Это ортогональные понятия, и Clojure выбирает динамическую типизацию.Фактически, в течение довольно долгого времени я не мог представить, как можно реализовать многие функции высшего порядка, такие как map, и при этом сохранить статическую типизацию.Теперь, когда у меня есть небольшой (очень маленький) опыт работы с Haskell, я вижу, что это возможно, и действительно часто довольно элегантно.Я подозреваю, что если вы поиграете с Clojure некоторое время, у вас будет противоположный опыт: вы поймете, что объявления типов не необходимы , чтобы дать вам ту силу, которую вы привыкли иметь вфункциональный язык.

23 голосов
/ 28 марта 2014

В Clojure нулевое наказание обеспечивает большую часть функциональности, которую 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)
}

Опция Scala и вариант Haskell Maybe - оба являются примерами Applicative. Это означает, что вы можете использовать значения этих типов в понимании. Например, Scala поддерживает:

for { a <- Some(1)
      b <- Some(2)
} yield a + b

Clojure для макроса обеспечивает понимание над seq. В отличие от монадических представлений, эта реализация позволяет смешивать типы экземпляров.

Хотя Clojure for нельзя использовать для составления функций из нескольких возможных значений nil, его функциональность тривиальна для реализации.

(defn appun [f & ms]
  (when (every? some? ms)
    (apply f ms)))

И называя это:

(appun + 1 2 3)    #_=> 6
(appun + 1 2 nil)  #_=> nil
15 голосов
/ 10 апреля 2015

Важно помнить, что концепция Monad не о типах! Системы типов помогают вам применять правила (но даже Haskell не может применять все правила, поскольку некоторые из них (законы монад) не могут быть полностью выражены системой типов.

Монады - это композиция, и это очень важная вещь, которую мы все делаем каждый день на каждом языке программирования. Во всех случаях Monad отслеживает некоторый «дополнительный контекст» о том, что происходит ... думайте об этом как о блоке, который удерживает текущее значение. К этому значению могут применяться функции, а дополнительный контекст может развиваться как ортогональная задача.

Тип Maybe предназначен для объединения длинных последовательностей вычислений, при этом не нужно ничего говорить о сбое (что является «дополнительным контекстом»). Это шаблон, который перемещает «обработку ошибок» из вычислений в монаду. Вы можете связать последовательность вычислений с типом Maybe, и как только один из них завершится неудачей, остальные будут проигнорированы, и в результате вы получите «ничего». Если все они успешны, то ваш окончательный результат - монада, содержащая значение результата.

Это позволяет вам писать код, который намного менее запутан.

Clojure поддерживает монады, как указывает @deterb.

12 голосов
/ 30 апреля 2011

Возможно / Option это тип. Это не имеет ничего общего с функциональным программированием. Да, некоторые языки (Scala, haskell, ocaml) помимо функциональности также предоставляют очень мощную систему типов. Люди даже говорят о haskell, что это программирование с типами.

Другие (clojure, lisp) не предоставляют много с точки зрения типов, даже если они полностью функциональные языки. Их акцент различен, и тип Maybe / Option не подходит. Он просто не дает вам много в динамическом языке. Например, многие функции clojure, работающие с последовательностями (списки, векторы, карты), будут отлично принимать ноль (nil) и рассматривать его как пустую структуру.

(количество ноль) даст вам 0. Также как (количество [])

Clojure нельзя назвать «программированием с типами», и поэтому тип может не иметь в этом особого смысла.

7 голосов
/ 30 апреля 2011

Ну, есть монада Maybe, но она использует nil как Nothing, захватывая only абстракцию вычислений (если input = nil возвращает nil, в противном случае calc независимо от ввода), чтобы избежать ошибок нулевых указателей, но она нестатическая безопасность во время компиляции.Также есть fnil , у которых есть аналогичная миссия, исправляя nil со значениями по умолчанию и -?> .Я думаю, что способ clojure более ориентирован на возврат значений по умолчанию, которые вызывают ошибки или ноль.

4 голосов
/ 05 мая 2011

Далее @amalloy и комментарии о том, что Clojure, как языку, не требуется дополнительное возвращаемое значение.

Я мало что сделал со Scala, но Clojure не нужно знать строгие детали о типе возвращаемого значения, чтобы иметь возможность работать со значением. Это почти как если бы монада Maybe была встроена и стала частью обычной оценки Clojure, так как многие операции, если они выполняются на nil, возвращают nil.

Я быстро взглянул на библиотеку Clojure-Contrib, и у них есть пакет монад , на который вы, возможно, захотите взглянуть. Еще один пункт, который действительно дал мне понять, как можно использовать монады в Clojure, - это учебник Космина по монадам в Clojure . Именно это помогло мне понять, как функциональность, изложенная более явно в Scala, обрабатывается как часть динамического языка Clojure.

1 голос
/ 19 июня 2018

Имеется some-> и some->>, доступных с Clojure 1.5

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (some-> input :c :f :h inc))
user> 8

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (some-> input :c :z :h inc))

user> nil

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (-> input :c :z :h inc))

user> NullPointerException   clojure.lang.Numbers.ops (Numbers.java:1013)

Функция some-> предоставляет опцию [T].

...