# A combinator library for taxes

Doing your taxes is no fun. But functional programming can ease the
pain. In this post I describe and demonstrate the Haskell
*tax* library, which provides data types and
combinators for defining taxes.

## What is a tax? §

Wikipedia defines a tax as *a compulsory financial
charge or some other type of levy imposed on a taxpayer*. Most
taxes have monetary “inputs and outputs” but other kinds of taxation
exist, such as the *corvée*. Therefore *tax*
defines a type that is abstracted over its inputs and outputs:

```
newtype Tax b a = Tax { getTax :: b -> a }
deriving (Semigroup, Monoid, Functor, Profunctor)
```

The `Tax b a`

type is a wrapper around the function type `(b -> a)`

.
Although `(->)`

has all the instances we need, I found it more
ergonomic to define a new type that communicates the *intent* of the
values. The `GeneralizedNewtypeDeriving`

extension enables
automatic derivation of the following type class instances:

```
instance Semigroup a => Semigroup (Tax b a)
instance Monoid a => Monoid (Tax b a)
instance Functor (Tax b)
instance Profunctor Tax
```

The `Semigroup`

operation sums outputs. The `Monoid`

identity
is a 0% tax.

```
> getTax (flat 0.1 <> flat 0.2 <> mempty) (Money 10)
λ$3.0
```

For convenience, *tax* exports a type synonym for taxes whose inputs
and outputs are money (of the same type). The input is an amount
subject to taxation (often income), and the output is the tax due:

`type MoneyTax a = Tax (Money a) (Money a)`

The `Money`

type comes from the
*dollaridoos* package.

```
newtype Money a = Money a
deriving (Eq, Ord)
```

`Money`

restricts the operations that can be performed by omitting a
`Num`

instance. Dedicated functions provide the operations that
make sense for money, like *scalar* multiplication:

```
($*) :: (Num a) => Money a -> a -> Money a
(*$) :: (Num a) => a -> Money a -> Money a
```

`Money a`

also has instances for `Semigroup`

and `Monoid`

when the
wrapped type has an instance of `Num`

:

```
instance (Num a) => Semigroup (Money a)
instance (Num a) => Monoid (Money a)
```

All types in *tax* are abstracted over the numeric representation.
Different applications can have different requirements for
precision. Users may want to use a type that carries additional
context, such as a currency. Therefore *tax* lets the user choose
the numeric representation to use.

## Constructing taxes §

The most basic taxes are **lump** sums, and **flat**-rate taxes:

```
lump :: a -> Tax b a
= Tax . const
lump
flat :: (Num a) => a -> Tax (Money a) (Money a)
= Tax . (*$) flat
```

Some other common taxation constructions include taxing the amount
**above** some threshold at a flat rate, or taxing the *whole*
amount at a flat rate when it exceeds the **threshold**. These
functions have the same type signature (I’ll show the implementation
later):

```
above, threshold :: (Num a, Ord a)
=> Money a -> a -> Tax (Money a) (Money a)
```

## Combinators §

More complex taxes can be built using a handful of
*combinators* (functions that assemble smaller
components into more complicated structures). This section
describes the combinators provided by the *tax* package.

Levy the **lesser** or **greater** of two taxes:

```
lesserOf, greaterOf :: (Ord a) => Tax b a -> Tax b a -> Tax b a
= Tax (min <$> getTax t1 <*> getTax t2)
lesserOf t1 t2 = Tax (max <$> getTax t1 <*> getTax t2) greaterOf t1 t2
```

**Limit** the tax payable to a given amount:

```
limit :: (Ord a) => a -> Tax b a -> Tax b a
= lesserOf . lump limit
```

Whereas `above`

and `threshold`

use flat rates, `above'`

and
`threshold'`

subject the taxable portion of the input to arbitrary
`Tax`

computations:

```
above' :: (Num b, Ord b)
=> Money b -> Tax (Money b) a -> Tax (Money b) a
= lmap (\x -> max (x $-$ l) mempty)
above' l
threshold' :: (Ord b, Monoid a) => b -> Tax b a -> Tax b a
=
threshold' l tax Tax (\x -> if x >= l then getTax tax x else mempty)
```

In `above'`

, note the use of `lmap`

to reduce (via the `Money`

subtraction function `($-$)`

) the amount the tax is levied upon.
This is the first usage of the `Profunctor`

instance, but it will
not be the last.

With `above'`

and `threshold'`

in hand, we now see that the
implementations of `above`

and `threshold`

(which apply flat-rate
taxes) are trivial:

```
above, threshold :: (Num a, Ord a)
=> Money a -> a -> Tax (Money a) (Money a)
= above' l . flat
above l = threshold' l . flat threshold l
```

In real world use, I have not (so far) used `above'`

or
`threshold'`

; the flat rate variants sufficed. Nevertheless, for
completeness *tax* exports the general variants.

