oops, gen-plan was missing a helper function:

(defn- with-bind [id expr psym body]
  `(fn [~psym]
     (let [[~id ~psym] ( ~expr ~psym)]
       (assert ~psym "Nil plan")
       ~body)))



On Mon, Apr 8, 2013 at 9:32 AM, Timothy Baldridge <tbaldri...@gmail.com>wrote:

> I have a love/hate relationship with monads. I think their use in Clojure
> programming is much more limited than most would like to admit.
>
> However, I have found a very nice use for them: in my case, I'm attempting
> to insert a very complex AST into Datomic. I'd like all my data to go into
> Datomic as one large transaction (a vector of hashmaps). My first attempt
> at this code looked like this:
>
> Assume my data is:
>
> {:type :+
>  :arg0 {:type :const
>           :value 1}
>  :arg1 {:type :const
>           :value 2}
>
>
> Insert for the + node:
>
> (let [[arg0-id with-arg0] (insert-node (:arg0 this) plan)
>       [arg1-id with-arg1] (insert-node (:arg1 this) with-arg1)
>       [this-id with-this] (assert-entity {:arg0 arg0-id :arg1 arg1-od})]
>   [this-id with-this])
>
> So basically every single function has to return the last inserted id as
> well as a db tx plan that contains all items that need to be inserted. Not
> only is this code ugly, but I found it very error prone. Sometimes I would
> pass the wrong plan name in, and things would break. So I looked at this
> and said "why not use the state monad". So now insert-node looks like this:
>
> (insert-node [ent]
>   (fn [plan]
>      ..... do stuff .....
>      [ent plan]))
>
>
> I created a monad binding function called "gen-plan":
>
> (defmacro gen-plan [binds id-expr]
>   (let [binds (partition 2 binds)
>         psym (gensym "plan_")
>         f (reduce
>            (fn [acc [id expr]]
>              `(~(with-bind id expr psym acc)
>                ~psym))
>            `[~id-expr ~psym]
>            (reverse binds))]
>     `(fn [~psym]
>        ~f)))
>
> And our example above looks like this:
>
> (gen-plan
>   [arg0-id (insert-node (:arg0 this))
>    arg1-id (insert-node (:arg1 this))
>    this-id (assert-entity {:arg0 arg0-id :arg1 arg1-id})]
>   this-id)
>
> Notice how the state monad makes the plan implicit.
>
> And now I can write super complex functions like this, without drowning in
> the code. Notice how this block (which generates a tx for writing a SSA
> style if expression to Datomic) is clear from any mentions of explicit
> state, but yet is remains completely functional.
>
> (gen-plan
>      [fnc (get-in-plan [:state :fn])
>
>       test-id (write-ssa test)
>       test-block (get-block)
>
>       pre-then-block (add-block fnc)
>       _ (set-block pre-then-block)
>       then-val (write-ssa then)
>       post-then-block (get-block)
>       then-terminated? (terminated? post-then-block)
>
>       pre-else-block (add-block fnc)
>       _ (set-block pre-else-block)
>       else-val (write-ssa else)
>       post-else-block (get-block)
>       else-terminated? (terminated? post-else-block)
>
>       merge-block (add-block fnc)
>       _ (set-block merge-block)
>       phi-val (add-phi)
>
>       _ (set-block test-block)
>       br-id (terminate-block :inst.type/br test-id pre-then-block
> pre-else-block)
>
>       _ (if then-terminated?
>           (no-op)
>           (gen-plan
>            [_ (set-block post-then-block)
>             _ (terminate-block :inst.type/jmp merge-block)
>             _ (add-to-phi phi-val post-then-block then-val)]
>            nil))
>
>       _ (if else-terminated?
>           (no-op)
>           (gen-plan
>            [_ (set-block post-else-block)
>             _ (terminate-block :inst.type/jmp merge-block)
>             _ (add-to-phi phi-val post-else-block else-val)]
>            nil))
>
>       _ (set-block merge-block)]
>      phi-val)
>
>
>
> So I look at this way, I see monads as a purely academic exercise 90% of
> the time. To try to go "whole hog" and apply them to every problem at hand
> is just nonsense. However, there are times (like in this example) where
> monads end up being the perfect tool for the job at hand. So I say, instead
> of looking at a problem and saying "what monad is this?" Instead, look at
> ugly code and say "hrm...I wonder if programming method X could make this
> cleaner". If that method is a monad, awesome!
>
> Timothy
>
>
>
> On Mon, Apr 8, 2013 at 8:56 AM, Carlos Galdino 
> <carloshsgald...@gmail.com>wrote:
>
>> Hi,
>>
>> I've been reading about monads for the past couple of weeks and I think I
>> got the idea, but I still don't know when to use it since I don't have
>> enough experience with functional programming.
>>
>> So, I'd like to ask you guys if you can point me to some examples of
>> Monads usage "in the wild". Because I've seen a lot of simple examples but
>> not a real one, used in a library, etc.
>>
>> Does anyone know a good example of real world usage?
>>
>> Thanks in advance.
>>
>> --
>> --
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clojure@googlegroups.com
>> Note that posts from new members are moderated - please be patient with
>> your first post.
>> To unsubscribe from this group, send email to
>> clojure+unsubscr...@googlegroups.com
>> For more options, visit this group at
>> http://groups.google.com/group/clojure?hl=en
>> ---
>> You received this message because you are subscribed to the Google Groups
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to clojure+unsubscr...@googlegroups.com.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
>>
>
>
>
> --
> “One of the main causes of the fall of the Roman Empire was that–lacking
> zero–they had no way to indicate successful termination of their C
> programs.”
> (Robert Firth)
>



-- 
“One of the main causes of the fall of the Roman Empire was that–lacking
zero–they had no way to indicate successful termination of their C
programs.”
(Robert Firth)

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to