Re: [Haskell-cafe] Trapped by the Monads

Greg Buchholz wrote:
Have you read...
Thanks. I'll take a look at it. I also need to take a look at the basic Haskell syntax. An interesting-looking web page which discusses monads is: http://www.nomaware.com/monads/html/analogy.html What struck me was this bit of code: assemblyLine w = (return w) >>= makeChopsticks >>= polishChopsticks >>= wrapChopsticks Interestingly, this looks like Forth (!), where you put a value on the stack, and successive operations fiddle with the stack as a series of transformations. Not that I know Forth, you understand. Hmm, so Haskell can be a concatenative language if you want it to be. ___________________________________________________________ Yahoo! Messenger - NEW crystal clear PC to PC calling worldwide with voicemail http://uk.messenger.yahoo.com

Mark Carter wrote:
What struck me was this bit of code:
assemblyLine w = (return w) >>= makeChopsticks >>= polishChopsticks
= wrapChopsticks
Interestingly, this looks like Forth (!), where you put a value on the stack, and successive operations fiddle with the stack as a series of transformations. Not that I know Forth, you understand. Hmm, so Haskell can be a concatenative language if you want it to be.
Another thing I noticed in my nano-experience of Haskell is the Maybe monad. This is interesting because it's a bit like a hybrid variables. If you look at a book like "Writing Solid Code" (or is it "Code Complete", I can't remember now) which examine C style, they basically scorn the use of hybrid variables. However, I read in something like "Thinking Forth" (or maybe it was just a comment I saw atrributed to Charles Moore, the inventor of Forth), who actually advocated hybrid variables. It would be interesting to see how far the notion of "Haskell as Forth" can go. Can Haskell make a better Forth than Forth can, or does it miss some things which are quite natural in Forth. ___________________________________________________________ How much free photo storage do you get? Store your holiday snaps for FREE with Yahoo! Photos http://uk.photos.yahoo.com

. . .
Another thing I noticed in my nano-experience of Haskell is the Maybe monad. This is interesting because it's a bit like a hybrid variables. If you look at a book like "Writing Solid Code" (or is it "Code Complete", I can't remember now) which examine C style, they basically scorn the use of hybrid variables. However, I read in something like "Thinking Forth" (or maybe it was just a comment I saw atrributed to Charles Moore, the inventor of Forth), who actually advocated hybrid variables.
Could you briefly elaborate on what you mean by "hybrid variables"?
It would be interesting to see how far the notion of "Haskell as Forth" can go. Can Haskell make a better Forth than Forth can, or does it miss some things which are quite natural in Forth.
I've always thought that there was a pretty natural correspondence between Forth and FLs (functional languages); words that pop args from the stack, compute, and push the results correspond to "pure" code, and words that do IO, fetch and assign variables, etc. correspond to the imperative code. The facts that 1) the innards of the Forth machine are exposed to the programmer and 2) that everything in Forth is a word make seamless language extension easy; I don't think the same can be said for FLs. On the other hand, recursion is difficult in Forth, and interpretation-as-you-read make higher-order functions difficult (I never saw any serious attempts at HOFs). I suspect it's a levels-of-abstraction thing; after all, the Forth environment could be viewed as the ideal stack machine upon which to implement FLs and block-structured languages. -- Bill Wood bill.wood@acm.org

Bill Wood wrote:
Could you briefly elaborate on what you mean by "hybrid variables"?
According to Google, hybrid in genetics means "The offspring of genetically dissimilar parents or stock, especially the offspring produced by breeding plants or animals of different varieties, species, or races." It's kind of like that - but for variables. The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory). It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location. The suggested solution is to give each variable one meaning. So, I could define, say, a variable called success, and do something like mem =improved_ malloc(1024, & success) if(success) { do_something(mem) } else { oops() } Admittedly, there's nothing to force the programmer to actually check the success variable; but at least the idea is that everything is much more explicit, and the programmer is less likely to shoot himself in the foot. ___________________________________________________________ Yahoo! Messenger - NEW crystal clear PC to PC calling worldwide with voicemail http://uk.messenger.yahoo.com

