As functional programming languages, such as Scala and F#, are becoming increasingly popular, key functional programming concepts have been receiving renewed attention. In a recent article, Matthew Podwysocki explains how one such concept, monads, can benefit even those not initially interested in functional programming techniques:
Ideas from functional programming aren’t just for guys in lab coats or those at the universities, but for general line of business developers as well. Stepping out of that comfort zone that is C# ... [or some other OO language] and into a more functional language will give you a greater appreciation of different styles and improve your day to day coding.
At the same time, certain features of functional languages are not at first easy to grasp. That may be because many functional programming concepts originated in academia. However, if explained in the proper context and with illustrative use-cases, such initially hard-to-grasp concepts can enhance one’s programming horizon.
One such concept is that of a monad. Borrowed from the category theory branch of mathematics, monads can lead to cleaner, easier-to-understand code, writes Podwysocki in Much Ado About Monads:
Think of it as a basic abstract data type used to represent computations. We can abstract away complex behavior, such as IO actions, asynchronous behavior uniformly in such a way that we can sequence these operations together. Here’s the best definition of all, “We can abstract the complicated things out of the way so that we can write a program that we want to read it instead of how it really works”.
In the article, Podwysocki provides an example that occurs in any code base quote frequently: that of a chain of conditional statements whose evaluation depends on one another: Certain conditions are evaluated only when some other conditions hold. Such code can easily become unwieldy, argues Podwysocki, and monads are an excellent tool to render this sort of code more intelligible. The examples in the article use Haskell:
Monads are a concept in the computer science field to come from the Haskell programming language. Most people assume, oh, it’s only used for IO, but that’s simply not true at all. Instead, we can treat it as a common abstraction to solve any number of problems, including the conditional logic from above which may succeed or fail…
What’s our ultimate goal here? Well, it’s the ability to string together functions that may or may not succeed and to sequence these instructions together. With an implementation of a monad, we get that ability.
While Haskell has a Monad class type, the monad concept can be employed in languages that do not have a monad class. In the concluding part of this article, Podwysocki illustrates how to implement monads in F#. This can be quite easily implemented in Scala as well.
What do you think of monads as a useful mechanism to make complex code more intelligible?
For those who aren't familiar with monads (but are familiar with Java), you can think of a monad as a checked exception that can never be caught. That is, once a function declares that it "throws" a monad, anything that calls that function must likewise declare that it "throws" a monad, and so on, all the way up to main().
If you're not careful, then, you could have your entire program infested with monads. All I/O functions in the standard library will throw a monad, so any program that performs any form of I/O will have to deal with monads. Good practice dictates that you keep your monadic functions to a minimum and to keep them quarantined so that all of your monadic I/O is carefully blocked off, with the rest of the program kept clean and providing support.
I assume that this is the kind of discipline that the author is advocating.
Monads like objects took me over a year to understand just because the explanations are so obtuse. Once understood, it looked so obvious that I wonder why so many complicated terms and talking around the subject when explaining it to somebody new.
Objects are basically structs containing pointers to data and pointers to functions. Monads are basically functions that wrap other functions that accept only 1 input but produce 2 outputs and transform them into functions that accept 2 inputs and produce 2 outputs so that you can chain them. The rest is ancillary. There are probably all kind of neat things you can do with that, but it is just another design pattern.
> Monads are basically functions > that wrap other functions that accept only 1 input but > produce 2 outputs and transform them into functions that > accept 2 inputs and produce 2 outputs so that you can > chain them. The rest is ancillary. There are probably all > kind of neat things you can do with that, but it is just > another design pattern.
I read through that article and was nonplussed. The problem solved seems trivial to me. Isn't it achieving what the following python function does?
def chain(arg, *funcs): result = None
for f in funcs: result = f(arg) if result == None: return None else: arg = result
return result
There are also simple OO (Java) solutions that do similar things. When people talk about Monads, they make them sound really special. I guess I was expecting something more.
> > Monads are basically functions > > that wrap other functions that accept only 1 input but > > produce 2 outputs and transform them into functions > that > > accept 2 inputs and produce 2 outputs so that you can > > chain them. > > I read through that article and was nonplussed. The > problem solved seems trivial to me. Isn't it achieving > what the following python function does? >
> def chain(arg, *funcs): > result = None > > for f in funcs: > result = f(arg) > if result == None: > return None > else: > arg = result > > return result >
>
A more interesting application is probably a random number generation function like this:
def randmonad(randfuncs=tuple(),init_seed=42,out=tuple()): if len(randfuncs)<1: return out else: cur_rand,new_seed=randfuncs[0](init_seed) cur_randfuncs=randfuncs[1:] cur_out=out+(cur_rand,) return randmonad(randfuncs=cur_randfuncs,init_seed=new_seed,out=cur_out)
> There are also simple OO (Java) solutions that do similar > things. When people talk about Monads, they make them > sound really special. I guess I was expecting something > more.
I'm speaking from the point of view of a novice but from my reading, Monads allow a language such as Haskell to not have holes in its type system. Preserving the strong-typing allows for better formal reasoning about the program, perhaps bringing us closer to programmatic proof of a program's correctness. The examples we see are trivial by themselves but strong-typing is the underlying value.
> I'm speaking from the point of view of a novice but from > my reading, Monads allow a language such as Haskell to not > have holes in its type system. Preserving the > strong-typing allows for better formal reasoning about the > program, perhaps bringing us closer to programmatic proof > of a program's correctness. The examples we see are > trivial by themselves but strong-typing is the underlying > value.
Sorry, I wasn't clear. I understand that it's something that is in Haskell. But my understanding is that this article is arguing that the Monad approach is useful in other languages such as Scala.
The point I am trying to make is that what this is actually achieving seems quite trivial to me. Perhaps it's just the example. What I take away from this article is that what makes Monads useful is how they tie into the constraints of a language like Haskell. That doesn't suggest to me that they are useful in other languages that don't have those constraints.
As I am writing this, one piece of sugar that I've thought might make Java better would be a special type of boolean evaluation where something like this: foo.isBar() would return false if foo was null instead of throwing a NPE. Perhaps monads would allow for something like that?
> Sorry, I wasn't clear. I understand that it's something > that is in Haskell. But my understanding is that this > article is arguing that the Monad approach is useful in > other languages such as Scala. > > The point I am trying to make is that what this is > actually achieving seems quite trivial to me. Perhaps > it's just the example. What I take away from this article > is that what makes Monads useful is how they tie into the > constraints of a language like Haskell. That doesn't > suggest to me that they are useful in other languages that > don't have those constraints.
My point about monads allowing code with side-effects to be Typed still stands for any language using them. Certainly you can question whether or not they give benefit to languages other than Haskell for formal reasoning. I believe one of Martin Odersky's goals was to work towards formal reasoning with Scala.
James Iry has some good blogs about monads from a conceptual standpoint.
> My point about monads allowing code with side-effects to > be Typed still stands for any language using them. > Certainly you can question whether or not they give > benefit to languages other than Haskell for formal > reasoning. I believe one of Martin Odersky's goals was to > work towards formal reasoning with Scala.
I'm not an expert on this but my understanding is that you can't perform much formal reasoning on program with side-effects. Isn't that kind of the definition of a side-effect?
Also, even though I wrote the code in Python, I don't see any reason why you could write that same method with static typing.
> I'm not an expert on this but my understanding is that you > can't perform much formal reasoning on program with > side-effects. Isn't that kind of the definition of a > side-effect? I have to apologize. I read a few posts again on Monads and some of what I wrote is incorrect. Monads do allow for reasoning but it's for the compiler so that more efficient code is produced. Here's a good post.
> > I'm not an expert on this but my understanding is that > you > > can't perform much formal reasoning on program with > > side-effects. Isn't that kind of the definition of a > > side-effect? > I have to apologize. I read a few posts again on Monads > and some of what I wrote is incorrect. Monads do allow for > reasoning but it's for the compiler so that more efficient > code is produced. Here's a good post. > > http://www.reddit.com/r/programming/comments/64th1/monads_i > n_python_in_production_code_you_can_and/c02u9mb
Thanks for the link. I've read a number of articles on monads and I just can't get past all the contradictory statements such as [underlining mine]:
"The reason we go to all this hassle, instead of just having exception throwing built into the language the way you might in Java or most any other imperative language, is because in functional languages functions cannot have side effects. That is, they are completely determined by what they return, and cannot do anything else. They cannot throw exceptions, or print to the screen, or set a global variable, or whatever."
...
"For example, it is possible to write out messages in a purely functional way using a monad. The idea is, instead of just returning a value, you return a value and the string that you want to write to the screen. Why do it that way, instead of just writing out to the screen? Because that way, your functions don't have side-effects, you preserve referential transparency"
I'm sorry if I'm just too dense to understand how something can do something without doing it.
1. printing to the screen is a side effect 2. functions can't have side effects 3. here's how you print to the screen in a function without side effects.
In other words: here's how to do what you can't do. A & !A
This might just be imprecise descriptions of what I side effect is but it's extremely aggravating. It almost seems like people are saying they get it because they are afraid of appearing to be stupid.
This isn't just nitpicking either. In the case of the example from the link you gave, the Monad theoretically prints and returns a value. The claim being that this allows the compiler "to simplify your code the way you would an equation". Sorry, but that's total nonsense. If I want to print the same thing to the screen 100 times, it's not correct for the compiler to simplify this to one call. The whole point of a side effect is that it cannot be simplified. If it could, it wouldn't be a side effect. And the other thing is that I thought a functions lack of side effects meant it didn't matter if it were called once or more than once. This doesn't address the second part at all. It might not matter in a local environment but what if you are distributing the code across a cluster? I might need this no-side-effect-side-effect-producing function to execute at least once on each machine in the cluster.
I'm sure there are all kinds of tricks with Monads to do whatever you need to do but until someone can explain to me the value of monads in a way that is logically consistent: the emperor has no clothes. As far as I am concerned it's just a work around to deal with the fact that reality is not a mathematical function.
Flat View: This topic has 16 replies
on 2 pages
[
12
|
»
]