## Examples §

### Progressive tax §

Many countries use *progressive taxes*, where
different bands of income are taxed at increasing flat rates. For
example, in Australia for the 2020–21 financial year the first
$18,200 is tax free, with income between $18,200 and $45,000 taxed
at 19%, then 32.5% up to $120,000, 37% up to $180,000, and 45% above
$180,000.

Observe that the `Monoid`

instance for `Tax`

sums the outputs of
constituent taxes applied to the same input. We can define a
function that takes a list of thresholds and rates, and constructs a
progressive tax:

```
marginal :: (Num a, Ord a)
=> [(Money a, a)] -> Tax (Money a) (Money a)
= foldMap (uncurry above) marginal
```

Because of the accumulative behaviour, the rate for each band must
be the **difference** to the previous band. The rate for the first
band is implicitly the delta to 0%. The Australian regime can be
expressed as:

```
ausTax :: (Fractional a, Ord a) => Tax (Money a) (Money a)
= marginal
ausTax Money 18200, 0.19 - 0 )
[ ( Money 45000, 0.325 - 0.19 )
, ( Money 120000, 0.37 - 0.325 )
, ( Money 180000, 0.45 - 0.37 ) ] , (
```

The `marginal`

function is useful enough that the *tax* package
provides it.

### Shade in §

Australia’s public health system is funded by the *Medicare Levy*.
It is currently 2% of income, but people below a certain threshold
are exempt (the threshold changes each year). The amount above the
threshold is taxed at 10% until it reaches 2% of the input. This
prevents a sudden jump in tax owed and eliminates a perverse
incentive to earn less than the threshold (if your income is around
that number). The Australian Taxation Office calls this
construction a *shade in*.

Using the functions defined above and taking the lower shade in threshold as a parameter, this tax is an elegant one-liner:

```
medicareLevy :: (Fractional a, Ord a)
=> Money a -> Tax (Money a) (Money a)
= lesserOf (above l 0.1) (flat 0.02) medicareLevy l
```

### Tax offsets §

A tax doesn’t have to result in an amount owed. Maybe your
government will *give* you some money based on your income. Indeed
Australia has some *tax offsets* that reduce the tax paid by people
on lower incomes.

An example is the *Low Income Tax Offset*, which was previously
defined as: *$445, reduced by 1.5c for every dollar earned over
$37,000* (the current definition is more complex). We can implement
it like so:

```
lito :: (Fractional a, Ord a) => Tax (Money a) (Money a)
= limit mempty
lito Money (-445)) <> above (Money 37000) 0.015) (lump (
```

`limit mempty`

ensures that the result does not exceed $0.

### Withholding tax §

Many jurisdictions collect income taxes by requiring employers to
remit a portion of employees’ wages directly to the tax authority.
In Australia, the amount to *withhold* from a payment can be
determined by extrapolating the amount to an annual income,
computing the tax due, then dividing it back down to the pay period.

We can use the `Profunctor`

instance to compute the amount to
withhold for different pay periods. Think of `dimap f g`

as an
adapter that modifies that data flowing in (via `f`

) and out (via
`g`

) of the target computation.

```
= ausTax <> medicareLevy (Money 23226) <> lito
allTaxes
= dimap ($* 52) ($/ 52) allTaxes
weeklyWithholding = dimap ($* 26) ($/ 26) allTaxes
fortnightlyWithholding = dimap ($* 12) ($/ 12) allTaxes monthlyWithholding
```

The examples above are not correct when there are 53 weekly or 27 fortnightly payments in a financial year. Can you see how to define the correct computation?

In the example I ignored some **rounding** rules. I also omitted
several other tax components. It is an example, not a complete
solution!

## Conclusion §

I hope you have enjoyed this tour of the *tax* library. Of course,
most real tax systems are much more complex than the handful of
examples in this article. But *tax* provides building blocks for
defining many kinds of taxes.

My *tax-ato* package builds upon *tax* to provide
types and behaviour for tax in Australia. In addition to the
kinds of taxes described in this article it also handles capital
gains tax, franking credits, student loan repayments,
deductions, and other concepts. I use it to predict and record my
own tax obligations. If you need to perform calculations related to
tax in Australia, you might find it useful too. It is definitely
not complete and comes with no guarantee of correctness.

One final note: oh how I wish Haskell would decouple numeric
literals from the `Num`

and `Fractional`

type classes. `Money`

cannot have instances of these type classes because like other
dimensional types, it is is not closed under multiplication and
division. As a consequence, we have to lift bare numeric values
into `Money`

in several places. Separate type classes for numeric
literals would avoid this. (`IsIntegral`

and `IsRational`

might be
sensible names, following the pattern of `IsString`

and `IsList`

).
Ultimately this is a minor inconvenience, but does add friction to
using *dollaridoos*, *tax*, and programs that use these libraries.