
Hans van Thiel wrote:
On Mon, 2008-08-25 at 00:33 +0200, Ben Franksen wrote:
Hans van Thiel wrote:
so 'The Greenhorn's Guide to becoming a Monad Cowboy' is on http://www.muitovar.com/monad/moncow.xhtml
"(Recall that a type definition is just like a data definition, but with no choice operator (|).)" First, you mean to say 'newtype', not 'type' (as in the code). Thanks for the feedback. If and when I get some more comments and error reports, I'll try to fix them. Second, a newtype may also contain only one data element (i.e. one type expression after the constructor), not many, as in a data type definition. Yes, that's what I thought too, but then I got confused by State s a, which looks to have two. Could you explain?
Most probably you are confusing type and data constructor. This is a common error and a hurdle I remember falling over more than once. It is due to the fact that in Haskell both are in completely separate name spaces, nevertheless both use capitalized names. Thus people often use the same name for both, especially with newtype, as there may only be one data constructor. In your case you have newtype State s a = State { runState :: (s -> (a, s)) } where the type constructor takes two (type-) arguments (even for a newtype it can take as many as you like), but the data constructor takes only one value as argument, namely a function from s to (a,s). Clear now?
Third, newtype is unlifted. The books I use for reference, the Craft and SOE, don't seem to mention this. I have to confess, I don't really understand the difference between newtype and data. Again, an explanation would be appreciated.
Did Ryan's explanation help?
As a general comment on the teaching of Haskell, all books and tutorials, which I've seen, appear to treat this aspect of Haskell as if it were self explanatory. This while the better known imperative languages don't have anything like it. Only Real World Haskell explains algebraic data types to some satisfaction (IMHO, of course).
This is one of the more difficult aspects Haskell, IME. I found the Haskell wiki book (http://en.wikibooks.org/wiki/Haskell) very useful, especially the chapter on denotational semantics (http://en.wikibooks.org/wiki/Haskell/Denotational_semantics). If you have a background in imperative languages, especially low-level ones like C, then it may help to think of the values of a lifted type (data ...) as being represented by a pointer to the data proper (e.g. a struct), whereas values of an unlifted type (newtype ...) are represented exactly as the argument type. A value of a lifted type always has one additional value in its type, namely bottom. You may think of bottom as being represented by a null pointer. In fact, one could say that, in Java, Objects are always lifted whereas basic types like integer are unlifted. Now, before I get shot down by the purists, I know that this is not exactly true, since bottom is also the value of an infinite loop, so Java in fact has a 'real' bottom in addition to null, etc. See the above cited online book chapter for a more precise (and still very readable) treatment. Cheers Ben