scalaz/scalaz — pull request #161 by eed3si9n (2012-09-30 06:50:22+00:00)
Title: Composition of applicative functions

see [ML thread](https://groups.google.com/forum/#!topic/scalaz/BtgmR-O21dk).

### steps

> The private `ProductApplicative` is a typeclass instance, but it doesn't have a way of retaining itself as a value because it uses pair to hold the data as `Applicative[({type λ[α] = (F[α], G[α])})#λ]`. So we have half of `⊠`, but we are missing the `(⊗)` operator.

### what this changes

This adds `A => M[B]` wrapper called `AppFunc` that implements `andThenA`, `composeA`, and `productA` methods that implements applicative composition with symbolic aliases `@>>>`, `<<<@`, and `@&&&` respectively.

Using these operators, we can now compose applicative functions:

```scala
// To count characters, treat Int as monoidal applicative
val countChar = AppFuncU { (c: Char) => 1 }

// To count lines, treat Int as monoidal applicative
val countLine = AppFuncU { (c: Char) => test(c === '\n') }

// To count words, we need to detect transitions from whitespace to non-whitespace.
val countWord = AppFuncU { (c: Char) =>
for {
x <- get[Boolean]
val y = c =/= ' '
_ <- put(y)
} yield test(y && !x)
} @>>> AppFuncU { (x: Int) => x }

// Compose applicative functions in parallel
val countAll = countWord @&&& countLine @&&& countChar

// ... and execute them in a single traversal
val ((wordCountState, lineCount), charCount) = countAll traverse text
```

The product and composition uses the existing `ProductApplicative` and `CompositionApplicative` internally. Since I did not touch existing code other than the word count example, it's unlikely this breaks the existing code.

### limitations

Each composition of `AppFunc`s returns more complex `AppFunc`, which breaks down the type inference on `@&&&` or `@>>>` operators if a composite comes on rhs.