# arrowp-qq

A preprocessor (aka syntax-desugarer) for arrow notation
based on the original `arrowp`

developed by Ross Paterson ross@soi.city.ac.uk.

`arrowp-qq`

extends the original `arrowp`

in three dimensions:

- It replaces the
`haskell-src`

based parser with one based on `haskell-src-exts`

, which handles most of GHC 8.0.2 Haskell syntax.
- It provides not only a preprocessor but also a quasiquoter, which is a better option in certain cases.
- It extends the desugaring to handle static conditional expressions. See the semantics section below for more details.

Note: `arrowp-qq`

provides an enhanced superset of the original `arrowp`

's functionality. One should have no reason to install both. Considering `arrowp`

no longer builds under modern versions of GHC and Cabal/Stack, `arrowp-qq`

should clearly be the more optimum package to install.

Both `arrowp`

and `arrowp-qq`

were originally developed during the days when GHC's `Arrows`

extension was not fully mature--a time when `proc`

syntax was not the norm. Of course, recent versions of GHC now support this notation directly, and give better error messages to boot. Unfortunately, GHC's `proc`

notation desugarer is in some cases not as good as it could be. As such, `arrowp-qq`

's quasi-quoter can still be useful in producing slightly more optimized desugaring for performance-critical, arrowized applications.

The modern use cases of `arrowp-qq`

are as follows:

- Viewing how your
`proc`

blocks (roughly) look like after desugaring for debugging/profiling purposes.
- Via the
`arrowp`

executable.
- NOTE:
`arrowp-qq`

is NOT guaranteed to produce the same desugaring as GHC (see limitations & semantics below).

- Optimizing the performance of your
`proc`

blocks via smarter desugaring.
- Either through the
`arrowp`

executable OR (preferably) the provided quasiquoter.

## Limitations

The parser cannot handle banana brackets for
control operators in arrow notation (the **proc** keyword in the original paper),
due to a limitation
in `haskell-src-exts`

. In order to use banana brackets, the recommendation
is to fall back to the GHC Arrows parser.

Support for GHC Haskell notation inside arrow blocks is not complete, e.g.
multi-way-if and lambda case are unlikely to work as expected. If you run into
one of these, please open an issue or vote for an existing one, as I plan to extend
the support on demand.

## Installation

`arrowp-qq`

is now compatible with Cabal v3's Nix-style local builds & installs:

`cabal install arrowp-qq`

## Usage

### Viewing desugared `proc`

syntax

```
arrowp myfile.hs | less
```

### Optimization

#### Via the `proc`

quasiquoter

```
addA :: Arrow a => a b Int -> a b Int -> a b Int
addA f g = [proc| x -> do
y <- f -< x
z <- g -< x
returnA -< y + z |]
```

#### Via the **arrowp** preprocessor

Add the following GHC pragma to the top of the source file:

```
{-### OPTIONS -F -pgmF arrowp ### -}
```

This can be useful for preserving compatibility with vanilla `proc`

notation, at the cost of flexibility; that is to say, all `proc`

blocks within the source file will be desugared via `arrowp-qq`

.

## Desugaring Semantics

### Static conditional expression optimization

As mentioned previously, `arrowp-qq`

extends the original `arrowp`

's desugaring to handle static conditional expressions. Given:

```
proc inputs -> do
results <- processor -< inputs
if outputResultsArg
then outputSink -< results
else returnA -< ()
returnA -< results
```

The standard `arrowp`

(and GHC) desugaring for this code is:

```
= ((processor >>> arr (\ results -> (results, results))) >>>
(first
(arr
(\ results -> if outputResultsArg then Left results else Right ())
>>> (outputSink ||| returnA))
>>> arr (\ (_, results) -> results)))
```

This requires an `ArrowChoice`

, but there is a more efficient desugaring which
performs the choice at compile time and thus an `Arrow`

suffices:

```
((processor >>> arr (\ results -> (results, results))) >>>
(first
(if outputResultsArg then outputSink else arr (\ results -> ()))
>>> arr (\ (_, results) -> results)))
```

`first`

call optimization

The GHC desugarer does not do a very good job of minimizing the number of
`first`

calls inserted. In certain `Arrow`

instances, this can have a material effect
on performance. Example:

```
trivial = proc inputs -> do
chunked <- chunk -< inputs
results <- process -< chunked
returnA -< results
```

This code ought to desugar to a chain of arrows, and indeed, both arrowp and
arrowp-qq desugar this to:

```
trivial = chunk >>> process
```

However GHC will produce (approximately) the following code:

```
arr(\inputs -> (inputs,inputs)) >>> first chunk >>> first process >>> arr fst
```