My primary experience with functional programming is my college’s CS151 course, which is taught in Typed Racket. Recently, I’ve done a bit of exploring into Haskell; so far, I’ve read a bit of Learn You A Haskell, and I’ve reimplemented some of CS151’s homework assignments in Haskell. It’s been an interesting experience, so I’ve written up my initial thoughts on the differences between the two languages. I’m planning on turning this into a series as I learn more Haskell, so stay tuned!.
Disclaimer: this post will contain solutions to some of the problems from UChicago’s 2016 winter-quarter CS151. If you are in a future version of this class, please click away.
Season 1: Homebrew Peano Numbers
One of our assignments was to write a simple implementation of Peano numbers, complete with conversion, addition, saturating subtraction, multiplication, and comparison functions. But before we do any of those, let’s first define the type we’ll be working with. In Racket:
(define-type Nat (U 'Zero Suc)) (define-struct Suc ([prev : Nat]))
And in Haskell:
data Nat = Zero | Suc Nat deriving (Show, Eq)
First, let’s look at the simple conversion functions between our
regular integer representations. In Racket:
(: nat->num : Nat -> Integer) (define (nat->num n) (cond [(Suc? n) (+ 1 (nat->num (Suc-prev n)))] [else 0])) (: num->nat : Integer -> Nat) (define (num->nat n) (cond [(not (zero? n)) (Suc (num->nat (sub1 n)))] [else 'Zero]))
And in Haskell:
nat_to_num :: Nat -> Natural nat_to_num Zero = 0 nat_to_num (Suc n) = 1 + nat_to_num n num_to_nat :: Natural -> Nat num_to_nat 0 = Zero num_to_nat n = Suc (num_to_nat (n - 1))
Easy enough. These were the first two functions I wrote in Haskell, so a bunch of things immediately jumped out at me.
- I love how you can just have your base case be an additional line. I like how it’s analogous to solving a system of equations. Very cool
syntax, and probably much more powerful than I know right now. Also
eliminates lots of
matches in Racket that only check one edge case.
- Why does Haskell have such restrictive rules on symbols in function names? From the Haskell wiki:
Variable identifiers start with a lowercase letter, constructor identifiers with an uppercase letter and both can contain underscores, single quotes, letters and digits. Operators are formed from one or more of
'!#$%&\*+./<=>?@\^|-~'. Constructors can be operator names, so long as they start with a ‘
That seems markedly worse than Racket’s “you can call your function
A+->=_*^% for all I care” approach.
3. I love Racket’s treatment of symbols (i.e.
'Zero). There doesn’t seem to be
an analog for that in Haskell.
Let’s examine the addition and multiplication functions. In Racket:
(: nat+ : Nat Nat -> Nat) (define (nat+ i j) (match* (i j) [(_ 'Zero) i] [('Zero _) j] [((Suc _) (Suc jprev)) (nat+ (Suc i) jprev)])) (: nat* : Nat Nat -> Nat) (define (nat* i j) (match* (i j) [('Zero _) 'Zero] [(_ 'Zero) 'Zero] [((Suc _) (Suc jprev)) (nat+ i (nat* i jprev))]))
And in Haskell:
nat_add :: Nat -> Nat -> Nat nat_add n1 Zero = n1 nat_add Zero n2 = n2 nat_add n1 (Suc n2) = Suc (nat_add n1 n2) nat_prod :: Nat -> Nat -> Nat nat_prod _ Zero = Zero nat_prod Zero _ = Zero nat_prod n1 (Suc n2) = nat_add n1 (nat_prod n1 n2)
Again, my Racket code translates easily to Haskell. I’m again struck by how much easier to read the Haskell code is. I haven’t yet had to indent anything, which is impressive. I just hope that the whole spaces-as-function-application thing doesn’t become ambiguous down the line, but I feel like it will.
Is it too much to ask for either of these languages to have a normal comment
; I kinda got used to, but it’s still clunky. There’s also no
way to block-comment, which is a bit annoying, but not too bad with
vim-commentary. But Haskell, why? Oh
-- is just so unreadable. Good thing I have vim set to italicize and dim the colors on all comments,
or I’d gloss right over them. And the block comments too…
Okay, /rant. For now, the conclusion is this: if you’re coming from really any Lisp dialect and you want to learn Haskell, go for it. The syntax is bound to be weird, but you’ll get used to it, and everything should be on conceptually familiar ground, at least to start. Also, Learn You A Haskell is great.
Alright, I guess that’s it for season 1! Roll credits! Season 1, boom!
Correction: 07 February 2017
My friend Isha informed me that Racket does in fact
have block comments with
#| ... |#. However, I don’t think that detracts from
my point of both languages having terrible comment strings.