• Advertisement
Sign in to follow this  

Monads on .NET

This topic is 3451 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.


Share this post


Link to post
Share on other sites
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
| ...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

(* 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 in
assert (x = Boolean);
let y = type_of y in
let z = type_of z in
assert (y = z);
y

Share this post


Link to post
Share on other sites
Quote:

Looks a lot like... Java.

Well, I would disagree with you here. The point is not that it looks imperative (step by step), it is that I don't need to check for typing failures. In java, in would look more like (assuming I'm using null to convey failure)


abstract class AST
{
abstract Type GetType();
}

class IfExpression : AST
{
private final AST _condition, _then, _else;

@Override
Type GetType()
{
Type ct = _condition.GetType();
if ( ct == null ) return null;
Type tt = _then.GetType();
if ( tt == null ) return null;
Type et = _else.GetType();
if ( et == null ) return null;
if ( tt != et ) return null;
return tt;
}
}





Also, the assert command is defined by myself, and can do anything I want. It could just immediately abort the entire computation (exception-throw-like), or it could keep a list of assertion failures, etc.

Quote:

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 in
assert (x = Boolean);
let y = type_of y in
let z = type_of z in
assert (y = z);
y

In a way, yes. But monads allow more customization, they have a greater "potential".

Share this post


Link to post
Share on other sites
I'm starting to see what you mean, but a definition of the relevant functions (join, bind, construct, assert?) with types would be very helpful.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ahnfelt
I'm starting to see what you mean, but a definition of the relevant functions (join, bind, construct, assert?) with types would be very helpful.

I'll try to work out a few examples; I myself needed several to be able to mentally "factor out" what they had in common so as to get a better idea what abstract concept is behind it all. I'll report back to you later.

Share this post


Link to post
Share on other sites
Have you use list comprehensions? Imagine having comprehensions for most of the concepts in your code. Imagine getting a dsl for free for your concepts. Imagine being able to compose these concepts. Anyways...

My List Monad.

module ListMonad [a] : MyMonad[a]
public bind[b] (m : list[a] , mb : a -> list[b]) : list[b]
match(m)
| [] => []
| xs => List.Flatten(xs.Map(mb))

public mreturn (x : a) : list[a]
[x];

public zero() : list[a]
[]




Then using the monad sugar I have list comprehensions. Although Nemerle already has list comprehensions. This though is a bit more flexible as it benefits from all the monad comprehensions although a tad more verbose.

def l1 = monadq(ListMonad)[ 
p44 <== [0,1,2,3],
(p44 != 0 && p44 != 3) :-
{q44 <== [1,2,2];
returns(p44 + q44)}
];

def AddStr(l1 : list[string], l2 : list[string])
monadq(ListMonad)[
p44 <== l1,
q44 <== l2,
(q44 != "a") :- {returns(p44 + q44)}];



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?


Well monads actually have nothing to do with state. In a lazy pure language like haskell it is no surprise that most article focus on the fact that it can allow sequencing and represent state. However you can have mutable variable in a monad. But there is nothing stopping me from introducing a mutable variable locally in a non pure language. For example using my syntax which is outside standard monad behaviour I could go (mutable q = 9) -> statement; statement and now have a mutable variable q in scope. But q will never have any destructive effects.

Why are monads useful? Again they are a high level mathematical design pattern. This just means that a lot of complex behaviour falls out because you have written a simple set of functions to be associative and compose in a certain way. You can then take advantage of them in a monad comprehension. Indeed the idea is to code little units of behaviour and then compose them cleverly to get almost emergent behaviour from them.

Continuations can be implemented as a monad. Then one can use that to write a simple constraint solver in a few ( < 50) lines. One can create a parser combinator with monads. Then you can for example use that to inteprete natural language... I plan to tackle some things like AI and procedural dungeons with them to show how useful they can be.

