3

I realize my precursory understanding on Monads is severely lacking in detail considering my knowledge comes mostly from Douglas Crockford's Monads and Gonads talk and complicated with my sevear handicap with Haskell (which looks like a bunch of non-alpha numeric characters mushed between disjointed English words to my tragically disadvantaged brain).

With that being said, I'd like to ask about programming practices concerning Monads and how they could be implemented in JavaScript. I'm prefacing this because I recognize that the very nature of the language can drastically affect how one perceives a concept and that because of my background in JavaScript this question could be inappropriate if it were based in a purely functional language like Haskell.

Often times while designing an interface or coding an object I will find myself implementing a form of chaining which mutates the encapsulated data. I prefer this style over more declarative forms like passing in a multi-lined object literal.

function Declarative(options) {
  this.options = options;
}
Declarative.prototype.compute = function() { ... }
var x = new Declarative({
  foo: 'foo',
  bar: 'bar'
});
x.compute();

Verses:

function Chained() { }
Chained.prototype.withFoo = function(v) {
  this.foo = v;
  return this;
};
Chained.prototype.withBar = function(v) {
  this.bar = v;
  return this;
};
Chained.prototype.compute = function() { ... }
var x = new Chained()
  .withFoo('foo')
  .withBar('bar')
  .compute();

Both these examples (aesthetics aside) raise a few hairs on my back because a small voice mockingly squeals "Mutability much?" I begin thinking this might be a good time to conciser a Monad pattern. What I mean by that is each method would in short return a new object of the same type. Then adding composition functions on it like map, bind, etc. could off a world of potential like I get with Promises and other Monad type. (Obviously taking care to follow the three Monadic laws when implemented).

Finally, my other half of the brain starts chiming in with "If all you have is a hammer, everything looks like a nail." Sigh. That is when my productivity and creativity crash here on SE curious and confused.

While I continue my research for understanding (perhaps gaining enough courage to contemplate Haskell) I ask: When does the idea of Monads (that being composability of functions on objects (ie types) along with immutability) become a good idea to be cultivated and patterned? And, when is it nothing more then an over utilized hammer?

(Concepts and learning opportunities welcomed, example code helpful)

18
  • 9
    @MasonWheeler: You see monads everywhere because being a monad is a property that lots of types happen to have (yes, including types that support mutable state); Haskell is (relatively) unique in allowing you to talk about that pattern. (In fact, most (or at least many) uses of monads in Haskell aren't about mutable state at all; I'm likelier to use Maybe (failure), (->) a (functions/global environment), or [] (lists/nondeterminism). Even if Haskell was impure (something that would, IMHO, "cripple" it even more), I think it would still be more expressive with monads than without.) Jul 15, 2014 at 3:24
  • 2
    @MasonWheeler If mutability is essential to non-trivial programs, how do non-trivial programs get written in pure languages?
    – Doval
    Jul 16, 2014 at 13:37
  • 3
    @MasonWheeler You've got it backwards - if anything, the monad lets you pretend pure functions are impure. There's no cheating and no tricks. There's no "fake pure functions" involved, nor "fake entire world". The functions are pure, and they do return a modified state. How else would they work? If you can't mutate existing values, returning modified copies is the only productive thing you can do. All the monad does is factor out the boilerplate of passing the output of the previous function to the next one in the chain. I guess DRY is silly?
    – Doval
    Jul 16, 2014 at 15:56
  • 2
    @MasonWheeler: You need not pass the entire world around as a variable. Think about state as a stream of snapshots. The real world does not really exist: what you can observe is just a sequence of snapshots taken at different points in time. Each element of the sequence (each snapshot) is immutable.
    – Giorgio
    Jul 16, 2014 at 15:58
  • 1
    @MasonWheeler 1) Mutation is not IO. You're bringing up two completely different subjects and swapping them interchangeably. However, even the IO monad is pure, because a value of type IO doesn't actually do anything unless you return it from main. putStr "Hello" does nothing. main = putStr "Hello" does something. 2) That quote is a fallacy, because pure functional programming is still a relatively new field. You've also missed his point, which is that characterizing functional programming as taking away mutation misses the point - the point of FP is improved modularity.
    – Doval
    Jul 16, 2014 at 16:24

