typedef struct {
char *string;
int value;
} WriterInt;
WriterInt returnWriter(int i)
{
WriterInt r;
r.string = "";
r.value = i;
return r;
}
WriterInt bind(WriterInt (*f)(int),WriterInt x)
{
WriterInt y = f(x.value);
WriterInt z;
z.value = y.value;
int len = strlen(x.string)+strlen(y.string);
z.string = malloc(len+1);
strcpy(z.string,x.string);
strcat(z.string,y.string);
return z;
}
WriterInt print(int i)
{
WriterInt x;
x.string = malloc(32);
x.value = sprintf(x.string,"%d\n",i);
return x;
}
WriterInt printplus_bad(int i)
{
WriterInt x = print(i);
return print(x.value); /* cheating! */
}
WriterInt printplus(int i)
{
WriterInt x = print(i);
return bind(print,x);
}
This time, instead of printing, we build up a string as a side effect.
printplus()
is implemented exactly as before, without knowledge of how to handle side effects, and yet it correctly handles strings returned as a side effect and concatenates them together. The magic is bind()
which allows a function expecting an int
input to be applied to a WriterInt
.I'm hoping that programmers of languages such as C can now begin to see the pattern shared by these examples.
Anyway, this is all just stalling for time while I try to get my quantum computation code working.
If you were doing this in C++, you could just accomplish this by labeling a variable as private, right? Then you wouldn't need to wrap and unwrap it all the time.
ReplyDeleteI keep looking at examples like this of monads, and I can't figure out what the excitement is.
Steven,
ReplyDeleteNot sure what you're trying to achieve using a private member.
My goal here isn't to do anything useful but to simply translate one of the simplest monads into C++ to make the way it works more apparent to people with a C++ background. It's not something that anyone would really write in C++, it's just a baby example in the hope that it illustrates the pattern. A more sophisticated monad like the list monad allows you to do things like logic programming in Haskell, something that is much more difficult to implement in C++ and really leverages the usefulness of monads. But it helps to understand these simple examples first.
Perhaps I misunderstood, but it looks like the wrapping and unwrapping is being done to hide data in these examples.
ReplyDeleteI guess I need to see a more complex example where the pattern makes something easier/clearer to understand why I'd want to use it.
I'm not trying to be difficult, I'm just slow (at least when it comes to monads). :)
Steven,
ReplyDeleteIn pure functional programming languages you aren't allowed to cause side effects. The only way a function can 'interact' with the outside world is to return something. So if you have a function that needs to return a number, but has a certain side effect, the only way to do this is to return the number and return some data 'containing' the side effect. But that's a pain in the ass - you have to write all of your code to return multiple values, and you have to write plumbing code to pass these side-effect containing values around. What monads can do is give a uniform API to side-effect containing data so you can just concentrate on the number that you want to return, and have the side-effect carried along automatically in the background. What's neat about monads is the same API can be used for many different problems, not just handling side-effects.
So half the time when dealing with monads we're solving a problem that we've caused in the first place by using a pure functional language. (But sometimes it's worth using a pure functional language because of other benefits.) The other half of the time we're using monads because they provide facilities that are very hard to implement otherwise - such as logic programming or continuation passing style. I've not given C++ examples of the latter because it would be way too hard. So I've only given examples of things that you can easily implement already in C++.
If you ever get a chance, check out the list monad in Haskell. It's beautifully simple, and yet it takes a considerable amount of work to implement the same thing in C++.