Only 2 exercises in this chapter so no code to write as such – just typing the JSON type classes in.
I had to read parts of this chapter a couple of times though such as the explanation of overlapping types which wasn’t very clear to me. Plus a couple of things that were just wrong – the example of the two types that derived show that won’t compile. Well, they definitely do compile – I spent a couple of minutes scratching my head over this until I realised the authors had made a mistake; the code given in the text is the corrected version of the problem.
The other mistake was the overlapping types given in the BrokenClass example the error I get in ghci suggests to use the pragma for FlexibleInstances, not OverlappingInstances. I think this is due to a change in the compiler from when the book was written.
Exercises
Since these didn’t involve writing any code I’ll just explain how I went about solving them.
The first exercise gave me a bit of a surprise after firing up ghci:
Prelude> :m Control.Arrow Prelude Control.Arrow> :i Arrow class (Control.Category.Category a) => Arrow a where arr :: (b -> c) -> a b c first :: a b c -> a (b, d) (c, d) second :: a b c -> a (d, b) (d, c) (***) :: a b c -> a b' c' -> a (b, b') (c, c') (&&&) :: a b c -> a b c' -> a b (c, c') -- Defined in Control.Arrow instance Arrow (->) -- Defined in Control.Arrow instance (Monad m) => Arrow (Kleisli m) -- Defined in Control.Arrow
Well, well, our old friend -> – hence the name of the module and types I guess. So looking at the signatures, second :: a b c -> a (d, b) (d, c) . Hmmm, we haven’t covered such types before except when we looked at the type of readFile back in chapter 2 which was glossed over.
So second takes an argument of type a b c and produces a result of type a (d,b) (d,c). Pretty opaque given our current knowledge – time to check Hoogle for the Arrow class.
So (->) is an instance of Arrow and second is a mirror image of first. first sends the first component of the input through the argument arrow, and copies the rest unchanged to the output.
Great! It doesn’t help me to know that part of the arrow class is explained by already understanding what an arrow is and what it does. Time to check the source code.
-- Ordinary functions are arrows.
Now this is very interesting and it was alluded to earlier in the book, we’re finally getting somewhere.
instance Arrow (->) where
arr f = f
first f = f *** id
second f = id *** f
-- (f *** g) ~(x,y) = (f x, g y)
-- sorry, although the above defn is fully H'98, nhc98 can't parse it.
(***) f g ~(x,y) = (f x, g y)
As an aside I looked up ~ which I hadn’t seen before and found it mentioned in the Haskell98 report, ~ is as an irrefutable pattern (which therefore always succeeds).
In the (->) instance, second takes a function argument, f, and returns id *** f.
*** takes 2 functions, f and g, and a tuple (x, y) and creates another tuple from f x and g y.
So second results in a function which when applied to a tuple creates a new tuple with its first item unchanged – hence the name second. The remaining tuple value is the result of applying the argument of second to the second tuple value. Now here’s something we can finally test in ghci:
Prelude Control.Arrow> second Data.Char.isUpper ("Yes", 'A')
("Yes",True)
A bit more head scratching using pseudo-code to show my thought process
second :: a b c -> a (d,b) (d,c)
second isUpper
isUpper :: Char -> Bool
isUpper :: (->) Char Bool
second :: (->) Char Bool -> (->) (d, Char) (d, Bool)
second :: (Char -> Bool) -> ((d, Char) -> (d, Bool)
Clarity at last. We kind of knew this intuitively when we saw the definition of toJValue in the JSON (JObj a) instance:
instance (JSON a) => JSON (JObj a) where
toJValue = JObject . JObj . map (second toJValue) . fromJObj
I'm a bit bothered that I only seem to be able to think in concrete terms - (second toJValue) :: (String, a) -> (String, toJValue a). Still it's all new and I haven't studied any maths since university. From background reading I understand arrows, monads and category theory are all manifest in the design of Haskell and its libraries. I expect it's going to be more of a struggle though without the theory behind the execution.
It seems nearly every chapter results in some kind of head exploding insight or revelation. In this case we've come to see concretely that the arrows we've been using in our function type declarations are instances of the general Arrow typeclass and are therefore type constructors that create functions. This leads me to wonder what other Arrow instances are used for. I feel like Columbo.
Just one more thing, class Category a => Arrow a where... I guess we'll get to categories later.
Again from ghci
:t (,) (,) :: a -> b -> (a, b) :t (,,) (,,) :: a -> b -> c -> (a, b, c)
So (,) is a constructor of tuples with 2 items and (,,) is a is a constructor of tuples with 3 items.
And finally for a bit of light relief
:t (,,,,,,,,,,)
(,,,,,,,,,,)
:: a
-> b
-> c
-> d
-> e
-> f
-> g
-> h
-> i
-> j
-> k
-> (a, b, c, d, e, f, g, h, i, j, k)
I wonder how deep that hole goes? They make their compilers pretty smart these days.