2013-12-06 6 views
3

Я пытаюсь понять монаду IO и синтаксис <-, который я часто вижу в коде Haskell. Я видел, что он используется с несколькими типами данных, включая массивы и IO.Что такое эквивалент clojure для Haskell's <-?

Что такое эквивалентная операция в clojure, если я должен был указать ее сам?

+7

Заметим, что '<-' (и' do'-обозначения в целом) не является оператором на всех, а синтаксический сахар для '(>> =)' оператор (произносится * привязывать *). Поскольку '(>> =)' - метод класса (класса «Монад»), он имеет совершенно разные реализации в зависимости от базового типа данных. – kosmikus

+0

Как это реализовано для ввода-вывода? – zcaudate

+0

Он не охватывает <- как таковой, но, возможно, этот пост, связанный с функциями Clojure и Haskell, может помочь: http://productivedetour.blogspot.com.es/2013/05/haskell-equivalents-of-some- clojure.html – danidiaz

ответ

3

Используя algo.monads, мы можем легко определить IO-монаду (если нечаянно).

В Haskell IO монада type IO a = World -> (a, World). Приятно думать об этом как о действии - что-то, что берет мир, делает что-то, и возвращает значение и мир.

Использование вектора вместо кортежа, это означает, что, в Clojure, действие IO (монадическое значение IO монады) выглядит примерно так:

(fn [world] 
    ; some stuff 
    [value world]) 

Чтобы сделать что-то интересное, нам нужно несколько действий: get-char и put-char.

get-char это действие, которое происходит в мире, читает символ, и возвращает этот символ в качестве своего значения наряду с миром:

(defn read-char 
    [] 
    (-> *in* .read char)) 

(defn get-char 
    [world] 
    [(read-char) world]) 

put-char принимает характер и создает действие, которое, учитывая мир, печатает символ и возвращают некоторое (несущественное) значение:

(defn put-char 
    [c] 
    (fn [world] 
    (print c) 
    [nil world])) 

Обратите внимание, что, чтобы сделать действие произойдет, мы должны поставить мир.Например, (put-char \a) вернет действие; ((put-char \a) :world) будет ссылаться на это действие, печать a и возвращение [nil :world].

Составление этих действий является потенциально очень грязным процессом. Если, например, вы хотели получить символ, затем распечатайте его, вам нужно будет позвонить get-char, распаковать его характер и мир, создать действие для этого символа с put-char, а затем передать мир этому действию.

С другой стороны, если мы определим монаду, мы получим domonad (что эквивалентно) от Haskell. Этот синтаксический сахар смягчает шаблон для распаковки/упаковки. Нам просто нужно несколько функций: m-result и m-bind (m-zero и m-plus также удобны, но не нужны).

m-result (return в Haskell) принимает значение и оборачивает это как действие:

(fn [v] 
    (fn [world] 
    [v world])) 

m-bind (>>= в Haskell) принимает действие и функцию, которая принимает регулярное значение, чтобы произвести действие, «разворачивает» значение, вызывая действие, и применяет к нему функцию. С монады IO, который выглядит следующим образом:

(fn [io f] 
    (fn [world] 
    (let [[v new-world] (io world)] 
     ((f v) new-world)))) 

Таким образом, используя algo.monads, мы можем определить io-m следующим образом:

(defmonad io-m 
    [m-result (fn [v] 
       (fn [world] 
       [v world])) 

    m-bind (fn [io f] 
      (fn [world] 
       (let [[v new-world] (io world)] 
       ((f v) new-world))))]) 

Теперь, когда мы получили примитивные действия ввода-вывода и средства составляя их, мы можем создавать более интересные. Обратите внимание, что распаковка оператор в Haskell (<-) подразумевается и результат автоматически обернут m-result поэтому мы не используем return о Haskell, чтобы прекратить выражения:

(declare get-rest-of-line) 

(def get-line 
    (domonad io-m 
    [c get-char 
    line (if (= c \newline) 
       (m-result "") 
       (get-rest-of-line c))] 
    line)) 

(defn get-rest-of-line 
    [c] 
    (domonad io-m 
    [cs get-line] 
    (str c cs))) 

(defn put-line 
    [s] 
    (if (seq s) 
    (domonad io-m 
     [_ (put-char (first s)) 
     _ (put-line (subs s 1))] 
     _) 
     (put-char \newline))) 

Наконец, мы можем написать программу, с точки зрения их IO действия:

(def run-program 
    (domonad io-m 
    [line get-line 
    :let [reversed-line (->> line reverse (apply str))] 
    _ (put-line reversed-line)] 
    _)) 

(run-program :world) 
6

Do-notation - это просто сахар для стандартных операций монады. Например, если у вас есть что-то вроде этого:

do 
    x <- someMonad 
    return (someFunction x) 

Это эквивалентно следующему:

someMonad >>= \x -> return (someFunction x) 

Так эквивалент Clojure, используя один из многих библиотек монада может быть что-то вроде этого:

(m-bind some-monad (fn [x] (m-result (some-function x)))) 
+0

Я ищу конкретный пример того, как bind может быть реализован в clojure с использованием printf и readline для io monad – zcaudate

3

Я думаю, что Чак ответил на ваш главный вопрос, но в случае, если вы хотите изучить способ осуществления монадских операций в Clojure с использованием algo.monads в качестве примера Следующее:

(domonad state-m 
    [_ (set-state "foo") 
    x (fetch-state)] 
    x) 

эквивалентно (ну, почти, см ниже) Хаскеля

do 
    _ <- put "foo" -- see below for a comment on this 
    x <- get 
    return x 

В algo.monads<- исчезает, потому что фактически подразумевается в каждой строке.

О «почти» и в _ выше: _ на самом деле это не волшебство в Clojure, и она будет привязана к стоимости возвращенного set-state, но это идиоматическое использовать этот символ в качестве имени местных жителей один не делает заботиться. Конечно, в Haskell было бы более обычным просто написать put "foo" вместо _ <- put "foo".

+0

Спасибо Michal .. была бы монада IO реализована так же, как и состояние? – zcaudate

+1

@zcaudate, да, это может быть на каком-то уровне ... Подумайте о том, как читать SPJ [документ] (https://www.google.ru/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCsQFjAA&url = HTTP% 3A% 2F% 2Fresearch.microsoft.com% 2Fen-связь% 2Fum% 2Fpeople% 2Fsimonpj% 2Fpapers% 2Fmarktoberdorf% 2Fmark.pdf.gz & е = bkuiUu6SCsbZ4QTvxYHYBQ & USG = AFQjCNGFue8D4X6wV9nLevHZ-sjjKJc8Eg & Sig2 = Prah0KQaBIiCrwy_VDF9Dg & BVM = bv.57752919, d.bGE & CAD = RJT) по предмету, он может предоставить некоторую полезную информацию о монаде IO. – ownclo

Смежные вопросы