Monads on .NET

Started by
21 comments, last by thed77 15 years, 8 months ago
WARNING: This is a long post. If you are not interested in Programming language theory then this will likely bore you. ---- So Monads are a big topic. Cause the word is fairly fancy. They were discovered within the tomes of Abstract Algebra by mages from the Haskell Tower hundreds of years ago in the early 80's. Since then haskell mages have written thousands of treatises on their use. Recently with the sudden popularity of functional programming the common Sorcerer has found the use of monads. So there is suddenly a resurgence of interest in monads. Monads are basically a higher level of design pattern. The encompass many things including - error handling, containers, Rules System ala prolog, probabilities, quantum mechanics, groups, continuations, parsers, AI etc... Monad combinators allow a powerful composition type methodology, myself I noticed they might be useful for expressing AI and lots o game play type code. Basically the idea is you create a basic building block of components or behaviours and then combine them by interpreting them in a sub language that almost falls out for free. I am using them to make a powerful magic system where users can create spells. For those of you on .NET if you use linq then you have used a monad before. Specific Monads can be implemented in any language with generics. Ability to abstract over monads requires either having a stronger type system or a weaker e.g. dynamic/freely typed system. The use of monads is their ability to capture abstraction and turn them into pure power. Traditional Maybe Monad Using Nemerle
variant Maybev[a]  :  Maybe[a]{ 
  | Nothing
  | Just { jst : a} 
}
class Maybe [a] :  MyMonad[a] {
    public bind (m : Maybev[a] ,  mb : a -> Maybev) : Maybev    {
     match(m){
      | Nothing => Maybev.Nothing();
      | Just(x) => mb(x)
      }}
    public static @>>>= (m : Maybev[a] ,  mb : a -> Maybev) : Maybev{
       m.bind(m,mb);
    }
    public  mreturn (x : a) : Maybev[a]{
      Maybev.Just(x);
    }}
type Just[a] = Maybev[a].Just;
type Nothing[a] = Maybev[a].Nothing;

To use:
def o = Maybev.Just(3) >>>= fun(x1){ 
                                
                                    Maybev.Just(2) >>>= fun(x2){
                                                         Maybe().mreturn(x1 + x2)
                                                 }                                  
                                                               } 
                                    

o is Just(5). But that is nothing special. F# has these things called Workflows and computation expressions. It lets you build and comprehend monads. It is the first language on .NET to have sugared support for monads. Using Nemerle's metaprogramming feature I was able to get similar to that. In F# you can write:
attempt { 
let! n1 = failIfBig inp1
let! n2 = failIfBig inp2
let sum = n1 + n2
return sum };;
In Nemerle with My monad dsl:
def opr = monadq(Maybe)[ 
                   p44 <== Just(3),
                   q44 <== Nothing(),  //<--The computation will bail out here and return Nothing.                    
                   returns (p44 + q44)] //<== only gets here if  ;