[Edited by - Daerax on August 4, 2008 7:54:06 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by daviangel
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.


Yeah. If you have used linq then you have basically used an extension of the list monad. Because of the nature of monads you can write your linq queries. Install the .NET parallel libary, stick a P infront of the queries and have it be made parallel for free.

The idea of composition is important. Monads are declarative, they are a step closer to specify a problem and let the computer solve it.

Share this post


Link to post
Share on other sites
Quote:

Why are monads useful? Again they are a high level mathematical design pattern. This just means that a lot of complex behaviour falls out because you have written a simple set of functions to be associative and compose in a certain way. You can then take advantage of them in a monad comprehension. Indeed the idea is to code little units of behaviour and then compose them cleverly to get almost emergent behaviour from them.


Your answer did not really satisfy the question. (or I didn't understand)

Little units of behavior composed into more complex behavior is not exactly astounding. Why are monads in particular good? Why should programmers go through the inherent mental gymnastics to 'compose them cleverly' when an imperative or an object oriented solution is less convoluted?


And I don't mean to knock monads or functional languages in general. It's just a nagging feeling that I'm somehow missing something small but vital.

Share this post


Link to post
Share on other sites
Quote:
Original post by Ahnfelt
I'm starting to see what you mean, but a definition of the relevant functions (join, bind, construct, assert?) with types would be very helpful.

The actual typechecker I made uses a slightly different, more extended monad, which can be seen as a small exception system: either a computation is succesful, or it fails with a message.


data Result a b = Success b
| Failure a

instance Monad (Result a) where
(Success x) >>= k = k x
(Failure x) >>= _ = Failure x
return t = Success t

instance (Show a, Show b) => Show (Result a b) where
show (Success x) = show x
show (Failure x) = "Failure: " ++ show x

check :: Bool -> a -> Result a ()
---------------------------------
check False err = Failure err
check True _ = Success ()

-- Usage
data TypingError = UnknownIdentifier Identifier
| CallingNonFunction Term Type
| WrongType Term Term Type Type
| DifferingBranchTypes Term Type Term Type
| FixingNonFunction Term Type
| InvalidFixFunction Term Type

type TypingResult = Result TypingError Type

typeOf :: Context -> Term -> TypingResult
-----------------------------------------
typeOf ctx t@(TermIf a b c) = do ta <- typeOf ctx a
check (ta == TypeBool) (WrongType t a TypeBool ta)
tb <- typeOf ctx b
tc <- typeOf ctx c
check (tb == tc) (DifferingBranchTypes b tb c tc)
return tb
...




Another way of using monads is to emulate a prolog-like search (actually a list comprehension). Here's code that finds all possible ways to achieve a certain goal given a list of numbers which can be combined with +, -, * and /.


-- Definition of the list monad (from standard library)
instance Monad [] where
m >>= f = concatMap f m
return x = [x]
fail s = []

instance MonadPlus [] where
mzero = []
mplus = (++)

-- Solving algorithm
import Monad

solve :: [Integer] -> Integer -> [Expression]
---------------------------------------------
solve ns goal = aux (map Number ns) goal
where
aux es goal = [ x | x <- es, eval x == goal ] ++
do (x, es') <- extractOne es -- pick first element from list
(y, es'') <- extractOne es' -- pick second element from list
op <- operators -- pick an operator
guard (check (eval x) op (eval y)) -- check if x op y is a valid operation
let expr = Apply x op y
aux (expr : es'') goal

-- Other definition for aux using Haskell's list comprehension syntax
aux es goal = [ x | x <- es, eval x == goal ] ++
[ r | (x, es') <- extractOne es,
(y, es'') <- extractOne es',
op <- operators,
check (eval x) op (eval y),
r <- aux (Apply x op y : es'') goal ]


check x Plus y = True
check x Minus y = x > y
check x Times y = x /= 1 && y /= 1
check x Divide y = y /= 0 && y /= 1 && x `mod` y == 0


data Operator = Plus | Minus | Times | Divide

instance Show Operator where
show Plus = "+"
show Minus = "-"
show Times = "*"
show Divide = "/"

data Expression = Number Integer
| Apply Expression Operator Expression

precedence :: Operator -> Integer
---------------------------------
precedence Plus = 1
precedence Minus = 1
precedence Times = 2
precedence Divide = 2

instance Show Expression where
show expr = aux expr 0
where
aux (Number n) _ = show n
aux e@(Apply x op y) prec = if prec > precedence op
then "(" ++ aux e 0 ++ ")"
else let prec' = precedence op
in aux x prec' ++ show op ++ aux y prec'

operators :: [Operator]
-----------------------
operators = [ Plus, Minus, Times, Divide ]

extractOne :: [a] -> [(a, [a])]
-------------------------------
extractOne (x:xs) = (x, xs) : [ (y, x:ys) | (y, ys) <- extractOne xs ]
extractOne [] = []

eval :: Expression -> Integer
-----------------------------
eval (Number n) = n
eval (Apply x Plus y) = eval x + eval y
eval (Apply x Minus y) = eval x - eval y
eval (Apply x Times y) = eval x * eval y
eval (Apply x Divide y) = eval x `div` eval y

-- usage
head $ solve [1, 3, 5, 8, 17] 111
(17-5)*(1+8)+3



Haskell's laziness makes this kind of solution quite elegant.

The best way to understand monads is to use and create them in a language which directly supports them (e.g. Haskell). There are many monad tutorials out there, each using a different approach, but in the end, nothing beats hands on experience.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Quote:

Why are monads useful? Again they are a high level mathematical design pattern. This just means that a lot of complex behaviour falls out because you have written a simple set of functions to be associative and compose in a certain way. You can then take advantage of them in a monad comprehension. Indeed the idea is to code little units of behaviour and then compose them cleverly to get almost emergent behaviour from them.


Your answer did not really satisfy the question. (or I didn't understand)

Little units of behavior composed into more complex behavior is not exactly astounding. Why are monads in particular good? Why should programmers go through the inherent mental gymnastics to 'compose them cleverly' when an imperative or an object oriented solution is less convoluted?


And I don't mean to knock monads or functional languages in general. It's just a nagging feeling that I'm somehow missing something small but vital.


Because overly object oriented and imperative designs do not scale well. Because by its very nature an imperative program can never guaranteed to be properly composable. It is not that you just that you can compose simple units into something complex its not just that you can do it with very little effort.

Because imperative programs over specify the solution, it better to say i have this problem here are the properties of the solution I want solve than to say I have this problem, to solve it you have to do this, that , this and the other. And also when this is that you will have to do that and this but not there.

Share this post


Link to post
Share on other sites
Thank you for the examples. From skimming them I think I can see why they're neat - they're like a small interpreter, except you only specify what "sequencing" (for lack of a better word), binding and returning means, the rest uses the ordinary language semantics (?) I will dig into the examples some day when it's not after midnight, and try to get some hands on experience. Thanks again for the pointers.

Share this post


Link to post
Share on other sites
So I can now write and pass around continuations in Nemerle. In the source below I purposely wrote it in a verbose style. I could have compacted it all to 2-3 lines but I plan to read the source in the future when I add coroutines support.


variant Continuations[r,a]
| RunCont { runC : (a -> r) -> r }

public static @>>>= [b](m : Continuations[r,a] , mb : a -> Continuations[r,b]) : Continuations[r,b]
Cont.bind(m,mb);

//callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k
type run_cont[r,a] = Continuations[r,a].RunCont;
module Cont[r,a]{
public let[b](v : a , mb : a -> Continuations[r,b]) : Continuations[r,b]
bind(mreturn(v),mb);

public CallCC[b](f : (a -> Continuations[r,b]) -> Continuations[r,a]) : Continuations[r,a]{
run_cont( fun(k)
def fn = f(
fun(j)
run_cont( fun(_ ){k(j)} ) );

match(fn)
| RunCont(s) => s(k); )
}
public bind[b] (m : Continuations[r,a] , mb : a -> Continuations[r,b]) : Continuations[r,b]
{
run_cont ( fun(k)
match(m)
| RunCont(f)=> f(
fun( j )
match(mb(j))
| RunCont(g) => g(k)
)
);
}
//public defzero : ;

public mreturn (x : a) : Continuations[r,a]
{
run_cont( k => k(x) );
}
}




Using the same comprehension I can write a convoluted delayed computation which does some operations on 3 random numbers. Later on I continue the computation with a function that takes the result of the continuation and swaps the number with some strings or numbers. One out put I got is: [zP, zP, 92, zP, 61, 70, 21]. Stepping through continuation code is dizzying.


def rn = System.Random();

def bar =
monads(Cont)[
let (x) = rn.Next (40),
let (y) = rn.Next (50),
let (z) = rn.Next (70),
returns( x * x + z * y * z * z)
];
def square(n){
Cont.CallCC(fun(k){
monads(Cont)[
let (n') = (n * n + 3),
(n' > 20) :- { k("over twenty") },
returns( (n'- 4) )
];

}
def Show(cc)
{
| Continuations.RunCont(f) => Console.WriteLine(
f( fun(pz){
def rp(c)
{| "1" => "X"
| "4" => "zP"
| x when ToI(x) * ToI(x) > 113 => "3F"
| x => x + rn.Next(3).ToString ()
}
def bd(xs){
| c :: cs => rp(c) :: bd(cs)
| [] => xs
}
bd(ToL(pz.ToString ()));
}
));
}
Show(bar);





[Edited by - Daerax on August 5, 2008 11:22:31 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by SamLowry
The best way to understand monads is to use and create them in a language which directly supports them (e.g. Haskell). There are many monad tutorials out there, each using a different approach, but in the end, nothing beats hands on experience.


I do not agree with this statement. This is a misconception that has carried on because of Wadlers old (c.a. 1990) paper on 'Comprehending Monads' that left people with the impression that they a special syntax is required. He latter clarified in essence of functional programming that this was not the case.

Monads are powerful but are certainly not limited to Haskell. The whole point is to disseminate these useful paradigms in a form anyone can use. Just as higher order function and closures are now standard.

Almost all modern programming languages -languages with closures and parametrically polymorphics or unsafely, dynamically typed - support them (python,ruby, ocaml, F#, Nemerle,C#). E.g. in C# you can comprehend them by hacking the Linq queries and using extension methods. In ruby you can interpret them in a Continuation and then use metaprogramming to wrap syntax.

Indeed continuations can interpret monads as monads can interpret continuations. This is no surprise since Scott? first used continuations to interpret imperative semantics to a functional style. It seems appropriate that it also allows one to interpret imperative semantics in a functional language. Monads are categories which are capable of interpreting the lambda calculus (they are closed cartesian), thus one is essentially embedding a mini language in a host language. You can then use it to extend the capabilities of your language in different directions. They are almost a form of metaprogramming.

Share this post


Link to post
Share on other sites
Quote:
Original post by Daerax
Quote:
Original post by SamLowry
The best way to understand monads is to use and create them in a language which directly supports them (e.g. Haskell). There are many monad tutorials out there, each using a different approach, but in the end, nothing beats hands on experience.

Monads are powerful but are certainly not limited to Haskell. The whole point is to disseminate these useful paradigms in a form anyone can use. Just as higher order function and closures are now standard.

I certainly did not mean to say that monads are or should be restricted to Haskell, I was just giving an example. The good thing about Haskell is that it has syntactic extensions for monads, and gives a number of monads in its standard library. I believe that that helps in understanding them. Once you get the idea, you can apply the concept in any language with or without direct support for monads.

Share this post


Link to post
Share on other sites
Here is some the Maybe monad in C#:


class Maybe<T>
{
public readonly static Maybe<t> Nothing = new Maybe<T>();
public T Value { get; private set; }
public bool HasValue { get; private set; }

Maybe()
{
HasValue = false;
}

public Maybe(T value)
{
Value = value;
HasValue = true;
}
}

public static class MaybeExtensions
{
public static Maybe<T> ToMaybe(this T value){ return new Maybe<T>(value) }
public static Maybe<U> SelectMany<T, U>(this Maybe<T> m, Func<T, Maybe<U>> f)
{
if (!m.HasValue)
return Maybe<T>.Nothing;
return f(m.Value);
}
public static Maybe<V> SelectMany<T, U, V>(this Maybe<T> m, Func<T, Maybe<U>> f, Func<T,U,V> s)
{
if (m.HasValue)
return s(m.Value, f(m.Value).Value).ToMaybe();
return Maybe<V>.Nothing;
}
}



The To???() extension method is the equivalent of the mreturn or return or unit, and the SelectMany method is the equivalent of the bind method.

Usage:

var results = from x in 3.ToMaybe()
from y in 2.ToMaybe()
select x + y;



OR


//using SelectMany<T, U>
var results = 3.ToMaybe().SelectMany(
x => 2.ToMaybe().SelectMany(
y => (x + y).ToMaybe()));

//using SelecMany<T,U,V>
var results = 3.ToMaybe().SelectMany(x => 2.ToMaybe(), (x, y) => x + y);



Then there is continuations which look like this:

delegate R K<T, R>(Func<T, R> k);

public static class ContinuationExtensions
{
public static K<T, R> ToContinuation<T, R>(this T value)
{
return (Func<T, R> c) => c(value);
}

public static K<U, R> SelectMany<T, U, R>(this K<T, R> m, Func<T, K<U, R>> k)
{
return (Func<U, R> c) => m((T x) => k(x)(c));
}

public static K<V, R> SelectMany<T, U, V, R>(this K<T,R> m, Func<T,K<U,R>> k, Func<T,U,V> s)
{
return m.SelectMany(x => k(x).SelectMany( y => s(x, y). ToContinuation<V, R>()));
}
}



Usage is pretty similar to the Maybe monad.

var rn = new Random();
var r = from x in rn.Next(40).ToContinuation<int,int>()
from y in rn.Next(50).ToContinuation<int, int>()
from z in rn.Next(70).ToContinuation<int, int>()
select x * x + z * y * z * z;



Mind you this is not my code. This is from Wes Dyer's blog The Marvels of Monads. He provides some good explanations of how to derive the bind function. I would also recommend aforementioned video with Dr. Beckman. It is really easy to follow.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement