Chapter 6: Shapes III: Perimeters

(Haven't really had much time for Haskell this week – I had set myself the task of comparing some versions of SOE exercises, but will get back to that as soon as I can.  In the mean time, another (shortish) post in the regular series…)

The first part is straight forward enough. We create a sub-module
“Perimeter”, that builds on Shape. The perimeters for Rectangle and
Triangle are easy enough. The one for polygon has a cute trick with zip

  > sides    :: [Vertex] -> [Side]
> sides vs = zipWith distBetween vs (tail vs ++ [head vs])

The cool trick is that (tail vs ++ [head vs]) is the list
cycled back, with the head on the end. So if you had the list [1..5],
you'd be zipping


This means that we can do the distBetween in a circular way. Very cute.
Hudak hints that zip can be written in terms of zipWith.

  > zip :: [a] -> [b] -> [(a, b)]

so we need a function that does

  > aux :: a -> b -> (a,b)

Like so

  > zip a b = zipWith aux a b
> where aux :: a-> b -> (a,b)
> aux a b = (a,b)

Which, checking the “Tour of the prelude”, is how it is defined, only
with a much better name for the auxiliary function: pair.


OK, here's some scary mathematics with nasty hints about integration and
then the off-handed “Oh, we'll just do it as an infinite summation!”
Luckily I've seen this notation, about 12 years ago, and I'm moderately
comfortable with the idea of summing a sequence that converges on zero,
so I think I can proceed without having to run and cower in the woods.

Defining the infinite sequence in terms of scanl is strange

  > s = let aux s i = nextEl e s i
> in scanl aux (0.25 * e^2) [2..]

Rather than defining that the base case s(1) is (0.25 * e^2) we're using
it as the init argument to scanl. scanl is like foldl
except that it returns the intermediate results. So, taking Hudak's
advice to the slow and confused, and working through the first few

 > s = let aux s i = nextEl e s i
> in scanl aux (0.25 * e^2) [2..]

So, something like:

  s = scanl aux s1 [2..]
= scanl aux s1 (2 : [3..])
= s1 : scanl aux (aux s1 2) [3..]
= s1 : scanl aux s2 [3..]
= s1 : s2 : scanl aux s3 [4..]

Cute. Typing in the definition of perimter (Ellipse r1 r2)
though, ghci refused to parse it with the always handy

  parse error (possibly incorrect indentation)

I eventually managed to appease it by entering a newline after the
where thusly:

 > perimeter (Ellipse r1 r2) 
> | r1 > r2 = ellipsePerim r1 r2
> | otherwise = ellipsePerim r2 r1
> where
> ellipsePerim r1 r2

I guess the layout rules for where might have changed since the
publication of the book, or have I made a stupid mistake?