Clojure's impurity doesn't mean that we wouldn't like to avoid
side-effecty ways of doing stuff. Monads can help you there, among
other things.
The monad idea captures a very high-level abstraction, giving you the
stuff that is implemented on that idea for free. For example, if you
want an idea of `map' for your data structure, you'll get m-fmap if you
write a monad instance.
Some of this stuff seeps into more commonplace operators. `for' is
essentially a sugary and faster `domonad sequence-m'. Likewise, the
terribly useful `-?>' from core.incubator is much like `((with-monad
maybe-m (m-chain [...])) start)'.
That said, Haskell-style evaluation and type inference does make them
more useful and easier to use.
> I would like to know do you use Monads in your real clojure
> applications,
Aside from `-?>' and `for', I've implemented one custom monad instance
for a production Clojure application. No doubt some of the stuff I've
done with dynamic vars would have been cleaner with the reader monad;
https://github.com/straszheimjeffrey/The-Kiln will, I think, be a good
use case for the reader monad when it's ready.
--
Stephen Compall
^aCollection allSatisfy: [:each|aCondition]: less is better
> On the net I read that "Impure functional programming doesn't really
> need monads."
> and "It appears that in the presence of mutable state, a lot of the
> advantages of monads become moot."
Monads are an abstraction mechanism, so you never need them. You can
always use the lower-level techniques in terms of which monads are
implemented.
The only language that has made monads nearly inevitable is Haskell,
because its standard library is based on monads. But even in Haskell,
monads can be avoided, at the cost of rewriting stuff that is already
in the standard library.
As with all abstractions, the real question is not whether you need
them, but whether their use improves your programs. This depends as
much on the programmer as on the problem, so there is not clear
answer. As a rule of thumb, I'd say that you should consider using
monads if your application
1) can profit from more than one of them, or
2) can profit from the generic monad operators.
I probably use monad more than the average programme in my own code,
but that's also because I happen to be familiar with them. I could
very well live with fewer monads in my code. But once you know monads,
they appear magically everywhere you look ;-)
Konrad.
I used monads in two projects.
* The last rewrite of ClojureQL before v1.0 used a state monad to keep track of various things during query creation.
* ClojureCheck also uses a monad approach to create and combine generators for test data.
* Dave Ray and I tried a monad style in the async branch of seesaw.
Both were custom monad implementations. Both work(ed) reasonably well. However things have a relatively high strangeness factor. Since the execution of things is deferred till someone actually runs the monad pipeline, you can't use your usual try/catch construct to take care of problems. Everything has to be constrained to your monadic function. So you need to have some way to error out of your monadic pipeline. This leads you to monad transformers and complicates things even more.
In Clojure I haven't seen a use were other approaches weren't just as feasible or were monads would have simplified things. They are a legal approach, but I would judge on a case-by-base basis.
Sincerely
Meikel
This +1.
You need to be more specific about what you mean when you say code
"uses monads". In one sense, any code which uses a 'for' sequence
comprehension is using a monad, because it satisfies all of the
properties of a monad. In another sense, only code which contains and
names specific things as monads, and uses general operators which
apply to all monads, is using them.
I've been using monads a lot recently with Overtone -- by which I mean
I've been using (state-t cont-m), the continuation monad transformed
to add state. I'm using it because: a) I want to simulate state
representing the current beat number, and b) I want to schedule future
events using apply-at; so rather than returning from a function to
continue a melody, I call apply-at and pass the current continuation
to it.
As an example of what I've achieved, see this gist:
https://gist.github.com/1441831
Using clojure.algo.monads, I have created a basic DSL which allows me
to say (wait 1) to pause for one beat, and (at-current-beat (foo)) to
schedule event (foo) to happen on the current beat. These commands are
relative to the current time, but using monads I can transform them
into commands at an absolute time using overtone's built-in 'at and
'apply-at macros, and to automagically handle the scheduling.
This is a work in progress; I'm planning a fuller write-up of what I'm
doing which I will send to the overtone list once I've ironed out the
wrinkles.
Phil
In terms of higher-level DSLs constructed out of monads, the most
useful monadic frameworks I've seen are monads for parsing, and monads
for representing probability distributions. If I needed to do one of
those things in Clojure, I'd look closely at monad options. But since
I haven't needed to do those things, and the common uses for monads
are already covered, I haven't found a need to do any monadic style
programming in my own code.
--Mark
If you work with self-imposed restrictions that make you call functions
with the same parameters twice in short order, you should reconsider
those restrictions.
If all the information you need from a validator is true/false, you can
just call a model function. One that is written under the assumption
that a certain requirement is met, before it is called.
If the validator returns nil or some concrete piece of imformation, you
just need to call it, store the result. Then you may call a model
function with the result (if it is non-nil).
Moustache makes that very straightforward:
(defn integer [s]
"returns nil if s does not represent an integer
(try
(Integer/parseInt s)
(catch Exception e)))
(app ["order" [id integer]] my-handler) ; for "/order/134" @id@ will
be bind to 134 (not "134"), this route will not match "/order/abc".
Example taken from https://github.com/cgrand/moustache
--
Thorsten Wilms
thorwil's design for free software:
http://thorwil.wordpress.com/
Am 04.01.2012 um 10:12 schrieb Thorsten Wilms:
>> (defn handler [{:keys [params] :as req}]
>> (if (person/valid? params)
>> {:status 200 :body (json/generate-string (person/create params))}
>> {:status 400}))
Or you let the create function return nil on invalid params.
(defn handler
[{:keys [params] :as req}]
(if-let [p (person/create params)]
{:status 200 :body (json/generate-string p)}
{:status 400}))
Sincerely
Meikel