<< Back to previous view

[ALGOM-11] Add the reader monad Created: 05/Feb/14  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Leonardo Borges Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: enhancement, patch

Attachments: Text File 0001-Add-the-reader-monad-and-associated-tests.patch    
Patch: Code and Test

 Description   

This patch adds the reader monad and supporting functions. It includes tests.

I've been using it with my own patched version of algo.monads. It would be nice to be able to
rely on the original project for the dependency as opposed to depending on a local built version.



 Comments   
Comment by Konrad Hinsen [ 05/Feb/14 5:11 AM ]

Done: http://github.com/clojure/algo.monads/commit/3055d0dc19d0e497556b5d6dc86c77a112d2dcd2





[ALGOM-7] maybe-m breaks the monad laws. Created: 15/Sep/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Seth J. Gold Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None


 Description   

One of the monad laws is that (m-bind (m-result v) f) should be the same as (f v). However, this is not the case in maybe-m:

user=> (with-monad maybe-m (m-bind (m-result nil) nil?))
nil

user=> (nil? nil)
true

The crux of the problem is that in algo.monad's maybe-m, there is no way to wrap a nil in a Just-like container.



 Comments   
Comment by Seth J. Gold [ 15/Sep/12 3:27 PM ]

Just realized that that demonstration doesn't actually work, because the function passed to m-bind is supposed to return a monadic value. Here's a better one:

user=> (with-monad maybe-m (m-bind (m-result nil) (comp m-result nil?)))
nil
user=> (with-monad maybe-m ((comp m-result nil?) nil))
true

Comment by Bruno Tavares [ 07/Jun/13 8:51 PM ]

I don't think this would be a valid example.

Here is an example in Haskell
Nothing >>= \x -> Just(x / 0) – => Nothing

