|
|
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
|
|