1 Answer 1

7

It's hard to tell exactly what you're asking, so this answer is aimed at providing more context about monads. Some preliminaries about monads:

  • Crockford's monad talk is a poor source for learning about monads (I hate to say this because he's so much smarter than me, and therefore probably understand monads better than I do, but IMHO it gives an inaccurate portrayal of monads). So are lots of other monad "tutorials". If you don't put in hard work making your way through real examples, you'll probably never understand monads.

  • monads are neither special nor magical. They can be implemented in many (most? all?) programming languages without special support from the language/runtime/compiler/interpreter. They aren't the end-all, be-all of functional programming. But for some reason, they're what we hear about all the time.

  • monads aren't unique. There are many other related datatypes which are useful in their own right.

  • there are alternatives to monads, depending on what you want/need to do. Search for "effects systems" and "algebraic effects".

  • you can often get most/much of the value of monads without explicitly using them. Just implement the combinators that you need and are useful and use them appropriately. Recognize patterns in your code and factor them out, possibly capturing them as combinators. If some of those combinators happen to match the requirements for unit and join, so be it. That doesn't mean you have to think about what you're doing in terms of monads. (But on the other hand, if you do use monads, you benefit from the numerous combinators already in existence for monads, as well as from writing super-generic code).

When is it inappropriate to use monads?

  • if you have to warp your datatype or the monad laws in order to "make" a monad, you probably shouldn't do that, and should instead ask yourself why you're doing that

  • if your code doesn't benefit from using a monad, or if it actually gets worse (whether because it's longer, or harder to understand, or harder to test, or harder to maintain, ... etc.), you're probably using monads inappropriately

7
  • 2
    They aren't the end-all, be-all of functional programming. But for some reason, they're what we hear about all the time. They're only practical with type inference and lambda expressions though. Using monads would be insane in Java 7. And I think that's why you hear about them so much - they're an example of what you simply can't do in languages where functions are second-class citizens. And as @AntalS-Z pointed out lots of types are monads, so it's an entire class of things you can't do rather than one obscure trick.
    – Doval
    Jul 16, 2014 at 14:44
  • 1
    @Doval "They're only practical with type inference" -- nonsense. "[...] entire class of things you can't do rather than one obscure trick" -- and again, you're elevating monads at the expense of other useful datatypes and patterns.
    – user39685
    Jul 21, 2014 at 14:30
  • 1
    nonsense I expect a better rebuttal than this. The type signatures of high-order functions can get complicated quickly, even if their usage is intuitive. As an example, see the section "Typing" on this Standard ML module. It gets even worse in C# and Java where functions are actually interfaces; curried functions become nested interfaces of the form Func<T1, Func<T2, Func<T3, T4>>> and god help you if you have some Optionals, Eithers or Tuples mixed in there. The type signatures can quickly become of equal or greater size than the lambda itself.
    – Doval
    Jul 21, 2014 at 14:43
  • 1
    and again, you're elevating monads at the expense of other useful datatypes and patterns. No, I'm saying they're more interesting than some other useful datatypes and patterns. An imperative programmer won't be impressed with "things that can be mapped over" (i.e. a functor). Although he can't compose them with ease, he can already write functions to map from one type to another. Moreover, he'd rather write a loop than mess with map. Defining your own control structures, on the other hand, is something that's both compelling and completely out of an imperative language's reach.
    – Doval
    Jul 21, 2014 at 14:50
  • 1
    @Doval in general, your posts seem to be pure regurgitation of your (or someone else's) biases without any attempt at critical thought. You make statements such as "Defining your own control structures, on the other hand, is something that's both compelling and completely out of an imperative language's reach" and "They're only practical with type inference and lambda expressions though" without any attempt at supporting them or providing some justification that they are correct. This essentially precludes any sort of constructive dialog.
    – user39685
    Aug 26, 2014 at 20:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.