; Clojure example
(with-monad maybe-m (m-bind m-zero #(/ % 0))) ; => nil
(with-monad maybe-m (= m-zero nil)) ; => true

;; Both examples will fail
(with-monad maybe-m (#(/ % 0) m-zero))
(with-monad maybe-m ((comp m-result #(/ % 0)) m-zero))

Those errors would not be possible to happen with Haskell's type system.

The following snippet shows it evaluates properly the monadic bind law for valid operations

(let [value 1
operation #(+ % 1)]
(with-monad maybe-m
(=
(m-bind (m-result value) operation)
(operation value))))

Comment by Konrad Hinsen [ 05/Feb/14 5:20 AM ]

The Clojure implementation of maybe-m is based on the convention that nil is not a value, but a marker for "not a value". That's a very common convention in Clojure, so from a practical point of view this is the most useful way to implement maybe-m.

There are other options, and everyone is invited to implement their personal preference and use it in place of maybe-m. For example, a monadic value could be either an empty list or a list containing a single value.

Note that the Haskell approach of setting up a special type for the maybe-m container is not an option in a dynamically typed language. Whatever you choose to represent "no value" can be created outside of the monad and thus be used to violate the monad laws. It must be understood that the monad laws are valid only subject to the conventions of the specific monad.





[ALGOM-2] Adding support for both :if-:then-:else and :cond statements on domacro implementations Created: 21/Jan/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Roman Gonzalez Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None
Environment:

Mac OS X Lion


Attachments: File monadic-cond.diff     Text File monadic-cond.patch    
Patch: Code and Test

 Description   

This patch contains the same features as

http://dev.clojure.org/jira/browse/ALGOM-1

plus a new one, the ability to add :cond statements using the domonad macro.

More info can be found on the commit history of the given patch.



 Comments   
Comment by Roman Gonzalez [ 24/Jan/12 11:15 AM ]

This is a new version of the patch that handles the conflicts with whitespaces vs tabs.

Comment by Konrad Hinsen [ 05/Feb/14 5:23 AM ]

This has been added long ago: https://github.com/clojure/algo.monads/commit/d9b13dcc57be0d11934441c2dc7a9b287385f435





[ALGOM-10] :when clause in domonad relies on optional m-zero monad operation Created: 30/May/13  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Glenn Vanderburg Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None


 Description   

In add-monad-step, the :when clause is implemented by this line:

(identical? bform :when)  `(if ~expr ~mexpr ~'m-zero)

which makes sense, except that m-zero is an optional operation for monads.
If :when is used with a monad that does not provide an m-zero operation,
the result (in cases where the condition evaluates to false) is an incorrect monadic value,
and strange, difficult-to-understand errors result.

I don't know that it's possible to make this work reasonably for all monads,
but at the very least the limitation should be documented, and ideally the use of :when
with a monad that does not define m-zero should issue a warning with a recommendation that
{:if :then :else} should be used instead.d



 Comments   
Comment by Glenn Vanderburg [ 03/Jun/13 8:23 AM ]

After more thinking, I think the best behavior would be to evaluate to m-zero if it's defined, and (m-result nil) otherwise. The behavior of :when is too useful with the state monad, and too cumbersome to duplicate with :if :then :else, to simply document the issue. The standard Clojure when macro evaluates to nil if the condition is false.

Comment by Konrad Hinsen [ 05/Feb/14 9:50 AM ]

The docstring for domonad clearly says that :when requires a definition of m-zero. I am not in favor of removing this rule, which has a clear role in the monad framework. "No value" is not in general the same as "a value of nil", so substituting (m-result nil) as a default for m-zero doesn't look like a good idea.

A better error message would indeed be desirable but I see no obvious implementation, because of the possibility of generic domonad forms for which the monad is specified later. As a workaround, I have replaced the ::undefined value by more descriptive symbols that convey a useful message to the reader:
https://github.com/clojure/algo.monads/commit/8b04e7b0bc9e70d27a497c4ce3bdb5cad37d7a1d





[ALGOM-1] Add conditionals stratements to domonad forms Created: 06/Jan/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Roman Gonzalez Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None
Environment:

The extension was developed in Mac OS X Lion


Attachments: File monadic-if-else.diff    
Patch: Code

 Description   

I implemented a patch that would allow us to do conditionals on domonad macros

Example:

(domonad monad-m
[a (action 1)
:if (= a 3)
:then [
result (action c)
]
:else [
result (action d)
]]
result)

Conditional blocks can be nested.

One gotcha one has to be aware of is: you may use bindings from a conditional branch in the m-result sexp as long as that branch gets called, given that one doesn't know for sure which branch is going to be called, you should set the same bindings on both branches.

The patch code is on: github.com/roman/algo.monads

I didn't do a pull request given that you need to this first.

NOTE: I've added this feature in some other projects I've been working on (parser combinators) and is working perfectly so far, there is tests in the patch but I wanted to give you that safe net as well.



 Comments   
Comment by Roman Gonzalez [ 10/Jan/12 12:28 AM ]

Adding support for monadic conditionals on the domonad macro

Comment by Konrad Hinsen [ 05/Feb/14 5:22 AM ]

This has been accepted long ago: https://github.com/clojure/algo.monads/commit/d9b13dcc57be0d11934441c2dc7a9b287385f435





[ALGOM-3] Bug in writer-monad-protocol for lists Created: 18/Apr/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Greg Chapman Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None
Environment:

Clojure 1.4, Java 1.7, Windows 7


Attachments: File monads.clj.diff    

 Description   

The writer-monad-protocol for lists uses concat in writer-m-combine, the result of which has type LazySeq. However, LazySeq does not have an implementation in writer-monad-protocol (the first example, using vectors, shows expected output):

{{
user=> (use 'clojure.algo.monads)
nil
user=> (domonad (writer-m []) [_ (domonad [_ (write "foo")] nil) _ (write "bar")] 1)
[1 ["foo" "bar"]]
user=> (domonad (writer-m ()) [_ (domonad [_ (write "foo")] nil) _ (write "bar")] 1)
IllegalArgumentException No implementation of method: :writer-m-combine of protocol: #'clojure.algo.monads/writer-monad-
protocol found for class: clojure.lang.LazySeq clojure.core/-cache-protocol-fn (core_deftype.clj:527)
}}

I suggest changing the protocol extension to ISeq as in the attached diff. With that change:

{{
user=> (use 'clojure.algo.monads)
nil
user=> (domonad (writer-m []) [_ (domonad [_ (write "foo")] nil) _ (write "bar")] 1)
[1 ["foo" "bar"]]
user=> (domonad (writer-m ()) [_ (domonad [_ (write "foo")] nil) _ (write "bar")] 1)
[1 ("foo" "bar")]
}}



 Comments   
Comment by Konrad Hinsen [ 05/Feb/14 5:45 AM ]

I fixed this using a somewhat different approach: ensure that the return type of writer-m-combine is a list if it is called with list arguments:
https://github.com/clojure/algo.monads/commit/7679776bbddde7a716b9e077a4f65d1a568fe9c7

I prefer this solution because ISeq is implemented by so many types that a writer-m-combine implementation for this protocol may well cause trouble elsewhere.





[ALGOM-4] algo.monad state-m fetch-val bug and efficiency issue Created: 08/Sep/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Sacha De Vos Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: bug, performance
Environment:

irrelevant



 Description   

;; the bug

(defn fetch-val
"Return a state-monad function that assumes the state to be a map and
returns the value corresponding to the given key. The state is not modified."
[key]
(domonad state-m
[s (fetch-state)]
(key s))) ;; does not work for integer or string keys

;; I propose replacing it with (get s key)

;; the efficiency issue :
;;
;; domonad with monad parameter binds all the monad functions,
;; looking these up in the state-m map on each call
;;
;; solution :

(defn fetch-val
"Return a state-monad function that assumes the state to be a map and
returns the value corresponding to the given key. The state is not modified."
[key]
(fn [s]
[(get s key) s]))

;; - we avoid the monad map lookups
;; - coding style brought up to par with the rest of state-m functions



 Comments   
Comment by Konrad Hinsen [ 05/Feb/14 5:52 AM ]

This was handled long ago: https://github.com/clojure/algo.monads/commit/90909e30965bb59fccc8e527d31f8aaabc9d404b





[ALGOM-6] algo.monads README has no artifacts information Created: 14/Sep/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Andy Fingerhut Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None


 Description   

algo.monads README has no artifacts information and does not conform to the Contrib README standards in any way. Because clojure-dev is a closed group, I am filing it here.

(originally filed by Michael Klishin in project CLJ, but seems to belong better here)



 Comments   
Comment by Konrad Hinsen [ 05/Feb/14 5:55 AM ]

This was handled a year ago: https://github.com/clojure/algo.monads/commit/15a21c7c1e088437f67ced35fa7fc739e3e4f161





[ALGOM-9] Replace calls to deprecated functions Created: 28/Oct/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Andy Fingerhut Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File algom-9-replace-calls-to-deprecated-fns-v1.txt    

 Description   

There are several calls to replicate, which was deprecated in Clojure 1.3. Recommended replacement is repeat, which was added in 1.0.



 Comments   
Comment by Andy Fingerhut [ 28/Oct/12 5:23 PM ]

algom-9-replace-calls-to-deprecated-fns-v1.txt dated Oct 28 2012 replaces calls to replicate with repeat.

Comment by Konrad Hinsen [ 05/Feb/14 6:02 AM ]

Done: https://github.com/clojure/algo.monads/commit/c48d268dc69eb4d7dcabff2d65188d9cd18230e3





[ALGOM-8] Cosmetic change in maybe-m Created: 12/Oct/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Closed
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Trivial
Reporter: Edward Tsech Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File maybe-m-cosmetic.patch    

 Description   

Diff:
;- (if (nil? mv) nil (f mv)))
;+ (when-not (nil? mv) (f mv)))



 Comments   
Comment by Konrad Hinsen [ 05/Feb/14 6:06 AM ]

Done: https://github.com/clojure/algo.monads/commit/c6e64985019bec27d335cfdaab0488fd38a2f3bd





[ALGOM-13] Update links to Jim Duey's tutorial Created: 17/Feb/14  Updated: 21/Feb/14  Resolved: 21/Feb/14

Status: Resolved
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Jim Duey Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: None

Attachments: Text File README.patch    
Patch: Code

 Description   

I provided an updated link to the beginning of the monads tutorial on my blog.



 Comments   
Comment by Konrad Hinsen [ 21/Feb/14 8:12 AM ]

Done: https://github.com/clojure/algo.monads/commit/c7754d6cdf0721cce4a3c89790bc2c0fb83ecaa7





[ALGOM-12] Add new monad tutorial links to README Created: 14/Feb/14  Updated: 21/Feb/14  Resolved: 21/Feb/14

Status: Resolved
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Leonardo Borges Assignee: Konrad Hinsen
Resolution: Completed Votes: 0
Labels: documentation, enhancement

Attachments: Text File 0001-Add-new-monad-tutorial-links.patch    
Patch: Code

 Description   

This patch adds the monad tutorials I wrote a while back.

It was requested by a user in this tweet: https://twitter.com/JakeGoulding/status/434309278329872384

Since he doesn't have a CA on file, I just created the patch myself.



 Comments   
Comment by Konrad Hinsen [ 21/Feb/14 8:15 AM ]

Done: https://github.com/clojure/algo.monads/commit/11429c1c05c2c0aa4d4befd783efa66748a40b1b





[ALGOM-5] continuation monad can easily overflow stack Created: 10/Sep/12  Updated: 05/Feb/14  Resolved: 05/Feb/14

Status: Resolved
Project: algo.monads
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: ben wolfson Assignee: Konrad Hinsen
Resolution: Declined Votes: 0
Labels: None


 Description   

The continuation monad uses tail calls, and because they aren't optimized out it's vulnerable to stack overflows for large computations:

;; there is a function clojure.algo.monads/m-reduce, but it has a
;; wierdly unuseful type.
(defmonadfn fold-m
[f a xs] (reduce (fn [macc x] (m-bind macc #(f % x))) (m-result a) xs))

(defn add [s]
(run-cont
(with-monad cont-m
(call-cc
(fn [k]
(fold-m (fn [a n]
(if (== n 8888) (k 1)
(m-result (+ a n))))
0 s))))))

(add (range 1 10000)) ;; stack overflow

Can be worked around with a trampoline:

(defmonad cont-tramp-m
[m-result m-result-cont
m-bind m-bind-cont])

(defn call-cc-tramp [f]
(fn [c] [(f (fn [v] (fn [_] [c v ::cont]))) c ::cont]))

(defn run-cont-tramp [cont]
(loop [cv (cont identity)]
(if (and (vector? cv)
(= ::cont (nth cv 2)))
(recur ((first cv) (second cv)))
cv)))

(defn add2 [s]
(run-cont-tramp
(with-monad cont-tramp-m
(call-cc-tramp
(fn [k]
(fold-m (fn [a n]
(if (= n 8888)
(k 1)
(m-result (+ a n))))
0 s))))))

(add2 (range 1 1000000)) ;; -> 1



 Comments   
Comment by ben wolfson [ 10/Sep/12 12:01 PM ]

I neglected to include m-result-cont and m-bind-cont for the cont-tramp-m call!

(defn m-result-cont [v] (fn [c] [c v ::cont]))

(defn m-bind-cont [mv f] (fn [c] [mv (fn [v] [(f v) c ::cont ]) ::cont]))

Comment by Konrad Hinsen [ 05/Feb/14 6:14 AM ]

It is true that the continuation monad can easily overflow the stack. I don't see any solution within the constraints imposed by the JVM (no tail call optimization).

A different implementation based on a trampoline is a useful workaround. Unfortunately I cannot use your code because you are not on the list of people who have signed a Contributor Agreement.





Generated at Wed Apr 23 17:39:43 CDT 2014 using JIRA 4.4#649-r158309.