Clojure 自定义`do`/`let`语法(不带宏)的想法?

Clojure 自定义`do`/`let`语法(不带宏)的想法?,clojure,macros,monads,state-monad,Clojure,Macros,Monads,State Monad,我刚刚构建了一个状态monad“object”,希望能够将步骤绑定到变量,并将后续计算连接起来,就像使用bind/>>=一样,比如haskell如何使用monad,以及clojure的各种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

我刚刚构建了一个状态monad“object”,希望能够将步骤绑定到变量,并将后续计算连接起来,就像使用
bind
/
>>=
一样,比如haskell如何使用monad,以及clojure的各种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的线程是宏),例如:

我可以查看clojure中现有的monad库,了解如何使用宏实现这一点,这很好。我很好奇,如果不使用宏,这是如何实现的,即使只是一个实验如何实现的练习


我知道clojure中的
绑定
s,但没有使用它们。如何使用这个或其他适当的构造来实现
do
ish语法?

没有宏,这两种样式都不可能实现。宏是用于在任何lisp中引入新语法结构的构造,因此,如果您想要新语法,宏是一种可行的方法。

宏唯一的额外用途是与作为抽象方法的函数相比,延迟计算。如果要将函数用于所有需要更改约定的内容,以便用户将函数作为参数传递,而宏可能会延迟表达式的计算。下面是重新标记
if
的示例:

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

(if* true
     (fn [] 1)
     (fn [] 2)) ; ==> 1
如果你能处理更多的样板文件,你永远不需要宏,但从长远来看,我很感激我所有的论点都不必是废话


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

为什么要排除宏?@Carcigenicate问得好,看看我是否可以,以及它的外观如何。它不必看起来完全像
do
语法,只是比一百万个嵌套绑定更方便,不需要使用新的宏,但我认为必须有某种方法来简化事情,而不需要宏。比如说,我有一种语言,宏很笨拙,甚至不可能,我想简化语法。我怎么能?即使在表达式列表上循环也是可能的,我只需要弄清楚如何…宏正是添加到语言中用于执行特定操作的功能。你会问,“我怎样才能在不使用油门的情况下让我的车加速?”也许有一些解决方案,比如把你的车挂在空档,然后把它滚下陡坡,但这些真的是你想要的解决方案吗?我同意,并且理解宏的重要性,以及除了我不想使用宏之外,它们如何与我的用例相匹配。我不一定需要一个新的语法结构,只是为了减少锅炉板,为了学习,不使用宏。我刚在JS中找到这个,现在我正在消化它
(defn if* [p a c] 
  (if p (a) (c)))

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