Review of "Programming in Haskell"

Paul Brown @ 2007-02-25T05:54:48Z

I pre-ordered a copy of Programming in Haskell by Graham Hutton from Amazon last year, and once it showed up last month, its ~150 pages were a quick and pleasant read.

For an experienced programmer who's happy with dirt under their nails, materials for learning Haskell are available on line: the classic Gentle Introduction to Haskell, the Haskell Wiki, and the Haskell Report (language specification and source code for core libraries). Also, the folks on Haskell Cafe and the Haskell IRC channel are generally receptive to beginner questions that aren't obviously homework...

And this brings me to the first strength of the book: the stated target audience for Programming in Haskell is the "absolute beginner", and the book does a good job playing to that audience. Recursion, higher-order functions, and laziness all appear in the book but relatively late and with a good amount of supporting exposition and some rules of thumb. Formal reasoning about Haskell programs (proof by computation, induction, etc.) appear only in the last chapter, and IO is presented without more general discussion of monads but with adequate discussion of the conflict between side-effects and a purely functional language.

The book's other major strength is in the examples. Each chapter includes compact, uncontrived, and interesting examples, which is a major feat for any textbook. I particularly liked the Caesar cipher and Countdown examples. I can see this being a good textbook for an introductory course, and with lecture slides available from the site for the book and supporting materials available from the publisher, the author has clearly gone out of his way to make it a good choice as a textbook.

As for non-positives, I've already spent time mucking through the online materials about Haskell and on a few other books on the topic (The Haskell Road to Logic, Maths, and Programming, The Haskell School of Expression, and Purely Functional Data Structures), so I didn't take any new knowledge about Haskell away from the book. Nonetheless, I did find a few things to take away from the book, e.g., a technique for compactly generating the powerlist (all sublists) of a list that was part of the Countdown example.

Other than the price seeming a bit much on a per-page basis, the only other thing that bugged me about the book — and only because I used to be a working mathematician — was this statement about the Game of Life on p. 94:

That is, we can think of the board as really being a torus, a three-dimensional doughnut-shaped object.

The game board is indeed a torus, but a torus is a two-dimensional object. One representation of a torus embedded in three-dimensional space would be as the surface of a doughnut, but that representation is not what defines a torus. A torus is a two-dimensional surface obtained as the cartesian product of two circles. An equivalent construction is to create a torus by gluing opposite sides of a square, like so:

Living on a torus with the usual metric is like playing Asteroids; if you fly off the {top,bottom,left,right} of the screen at some angle, you fly on the {bottom,top,right,left} at the same angle. Or, to put it another way, if you were a two-dimensional creature, it would be like being on a plane where there were infinitely many copies of you, placed at points with two integer coordinates. While this is admittedly a bit of a niggle, I emailed Graham, so hopefully this shows up as an erratum.

(comment bubbles) 2 comments

Yup-Yupping for Startupping

Paul Brown @ 2007-02-22T18:10:00Z

Several people have pointed to Startupping as a new resource for entrepreneurs, and I've signed up and subscribed to the feeds. It looks to have plenty of traction (in the form of content, participation, and buzz), and I'll throw in my $0.02 where it makes sense.

(comment bubbles) 2 comments

JSON as a Migration Format

Paul Brown @ 2007-02-22T16:11:12Z

I'm making slow progress on my personal publishing platform rewrite in Haskell (see earlier posts here and here), so herein part 3 of n, wherein I experiment with data migration and an embarrassingly simple data model. A forthcoming part 4 will be really simple Atom serialization.

Data Out, Data In

As I experiment with the new platform, I'd like a way to move the data from the typo instance into the new environment on-demand; this post is my lab notebook for the export/import experiment.