Mark Carter wrote:
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location.
This is a bad idea in C, because you cannot force programmers to test the return value properly. The Maybe type in Haskell is a good idea, because you must test the a Maybe value to extract the real value. (Using the Maybe monad this can be hidden.) -- Lennart

Lennart Augustsson wrote:
Mark Carter wrote:
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location.
This is a bad idea in C, because you cannot force programmers to test the return value properly.
The Maybe type in Haskell is a good idea, because you must test the a Maybe value to extract the real value. (Using the Maybe monad this can be hidden.)
Yes, I didn't mean to imply that Haskell had got its ideas fundamentally wrong. I think that the main thrust of the argument is that in something like C, hybrid variables make it easy for a programmer to inadvertently use them wrongly, as opposed to just be lazy. I suppose there's some element of debate on the matter: for example, what C programmer doesn't honestly know that memory allocation can fail. And for an unfamiliar function, why doesn't the programmer read the documentation to find out what constitutes valid and invalid return values. OTOH, I think Charles Moore is quoted as saying that if he want to add 1 to the letter A, then he didn't want the programming language to stop him. Which is quite a contrast to Haskell and its notions on safety. I'm not flamebaiting, you understand, I'm just pointing out the various viewpoints to the argument. ___________________________________________________________ How much free photo storage do you get? Store your holiday snaps for FREE with Yahoo! Photos http://uk.photos.yahoo.com

Mark Carter wrote:
OTOH, I think Charles Moore is quoted as saying that if he want to add 1 to the letter A, then he didn't want the programming language to stop him. Which is quite a contrast to Haskell and its notions on safety. I'm not flamebaiting, you understand, I'm just pointing out the various viewpoints to the argument.
I agree with Charles Moore, if I take his statement to mean that I should be able to add 1 to the bit pattern for the letter A (in some encoding) and the interpret that as a character again. But I would not want the conversions to be implicit (unless the letter A is defined to be the same as an integer, as it is in C). -- Lennart

Compare:
int *p=...;
int x=*p;
and:
let
p = ...
Just x = p
So actually, there is few difference between dereferencing a pointer
without checking for 0, and extracting the Maybe value without
handling Nothing, apart from that it leads to undefined behavior in C
which in fact isn't really a point against "hybrid variables".
On 9/20/05, Lennart Augustsson
Mark Carter wrote:
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location.
This is a bad idea in C, because you cannot force programmers to test the return value properly.
The Maybe type in Haskell is a good idea, because you must test the a Maybe value to extract the real value. (Using the Maybe monad this can be hidden.)
-- Lennart
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

There's a big difference. You can see you are doing something fishy, and the compiler can too, and it can warn you. -- Lennart Michael Walter wrote:
Compare:
int *p=...; int x=*p;
and:
let p = ... Just x = p
So actually, there is few difference between dereferencing a pointer without checking for 0, and extracting the Maybe value without handling Nothing, apart from that it leads to undefined behavior in C which in fact isn't really a point against "hybrid variables".
On 9/20/05, Lennart Augustsson
wrote: Mark Carter wrote:
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location.
This is a bad idea in C, because you cannot force programmers to test the return value properly.
The Maybe type in Haskell is a good idea, because you must test the a Maybe value to extract the real value. (Using the Maybe monad this can be hidden.)
-- Lennart
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

. . .
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
An infamous example would be the convention in Common Lisp that nil, the empty list, is also "false" for conditionals while anything else is "true" for conditionals. So the member function ('a * ['a] -> ['a]) can be used either as a predicate or a function returning a useful value.
It's considered a bad idea because it makes it easy for programmers to use the value inappropriately - witness the number of programmers who pass in 0 as a memory location. The suggested solution is to give each
And the Scheme community chose "#f" and "#t" for boolean values so you had to be a little more explicit about what you were doing. I mostly agree with the "tightening-up", but there are times when I really miss the nil hacks :-) -- Bill Wood bill.wood@acm.org

