Often we need a pair of conversion functions: one to encode a value as a string, and another corresponding function to decode a string back into the original type. Here we show a concise way to define these functions for datatypes whose constructors enumerate a small fixed set of possibilities (sometimes described as “enums”).

We will make use of a few language extensions (GHC generics and the `anyclass`

strategy) to let the compiler automatically generate a list of all the values of an datatype.

We use the deriving strategies extension to be explicit about which deriving mechanism is in use for each typeclass.

This code uses `\case`

expressions.

In addition to GHC’s `Generic`

class, we’ll also need to import the `GEnum`

class from the `generic-deriving`

library. When a type has an instance of the `GEnum`

class, we can use `genum`

to obtain a list of all values of that type.

We’ll be using the `Map`

data structure from the `containers`

library.

First we’ll define two enum datatypes to use for examples. Let’s imagine we’re writing code related to billing for a subscription service. The product comes in three varieties – *basic*, *standard*, and *pro* – and our users can choose to pay either monthly or annually.

For each type, we derive instances of the `Generic`

and `GEnum`

classes. We will not be directly using the `Generic`

class, but it is a requirement for deriving the `GEnum`

class which we will be using.

We’ll also define a `Bill`

datatype, consisting of a product and a billing frequency. These two fields represent a summary of a subscriber’s invoice.

Here are some example encoding functions.

For our first example, we define a way to represent values of the `Product`

type as `String`

s.

Next we define a way to encode the `Bill`

type as an `Integer`

. Since a bill consists of a `Product`

(of which there are three) and a `Frequency`

(of which there are two), then there are *3 × 2 = 6* values of `Bill`

to encode.

*Decoding* is converting back in the other direction – for example, `Integer -> Maybe Bill`

. The return type includes `Maybe`

because not every integer represents a bill, so decoding can fail.

It could be rather tedious and redundant to also write to corresponding decoding functions. Fortunately, we can take a shortcut.

If the type `a`

we’re encoding has an instance of `GEnum`

and we have an encoding function `f :: a -> b`

such as `encodeProduct`

or `encodeBill`

, then this `invert`

function will infer the corresponding decoding function.

Now we can use the `invert`

function to generate the decoding functions for `Product`

and `Bill`

with minimal effort.

We demonstrate by printing the results of some encodings and decodings.

```
putStrLn (encodeProduct Basic)
putStrLn (encodeProduct Standard)
putStrLn (show (decodeProduct "p1"))
putStrLn (show (decodeProduct "xyz"))
putStrLn (show (encodeBill (Bill Basic Annual)))
putStrLn (show (encodeBill (Bill Pro Monthly)))
putStrLn (show (decodeBill 31))
putStrLn (show (decodeBill 50))
```

Next: Dynamic typing