One of the things that Rails has gotten 100% right is the ability to (easily) access configured environments via interactive (script/console) or scripting (script/running) front-ends. (Using a framework like Spring in the Java space can provide similar functionality by constructing an application context, but it's more awkward to separate out the services that the runtime container would be providing.) My first thought on exporting was to use YAML, but the significant whitespace and cryptic annotations ("|" for a free-form text block with a trailing linebreak and "|-" for a free-form text block without a trailing linebreak) just rubbed me the wrong way. JSON turns out to be a better choice because ActiveRecord supports JSON export (via ActiveSupport::JSON), and there are a couple of JSON libraries for Haskell. One is a predecessor version of the other, and I'm going to work with the earlier version because it has no dependencies other than a baseline GHC 6.6 install.

Getting an entry out to play with is a piece of cake:

./script/runner 'puts Article.find_by_state("Published").to_json' \
  > /tmp/entry.json

Parsing the JSON is similarly straightforward:

$ ghci
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.6, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Prelude> :load /tmp/JSON.hs
[1 of 1] Compiling JSON             ( /tmp/JSON.hs, interpreted )
Ok, modules loaded: JSON.
*JSON> entry <- P.parseFromFile json "/tmp/entry.json"
Loading package parsec-2.0 ... linking ... done.
Right (Object (fromList [("attributes",Object (fromList [
[...]

(JSON.hs aliases Data.Map as M and Text.ParserCombinators.Parsec as P, so that's where the P.parseFromFile is coming from.) The map of values is wrapped up a bit, but a couple of simple functions will get it out from behind the type constructors:

*JSON> let unR = \(Right r) -> r
*JSON> let unO = \(Object o) -> o
*JSON> :t (unO.unR) entry
(unO.unR) entry :: M.Map String Value

which gets us down to the level of the first map with one entry under the key "attributes". To get the map of attributes we want out:

*JSON> let m = unO ((((M.!).unO.unR) entry) "attributes")
*JSON> m
fromList [("allow_comments",String "1"),("allow_pings",String "1"),...

(Haskell uses ! for dereferencing keys in a Data.Map.) And now the components of the entry are easy to extract:

*JSON> let atts = ((M.!) m)
*JSON> atts "allow_comments"
String "1"
*JSON> atts "updated_at"
String "2006-09-15 02:12:45"
*JSON> atts "body" 
String "<p>Although I really do like the...

The first-cut data model for entries looks like this:

data BlogPost = BlogPost { p_title :: String,
               p_summary :: Maybe String,
               p_permalink :: String,
               p_metadata :: PostMetadata,
               p_body :: String,
               p_tags :: [String],
               p_uid :: String,
               p_comments :: [BlogPost]
             }
        deriving (Show)

data PostMetadata = PostMetadata { m_created :: CalendarTime,
                   m_publish :: CalendarTime,
                   m_updated :: CalendarTime,
                   m_author :: PostAuthor,
                   m_published :: Bool }
          deriving (Show)

data PostAuthor = PostAuthor { p_name :: String,
                   p_uri :: Maybe String,
                   p_email :: Maybe String,
                   p_showEmail :: Bool
                 }
          deriving (Show)

And interpolating from typo's model to the new model is just putting the fields in the right place with a little bit of date munging, since the new model has the expectation that dates are represented as Haskell CalendarTime. The reuse of the BlogPost structure for comments is intentional, both for Atom syndication and to support threaded comments.

Pulling all of the entries out is also straightforward:

$ ./script/runner 'puts (Article.find_all_by_state("Published")).to_json' \
  > /tmp/entry.json
$ ./script/runner 'puts (Article.find_all_by_state("ContentState::Published")).to_json' \
  >> /tmp/entry.json

and pulling comments and trackbacks is a similar exercise:

$ ./script/runner 'puts (Comment.find_all_by_state("ContentState::Ham")).to_json' \
  > /tmp/comments.json
$ ./script/runner 'puts (Trackback.find_all_by_state("ContentState::Ham")).to_json' \
  > /tmp/trackbacks.json

although it takes a little doing to collate comments and trackbacks with their parent posts. So far, so good — unlike any of the other migrations (Radio Userland→SnipSnap, SnipSnap→WordPress, and WordPress→typo) I've done, this looks to be neither lossy nor labor-intensive.

As an aside, over about four years of blogging (2003-02-17 through the present), I've accumulated the equivalent of ~110 single-spaced pages of content.

(comment bubbles) 1 comment

Cure for irbarrhea

Paul Brown @ 2007-02-22T00:19:14Z

I've been working with the ActiveRecord model for my typo installation using irb (via scripts/console), but some expressions were producing a little too much output for convenience, e.g.:

Article.find_all.collect { |a| a.permalink }

Fortunately, there is a convenient configuration property that does the trick by suppressing the output of evaluated expressions:

conf.return_format = ""

After which, what you get is what you puts.

(comment bubbles) 0 comments

Toddler Version of Getting Back on the Horse

Paul Brown @ 2007-02-17T10:07:18Z

The kid took a little spill at the playground today. She was happily rocking away on one of these rocking contraptions that is a fixture on Seattle playgrounds, where the bottom portion is a heavy coil spring and the top is made out of plywood and metal to look like a motorcycle or a horse or whatever. Then she stopped to look at her shoe. I wandered around from behind her to look at her shoe, too, and as soon I was out from behind her, she pitched off backwards and landed flat on her back on packed sand. A 30-inch drop is substantial when you're three feet tall... This was my second fatherly failing of the day, although the earlier incident (wherein I put her shoes on the wrong feet) was more minor but probably still enough to knock me out of contention.

She's made of sturdy stuff and was just fine, but she was pretty upset nonetheless. Once the wife had her calmed down, she trudged straight back, demanded to be put back on, and went back to happily rocking. She's always been an intent and intense little person, although the more common manifestation has been "I am going to jump in that puddle, and there is nothing you can do to stop me. Even if you pick me up kicking and screaming and carry me a few blocks away, I will find my way back..." We've had hopes all along that her intensity would turn out to be a positive thing, and this was the first time that it looked like it might indeed.

02-17-07_1658

(comment bubbles) 0 comments

Ownershit

Paul Brown @ 2007-02-17T03:33:46Z

In the continuing spirit of the Devil's Dictionary, here's a definition of "ownershit":

ownershit, n. What you take when you inherit a project that someone else has tarted up for appearances but is otherwise a complete train wreck.
(comment bubbles) 0 comments

T's and K's

Paul Brown @ 2007-02-13T01:42:48Z

The kid's vocabulary is growing quickly, she says "please" once in a while without being prompted, and she understands ownership ("dada's chair", "mama's shoes", etc.) Just the same, she confuses "t" sounds and "k" sounds some of the time. Her version of "OK" makes me think of classic Eddie Murphy on SNL, back when it was funny. Sometimes, it makes it difficult for me to understand what she wants, but with practice, I think I've more or less got it down. The other day, she came to me and said, "Dada... titty... see!" Being an attentive father, I hustled in to see her favorite stuffed kitty tucked into her bed...

(comment bubbles) 0 comments

All Posts contains 399 items in 57 pages of 7 items each:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57