Monads usage
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.
Re: Monads usage
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.comwrote: 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
Re: Monads usage
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.comwrote: 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.comwrote: 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
Re: Monads usage
Last week I released a project with a monadic translator that needed to: - work on sequences of expressions, arbitrarily nested - generate Clojure code or stop and report the first error - maintain a symbol table with easy access but not global state The relevant code is here: https://github.com/blancas/eisen/blob/master/src/main/clojure/blancas/eisen/trans.clj The code uses a StateT monad transformer and Either monad wrapped in five API functions: -left, -right, get-se, modify-se, run-se. Functions report errors with (-left); function (-right) wraps good values. The macro (monad) corresponds to Haskell's do; it chains monadic values or short-circuits and propagates errors. (defn trans-binop Translates the application of a binary operator. [ast] (monad [x (trans-expr (:left ast)) y (trans-expr (:right ast))] (let [f (- ast :op :value str symbol)] (-right `(~f ~x ~y) The symbol table is available through get-se and modify-se. A typical use case is to enter declared names, translate the expressions, then remove the names from the symbol table. (defn trans-let Translates a let expression. [{:keys [decls exprs]}] (let [env (map (comp symbol :name) decls)] (monad [_ (modify-se into env) decls (trans-bindings decls) exprs (trans-exprs exprs) _ (modify-se difference env)] (-right `(let [~@(apply concat decls)] ~@exprs) To translate expressions in a sequence it uses the generic function (seqm); function (run-se) evaluates the resulting sequenced monads using an initial state predefs. The result from (run-se) feeds (either) which evaluates to the first form on -left and to the second on -right. (let [job (monad [v (seqm (map eval-ast coll))] (-right v))] (either [res (run-se job predefs)] {:ok false :error res} {:ok true :decls (map first res) :value (- res last second)}))) On Monday, April 8, 2013 7:56:35 AM UTC-7, Carlos Galdino 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.