简体   繁体   English

什么是Haskell的< - ?等效的clojure

[英]What is the clojure equivalent for Haskell's <-?

I'm trying to figure out the IO monad and the <- syntax which I often see in Haskell code. 我试图找出IO monad和我经常在Haskell代码中看到的<-语法。 I've seen it used with multiple datatypes, including arrays and IO. 我已经看到它用于多种数据类型,包括数组和IO。

What is the equivalent operation in clojure if I were to specify one myself? 如果我自己指定一个,那么clojure中的等效操作是什么?

Do-notation is just sugar for the standard monad operations. 标记只是标准monad操作的糖。 For example, if you have something like this: 例如,如果你有这样的东西:

do
  x <- someMonad
  return (someFunction x)

That's equivalent to this: 这相当于:

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

So the equivalent Clojure using one of the many monad libraries might be something like this: 所以使用众多monad库之一的等效Clojure可能是这样的:

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

I think Chuck has answered your main question, but in case you'd like to investigate the way the monad operations can be implemented in Clojure using algo.monads as an example, the following: 我认为Chuck已经回答了你的主要问题,但是如果你想调查使用algo.monads作为例子在Clojure中实现monad操作的方式,请执行以下操作:

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

is equivalent (well, almost, see below) to Haskell's 与Haskell相当(好吧,差不多,见下文)

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

In algo.monads the <- disappears, because effectively it is implied on every line. algo.monads<-消失,因为它实际上隐含在每一行上。

About the "almost" and the _ above: _ actually isn't magic in Clojure and it will be bound to the value returned by set-state , but it's idiomatic to use this symbol as the name of locals one doesn't care about. 关于“几乎”和_上面: _在Clojure中实际上不是魔法,它将被set-state返回的值绑定,但是使用此符号作为本地人的名称并不关心。 Of course in Haskell it would be more usual simply to write put "foo" instead of _ <- put "foo" . 当然在Haskell中,更常见的是简单地写put "foo"而不是_ <- put "foo"

Using algo.monads , we can define an IO monad easily (if unnecesarrily). 使用algo.monads ,我们可以轻松定义IO monad(如果不必要的话)。

In Haskell, the IO monad is type IO a = World -> (a, World) . 在Haskell中,IO monad是type IO a = World -> (a, World) It's handy to think of this as an action - something which takes the world, does something , and returns a value and the world. 将这视为一种行动是很方便的 - 这种行为需要世界, 做某事 ,并回报价值和世界。

Using a vector instead of a tuple, this means that, in Clojure, an IO action (a monadic value of the IO monad) looks something like this: 使用向量而不是元组,这意味着,在Clojure中,IO操作(IO monad的monadic值)看起来像这样:

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

To do something interesting, we need a couple of actions: get-char and put-char . 要做一些有趣的事情,我们需要一些动作: get-charput-char

get-char is an action which takes in the world, reads a char, and returns that char as its value alongside the world: get-char是一个接受世界的行为,读取一个char,并将该char作为其值与世界一起返回:

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

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

put-char takes a character and creates an action which, given a world, prints the character and returns some (inconsequential) value: put-char接受一个角色并创建一个动作,给定一个世界,打印角色并返回一些(无关紧要的)值:

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

Note that, to make an action happen, we have to supply a world. 请注意,为了实现行动,我们必须提供一个世界。 For instance, (put-char \\a) will return an action; 例如, (put-char \\a)将返回一个动作; ((put-char \\a) :world) will invoke that action, printing a and returning [nil :world] . ((put-char \\a) :world)将调用该操作,打印a并返回[nil :world]

Composing these actions is potentially a very messy process. 编写这些操作可能是一个非常混乱的过程。 If, for example, you wanted to get a character, then print it, you'd have to call get-char , unpack its character and world, create an action for that character with put-char , then pass the world to that action. 例如,如果您想要获得一个角色,然后打印它,您必须调用get-char ,解包其角色和世界,使用put-char为该角色创建一个动作,然后将世界传递给该动作。

On the other hand, if we define a monad, we get domonad (the equivalent to Haskell's do ) for free. 另一方面,如果我们定义一个monad,我们可以domonad获得domonad (相当于Haskell的do )。 This syntactic sugar alleviates the unpacking/packing boilerplate. 这种语法糖减轻了拆包/包装样板。 We just need a few functions: m-result and m-bind ( m-zero and m-plus are also handy, but not necessary). 我们只需要一些函数: m-resultm-bindm-zerom-plus也很方便,但不是必需的)。

m-result ( return in Haskell) takes a value and wraps it up as an action: m-result (在Haskell中return )获取一个值并将其包装为一个动作:

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

m-bind ( >>= in Haskell) takes an action and a function which takes a regular value to produce an action, "unwraps" the value by invoking the action, and applies the function to it. m-bind>>=在Haskell中)接受一个动作和一个带有常规值的函数来产生一个动作,通过调用动作“展开”该值,并将该函数应用于它。 With the IO monad, that looks like this: 使用IO monad,看起来像这样:

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

So, using algo.monads , we can define io-m as follows: 因此,使用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))))])

Now that we've got the primitive IO actions and a means of composing them, we can create more interesting ones. 现在我们已经有了原始的IO动作和组合它们的方法,我们可以创建更有趣的动作。 Note that Haskell's unpacking operator ( <- ) is implicit and the result is automatically wrapped with m-result so we don't use Haskell's return statement to terminate the expressions: 请注意,Haskell的解包运算符( <- )是隐式的,结果会自动用m-result包装,因此我们不使用Haskell的return语句来终止表达式:

(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)))

Finally, we can write a program in terms of these IO actions: 最后,我们可以根据这些IO操作编写程序:

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

(run-program :world)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM