POV-Ray : Newsgroups : povray.off-topic : Monads in C# : Re: Monads in C# Server Time
29 Jul 2024 22:32:16 EDT (-0400)
  Re: Monads in C#  
From: Darren New
Date: 2 Jul 2011 15:36:50
Message: <4e0f7352$1@news.povray.org>
On 7/2/2011 12:16, Warp wrote:
>    Btw, in the articles mentioned in the original post the example is
> given of making an int "nullable" via a monad

OK. In C#, the generic class "Nullable" takes a type argument.

Declaring X to be of type Nullable<T> means that X has the methods
   X = (T) Y;  // This lets you turn a T into a Nullable<T> in other words
   X.HasValue is a boolean that returns true.
   X = null;   // This makes X.HasValue return false
   X.Value returns a value of type T if and only if X.HasValue is true.

So, "Nullable<T>" is an "amplifier" of any type T that lets it also have a 
null value.  (Technically, it only works when T is a value type and thus 
doesn't already support being assigned a null value.)

(You could probably do the same thing with multiple inheritance in C++, but 
C# doesn't really have that.)

Turning it into a monad requires making every operation that works on a T 
also work on a Nullable<T>, with reasonable semantics. That's the "bind" 
part of the story.  So for Nullable<int> to be a monad, you have to be able 
to have two Nullable<int>s support addition, subtraction, printf, etc etc 
etc.  So the "bind" function takes a function F that needs a T and gives you 
back a function F2 that needs a Nullable<T>, for any function F.

> I can't comprehend what this has to do with I/O

I think it's more that since you can take the object (anything you'd want to 
print) and append it onto an IO stream, you wind up with a monad. I don't 
think the monad-ness is essential to the IO-ness. It's more that if you do 
IO the way Haskell does, the type of the IO stream winds up being a monad. 
In part because you need each I/O operation to take the result of the 
previous I/O operation, in order to force the I/O operations to be 
evalueated in the correct order.  So where in C you'd say
    printf("Hello"); printf("World");
and the sequencing would ensure they come out in the same order, in Haskell 
you have to say
    x = fopen("...", "w");
    y = printf("Hello", x);
    z = printf("World", y);
where "x" is the IO stream coming in from the outside world, either as 
stdin/stdout or via fopen. So to speak.  So that chaining of values, after 
each call, is what forces the evaluation order. You can't evaluate "world" 
there before you evaluate "hello", because you don't know what "y" to pass 
before the previous printf finished. Monad syntax is just a way of writing 
that without explicitly writing x and y and z. You just say
    fopen(...) -> printf("Hello") -> printf("world")
and it expands into the above sequence of stuff. And what the "->" means is 
going to change for each type.

So the I/O gets done in the specified order because each I/O statement takes 
as an argument the result of evaluating the previous I/O statement. And in 
the same way, each function that might throw an exception has to take the 
result of the previous function that might have thrown an exception and see 
if that previous function actually threw an exception or not. If the 
previous exception is supposed to keep you from evaluating, you have to 
finish the previous evaluation before you can start the next one.

And just like doing "+" on a series of Nullable<int> requires you to look at 
each Nullable<int> in turn to find out if you have a null before you proceed.

-- 
Darren New, San Diego CA, USA (PST)
   "Coding without comments is like
    driving without turn signals."


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.