Tuesday, 30 September 2014

An ode to lazyness


Lazy Fibonacci

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fibs = 0 : 1 : 1 : 2 : 3 : 5 : 8 ...
tail fibs = 1 : 1 : 2 : 3 : 5 : 8 : 13 ...
tail (tail fibs) = 1 : 2 : 3 : 5 : 8 : 13 : 21 ...
tail (tail fibs) = zipWith (+) fibs (tail fibs)
-- but
fibs = 0 : 1 : (tail (tail fibs))
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Loops


newtype Co a b = Co { runCo :: a -> (b, Co' m a b) }

loop :: Co (a, d) (b, d) -> Co a b
loop (Co f) = Co g
where
g b = do
let ((c, d), c') = f (b, d) -- laziness feeds state back into a loop
return (c, loop c')
sumToN :: Co Int Int
sumToN = loop $ id *** init 0
>>> arr (\(acc, i) -> (acc+i, i+1))
The loop construct, in Arrow loop for a Mealy machine. Which allows us to express a traditional loop like construct. Writing our code in terms of iterations instead of recursion, the loop combinator allows us to feed the state back into the loop.


Impossibly efficient - replaceWithMax

replaceWithMax :: [Int] -> [Int]
replaceWithMax l = ans
where
(ans, maximum) = f (l, head l)
f (l:ls, m) = Control.Arrow.first (maximum:) $ f (ls, max m l)
f x = x

Where the function above allows to effectively replace an entire list of elements with the maximum element in the list with a single pass over the list as opposed to passing over the list twice, once to find the max and count the number of elements and the next, to create the list consisting only of the maximum element.


Impossibly efficient Relabelling of bin-trees

relabel :: Tree Int -> Tree Int
relabel t = ans
where
(ans, vecOfLastStrip) = go (0:vecOfLastStrip) t
go :: [Int] -> Tree Int -> (Tree Int, [Int])
go vecOfLabels (Leaf a) = (Leaf (head vecOfLabels, a), head vecOfLabels + 1 : tail vecOfLabels)
go vecOfLabels (Internal t a t'') = (Internal t' (head vecOfLabels, a) t''', head vecOfLabels + 1 : e'')
where
(t', e') = go (tail vecOfLabels) t
(t''', e'') = go e' t''
We have some beautiful code, to can tag each node of a binary tree with an id, where the id is its position as encountered during a Breadth First Search. The way it does this is by considering the tree as a set of diagnol parallel strips of nodes. It iterates across through and across these strips keeping track of a vector of labels to be used. Each index of the vector corresponds to the same index in the strip. The vector for strip (i+1) is an increment of each index of the vector for strip i. The vector for strip 0, is the increment of each index of the vector for the last strip. The invariant of go is that it returns the relabelled tree and the increment of each index of the vector for the last strip of the tree.





Monday, 29 September 2014

Parsers in Haskell 1

Prelude

I found quite a few gems when trying to roll my own parser which I'll talk about over a series of posts. I think parsers illustrate in italic cursive a few of Haskell's strengths: the elegance and power of Haskell EDSLs, the guidance types provide in programming and code reuse.

Define the interface

First lets define what we want to build. A parser is a program that analyzes a string of tokens based on a formal grammar. Thus we want to expose a grammar to the user and something that consumes sentences in the grammar and spits out an answer.

We'll use regular expressions for that grammar, our interface will thus comprise:
concatenate :: Parser Token Result -> Parser Token Result -> Parser Token Result
alternate :: Parser Token Result -> Parser Token Result -> Parser Token Result
star :: Parser Token Result -> Parser Token Result
Concatenate should join two regular expressions end to end, such that it doesn't succeed until both succeed. While alternate is 'opposite' in some sense; it succeeds when either of its arguments succeed.

Let's formalize a bit

These look a fair bit like logical and and or, lets see if they really fit together by applying their laws on our parser.

p `concatenate` (p' `concatenate` p'') = (p `concatenate` p') `concatenate` p'' -- Associativity
p `alternate` (p' `alternate` p'') = (p `alternate` p') `alternate` p'' -- Associativity
p `alternate` p' = p' `alternate` p -- Commutativity

-- Distributivity
p `concatenate` (p' `alternate` p'') = (p `concatenate` p' `alternate` p `concatenate` p'')
p `alternate` (p' `concatenate` p'') = (p `alternate` p') `concatenate` (p `alternate` p'')
The laws quite snugly capture the essence of these operations.


Generalizing

I think parsers are exactly conjuncts and disjuncts. The above laws form the description for the MonadPlus interface with mplus as alternate and (>>=) as concatenate. So in order to build our parser we need not worry about which Monad to use as long as they are legitimate instances of the MonadPlus interface. We can thus build our parser with a high level of abstraction, in addition we have a huge library to power our parser and can switch out Monads as we see fit. 


What about the others?

In addition we will need a way to construct and deconstruct a parser:
match :: Token -> Result -> Parser Token Result
step :: (Token, Parser Token Result) -> (Result, Parser Token Result)
The match combinator should, if it matches a token output the result and step should extract a result and give the new parser. The parser thus seems extremely similar to a stream with step as a form of pattern matching on it and match as a constructor for the stream. In addition it also transforms an input stream into an output one.

Finally we need to build the star combinator that will match 0, 1 or many concatenations of a particular parser. This combinator can be constructed out of what we have so far.

Conclusion

So having defined the interface, applied some generalizations and made some observations the next goal is to build the actual parser interface.

Rumination

I think Haskell has an array of very composable building blocks. They are extremely powerful and general. In point of fact, the MonadPlus interface unifies the concept of conjunctions and disjunctions which crop up all over the place. 


Sunday, 28 September 2014

Isos

Prelude

Continuing on from last time on Equality, excitement obscures fatigue, tumbling through the hackage we cycle through our tabs using our spellbindings - onto Isos.

Isomorphisms

So in the last post we saw Equality. Equality's were really strong, demanding identical types. Isomorphisms are a little kinder, simply requiring a forward and backward mapping between the types whose composition is equal to the identity function.

type Iso s t a b = (Profunctor p, Functor f) => p a (f b) -> p s (f t)

The above type fits the relation between Equality and Isomorphism. The presence of constraints on the contexts implies all Equality are Isomorphism but not the reverse, the reverse being untrue as our proof for equality must work for arbitrary contexts.

Working with the knowledge we gained from our previous post, we know there will be an attempt to show similarity between s and a and b and t.
The Profunctor and Functor contexts substantiate these intuitions, indicating a forward mapping from s to a could be used with the contra variance of the p's first argument, and the backward mapping from b to t using the covariance of its second mapping.
What is strange though is the lack of relationship between the backward and forward mapping.
Thus lets create a type to hold these mappings,
data Exchange a b s t = Exchange (s -> a) (b -> t).

Exchange is a Profunctor, so to fit this into our Isomorphism type, we use our trick from before to get:
type AnIso s t a b = Exchange a b a (Identity b) -> Exchange a b s (Identity t)

What can we do with this?


We can create iso-morphisms from the forward and backward mappings,
iso :: (s -> a) -> (b ->t) -> Iso s t a b
The power granted by the pro functor is just sufficient to achieve this and the function writes itself through the types.
iso f g p = dimap f (fmap g)

Lets prove some typical properties of isomorphisms:
The forward and backward mappings can be flipped, the proof being:
from :: Iso s t a b -> Iso b a t s

If we have a function iso' :: Iso s t a b -> ((s -> a), (b -> t)) that provides access to the mappings, then the definition is pretty straight forward:

from = uncurry (flip iso) . iso'

To extract the mappings Exchange comes into play, Since our isomorphism works for any Profunctor: we can choose whatever Profunctor we want to deconstruct it:
iso' :: Iso s t a b -> ((s -> a), (b -> t))
iso' is = case is (Exchange Prelude.id Identity) of
Exchange s b -> (s, runIdentity . b)
-- Fix 'p' to Exchange and 'f' to Identity in is

Another property is the uniqueness of the inverse mapping, t -> s and b -> a
That is Iso s t a b -> (t -> s) -> b -> a
whose proof is :
under :: Iso s t a b -> (t -> s) -> b -> a
under is f b = uncurry (\h k -> h $ f $ k b) $ iso' is

Let's define some isomorphisms:


Equality is an isomorphism
equality = iso id id

Curried and uncurried functions
curried :: Iso ((a, b) -> c) ((d, e) -> f) (a -> b -> c) (d -> e -> f)
curried = iso curry uncurry

uncurried :: Iso (a -> b -> c) (d -> e -> f) ((a, b) -> c) ((d, e) -> f)
uncurried = iso uncurry curry

Enums and integers:
enum :: Enum a => Simple Iso Int a
enum = iso toEnum fromEnum

Maybe and Either ()
mToE :: Simple Iso (Maybe a) (Either () a)
mToE = iso f g
  where
g (Just x) = Right x
g Nothing = Left ()
     h (Right x) = Just x
h (Left ()) = Nothing

Isomorphisms strike me as a very precise version of the adapter pattern.