自定义`do` /`let`语法(没有宏)的想法?

时间:2016-07-23 22:25:28

标签: clojure macros monads state-monad

我刚刚构建了一个状态monad" object",并且希望能够将步骤绑定到变量,并将连续计算链接起来,就像使用bind / >>=一样就像haskell如何与monad一起工作,以及clojure的各种monad libs如何工作。这是我的monad:

(def state-m
  (let [pure (fn [x] (fn [s] [x s]))
        bind (fn [mv mf] (fn [s] ; mv :: s -> (a, s) || mf :: a -> (s -> (b, s))
                           (let [[a s'] (mv s)]
                             ((mf a) s'))))

        then     (fn [mv mf] (bind mv (fn [_] mf))) ; eg `>>`
        get-s    (fn [s] [s s])
        put-s    (fn [s] (fn [_] [nil s]))
        update-s (fn [f] (fn [s] [nil (f s)]))] ; f :: s->s

    {:pure     pure
     :bind     bind
     :then     then
     :get-s    get-s
     :put-s    put-s
     :update-s update-s}))

以下是使用它的示例:

(let [pure     (:pure state-m)
      >>=      (:bind state-m)
      >>       (:then state-m)
      get-s    (:get-s state-m)
      put-s    (:put-s state-m)
      update-s (:update-s state-m)]
  (and (= [:v :s] ((pure :v) :s))
       (= [5 :state] ((>>= (pure 3)
                           (fn [x] (pure (+ 2 x))))
                      :state))
       (= [3 3] ((>>= get-s (fn [n] (pure n))) 3))
       (= [4 5] ((>>= get-s (fn [n] (>> (put-s (+ n 1))
                                        (pure n)))) 4))
       (= [4 8] ((>>= get-s (fn [n] (>> (update-s (partial * 2))
                                        (pure n)))) 4))))

它有点冗长。我现在不介意,但我 希望能够使用do语法,例如:

(my-do [a (get-s)
        _ (put-s 33)
        b (* 2 a)
        _ (put-s 44)
        c (* a b)]
  c)

甚至还有一些功能(虽然我知道clj的线程是宏),例如:

(my-> (pure 1)
      (>>= (fn [a] ...))
      (>>= (fn [b] ...))
      (>> (* a b))) ; notice non-local bindings `a` and `b` are available here

我可以在clojure中查看现有的monad库,了解如何使用宏完成此操作,这很好。我很好奇如何在不使用宏的情况下实现这一目标,即使只是练习如何进行实验。

我知道binding在clojure中,但还没有使用它们。我怎么能用这个或其他适当的结构来实现do ish语法?

2 个答案:

答案 0 :(得分:2)

没有宏,这两种风格都不可能。宏是 结构,用于在任何lisp中引入新的语法结构,因此如果你想要一个新的语法宏,那么就可以了。

答案 1 :(得分:0)

与作为抽象方法的函数相比,宏的唯一目的是延迟评估。如果您要使用函数来更改合约所需的所有内容,以便用户将函数作为参数传递,其中宏将延迟表达式的求值。以下是重新制作if的示例:

(defn if* [p a c] 
  (if p (a) (c)))

(if* true
     (fn [] 1)
     (fn [] 2)) ; ==> 1

如果你可以应付更多样板,你永远不需要宏,但从长远来看,我感激我所有的论点都不一定是thunk。

经验法则是尝试使用函数进行抽象,但是如果没有太多的模糊使用宏来将更简单的语法转换为样板文件是不可能的,那么使用它们的语言最重要的特征就是它。你不像Clojure那样依赖于Java语言的新版本,因为大多数功能都可以使用更多样板和宏来简化语法,使其与新语言功能无法区分。