Bill Wood
The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
IMO, Maybe is exactly the oppsite, it extends a data type with a NULL value (to use DB terminology), and leaves no opportunity for confusing the extended type with the original.
I mostly agree with the "tightening-up", but there are times when I really miss the nil hacks :-)
In Haskell as well? I alwasy felt the cost of inserting a "null" predicate (when I want to test for an empty list) to be low. -k -- If I haven't seen further, it is by standing in the footprints of giants

Mark Carter wrote:
Could you briefly elaborate on what you mean by "hybrid variables"?
According to Google, hybrid in genetics means "The offspring of genetically dissimilar parents or stock, especially the offspring produced by breeding plants or animals of different varieties, species, or races." It's kind of like that - but for variables.
The typical example in C is: mem = malloc(1024) Malloc returns 0 to indicate that memory cannot be allocated, or a memory address if it can. The variable mem is a so-called hybrid variable; it crunches together 2 different concepts: a boolean value (could I allocate memory?) and an address value (what is the address where I can find my allocated memory).
Well in that case, Maybe provides the perfect example of how to
implement hybrid variables correctly.
The types "Ptr a" and "Maybe (Ptr a)" are distinct. If you try to pass
the latter to a function which expects the former, you'll get a
compile-time error. You first have to extract the underlying value,
which means that you need to match against (Just x). If the wrapped
value is Nothing, you'll get an exception. Furthermore, if you forget
to handle the Nothing case, you'll get a compile-time warning.
In C, there's no way to distinguish (using the type system) between a
possibly-null pointer and a non-null pointer. Using a pair of a
boolean and a pointer is the wrong approach because the pointer is
meaningless if the boolean is false, but the type system won't prevent
you from using the value of the pointer in that case.
A more general example is structures where certain fields are only
valid in certain circumstances (e.g. depending upon the "type" field).
Haskell-style sum types, (of which Maybe is an example) are a much
better solution, as the the fields only exist when they are
meaningful.
--
Glynn Clements

. . .
What struck me was this bit of code:
assemblyLine w = (return w) >>= makeChopsticks >>= polishChopsticks >>= wrapChopsticks
Interestingly, this looks like Forth (!), where you put a value on the stack, and successive operations fiddle with the stack as a series of transformations. Not that I know Forth, you understand. Hmm, so Haskell can be a concatenative language if you want it to be.
Some time ago I had occasion to model a special-purpose machine in SML, and the potential users wanted a programmatic interface that looked like an assembly language for the machine. I modeled the instructions as curried functions with the machine state as the last parameter and return value, and defined a "reverse compose" function -- (f >> g) x === g (f x). This allowed me to write programs with op codes and parameters running down the page, just like real assembler (I tabbed over to place the ">>" so they kinda hung out in the comment area so as not to spoil the illusion). It was a quick 'n dirty hack that turned out to be pretty slick. -- Bill Wood bill.wood@acm.org

Mark Carter wrote:
What struck me was this bit of code:
assemblyLine w = (return w) >>= makeChopsticks >>= polishChopsticks >>= wrapChopsticks
Interestingly, this looks like Forth (!), where you put a value on the stack, and successive operations fiddle with the stack as a series of transformations. Not that I know Forth, you understand. Hmm, so Haskell can be a concatenative language if you want it to be.
You might also like take a look the Joy language... http://www.latrobe.edu.au/philosophy/phimvt/joy.html ...sort of a functional, higher level cousin of Forth. In Joy, you create programs by composing functions with other functions. This is in contrast to other languages where functions are mainly applied to other functions (and data). The composition operator Joy is denoted by white space. This is similar to the way juxtaposition is used to denote function application in more conventional languages. (i.e., in Joy "f g" means "g" composed with "f", while in other languages "f(g)" means apply function "f" to argument "g"). Now, what if we had a name for this implicit composition operation? We could then modify it to do a whole host of other things with the functions "f" and "g" besides just the boring old composition (like maybe skipping the execution of "g" if "f" fails, or allowing backtracking, or something more bizarre and clever). And what should we name this new super composition operator? ">>=" maybe? Ah... Greg Buchholz
participants (7)
-
Bill Wood
-
Glynn Clements
-
Greg Buchholz
-
Ketil Malde
-
Lennart Augustsson
-
Mark Carter
-
Michael Walter