It is a macro which converts to the more verbose syntax. It works on any Monad. As well I am writing standard monad operations like map and fold etc as macros since the .NET type system is not flexible enough to allow that. OUTPUT:
KKK
KKP
Car v bike
bike v Car
c v b
13
13 1
ConsoleApplication11del.Maybev`1+Nothing[System.Int32] No good Got 5 Got 5
vh?
Hi! 11 4 11 [2, 4, 6] [7, 10] (7, 10) 6 29 29 4
I was testing other things like currying, extensible matching, function operators and Existential types there.
Advertisement
Maybe I just missed the point of your post, but it seems to me you've written a long introduction to monads, without actually explaining what they are.

If someone isn't familiar with monads, then stuff like "The use of monads is their ability to capture abstraction and turn them into pure power" isn't very useful. What the hell is "pure power"? Are we talking about the energy source of the future here?
And isn't the purpose of most programming language constructs to "capture abstraction"?

So what you're really saying is "monads are awesome and can do a ton of stuff, and you've probably used them before". But the only actual description we get is "they can be converted to power", "and they're used for pretty much the same thing as any other programming language construct."

How exactly is that helpful to someone who wants to know what monads are? [grin]

Then again, maybe I just missed the point of your post... Or maybe I'm just too tired to make sense of it right now... In which case, carry on.
Yeh the post is an advertisement biased towards people who already know Monads and to entice who dont to search them on google. There are so many explanations of monads that I did not even bother to add yet another confusing analogy. ala

"Think of a monad as a spacesuite full of nuclear waste in the ocean next to a container of apples. now, you can't put oranges in the space suite or the nucelar waste falls in the ocean, *but* the apples are carried around anyway, and you just take what you need." - Dons

But in one line a monad is a triple (TypeConstructor, bind(), return()) where each function follows basic laws of association and composition.

It is basically a proof of concept post. Another .net language with monad sugar. I plan to make a more indepth journal post as well.
Come back when you have these monad do-hickeys implemented in C# [grin]
Mike Popoloski | Journal | SlimDX
Maybe not in C#, not yet expressive enough to make them worthwhile (you can implement them quite verbosely in C#. But just not use them easily ), but Ill be sure to expose anything I can utilizing them in a CLS compliant manner.
So in what way are monads useful in a language that actually allows state?

As someone who is inexperienced with functional languages (and formal theory in general) it took me about 5 different readings of what a monad is to understand that they're a very clever way of solving functional languages' general impracticality with something that is even less practical.


Sure, it means you can implement something like nullable types in a bit more elegant of a manner, but that's of little use when even the mere mention of monad causes most rank and file programmers to stare at you blankly. And maybe I've just not hit that 'click' moment, but what's the big deal?
Quote:Original post by Mike.Popoloski
Come back when you have these monad do-hickeys implemented in C# [grin]

Yeah for some reason when I saw .NET in the title and mention of LINQ I also thought C# was going to be mentioned.
Anyways, the mention of monads has given me the urge to look it up at channel 9 and they do seem to have some video's on it.
If they make any sense I'd let everyone know if they are any good;)

Dr. Brian Beckman, a Channel 9 celebrity, astrophysicist and senior software engineer thought it would be a very good idea to address the complexity of monads in an easy to understand way: a technical conversation at the whiteboard with yours truly for Channel 9.


[size="2"]Don't talk about writing games, don't write design docs, don't spend your time on web boards. Sit in your house write 20 games when you complete them you will either want to do it the rest of your life or not * Andre Lamothe
Quote:Original post by Telastyn
So in what way are monads useful in a language that actually allows state?

As someone who is inexperienced with functional languages (and formal theory in general) it took me about 5 different readings of what a monad is to understand that they're a very clever way of solving functional languages' general impracticality with something that is even less practical.


Sure, it means you can implement something like nullable types in a bit more elegant of a manner, but that's of little use when even the mere mention of monad causes most rank and file programmers to stare at you blankly. And maybe I've just not hit that 'click' moment, but what's the big deal?


I toyed around with monads a bit, and as the OP says, it is a very high level design pattern. Making such a concept "explicit" makes it easier to work with and to recognize situations in which they may come in handy.

Functional languages generally offer functions as first-class values. C# does so too with delegates and lambda expressions (since v3.0). Java however does not AFAIK, so you have to "fake them" with classes/interfaces. Using typical functional functions like map, fold, filter etc. in java is quite clumsy due to its very verbose syntax. So, using the higher level concept of function values can very much come in handy, and having a nice syntax for it helps a lot.

The same thing is true for monads: I wouldn't really bother with them if you can't find a nice way to write them down in your language of choice, otherwise it'll look like a very elaborate way of doing things; it'd be like emulating function calls by manually handling the stack and pushing and popping return addresses.

Which brings us to what monads can actually be used for. The OP already mentioned a few uses. I once used monads to implement a type checker in Haskell for a small language, they made the code quite a lot clearer. I'll spare you all the details and just give a comparison of how the code looks like with and without monads:

(* O'Caml-like pseudo code, without monads *)let type_of ast =  match ast with    | if x then y else z ->        let xt = type_of x in        if xt = Failure then Failure        (* x has no valid type *)        else if xt <> Boolean then Failure  (* x is not of type bool *)        else let yt = type_of y in        if yt = Failure then Failure        (* y has no valid type *)        else let zt = type_of z in        if zt = Failure then Failure        (* z has no valid type *)        else if yt <> zt then Failure       (* y and z don't have matching types *)        else Success yt    | ...(* With monads *)let type_of ast =  match ast with    | if x then y else z ->        do x <- type_of x           assert x = Boolean           y <- type_of y           z <- type_of z           assert y = z           return y    | ...
I dont get why, in a purely functional language that uses monads to wrap state, why its not possible to simply annotate functions like List.map or List.iter with an attribute to say automatically parallelise this (using k number of threads/cores) when output must be computed (laziness and all that). likewise function attributes to indicate automatic memoization. I am sure that i have read that monads can supposedly improve static analysis but i see no evidence of the benefits in practice (cursory reading). that said i dont really pretend to understand monads - the best analogy in my mind after several efforts at trying to understand them is with continuation passing style programming.
(* With monads *)...        do x <- type_of x           assert x = Boolean           y <- type_of y           z <- type_of z           assert y = z           return y...

Looks a lot like... Java. Maybe that is the point, but then, what is it's use in a non-pure language? The following ocaml is just as clean and semantically equivalent I think:
let x = type_of x inassert (x = Boolean);let y = type_of y inlet z = type_of z inassert (y = z);y

This topic is closed to new replies.

Advertisement