Skip to content

Commit

Permalink
Finish teh cookbook
Browse files Browse the repository at this point in the history
  • Loading branch information
theophile-scrive committed Dec 13, 2024
1 parent f28f08b commit 6b40fb5
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 5 deletions.
31 changes: 27 additions & 4 deletions doc/cookbook/multiverb/MultiVerb.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ Let us create an endpoint that captures an 'Int' and has the following logic:
* If the number is even, we return a 'Bool' in the response body;
* If the number is odd, we return another 'Int' in the response body.
Let us list all possible HTTP responses:
```haskell
Expand All @@ -39,7 +38,8 @@ type Responses =
]
```
Let us create the return type:
Let us create the return type. We will create a sum type that lists the values on the Haskell side that correspond to our HTTP responses.
In order to tie the two types together, we will use a mechanism called `AsUnion` to create a correspondance between the two:
```haskell
data Result
Expand All @@ -53,8 +53,7 @@ data Result
instance GSOP.Generic Result
```
These deriving statements above tie together the responses and the return values, and the order in which they are defined matters.
For instance, if `Even` and `Odd` had switched places in the definition of `Result`, this would provoke an error:
These deriving statements above tie together the responses and the return values, and the order in which they are defined matters. For instance, if `Even` and `Odd` had switched places in the definition of `Result`, this would provoke an error:
```
• No instance for ‘AsConstructor
Expand All @@ -76,6 +75,30 @@ type MultipleChoicesInt =
Result
```
### Implementing AsUnion manually
In the above example, the `AsUnion` typeclass is derived through the help of the `DerivingVia` mechanism,
and the `GenericAsUnion` wrapper.
If you would prefer implementing it yourself, you need to encode your responses as [Peano numbers](https://wiki.haskell.org/Peano_numbers),
augmented with the `I`(identity) combinator.
See how three options can be encoded as the Z (zero), S Z (successor to zero, so one),
and S (S Z) (the sucessor to the successor to zero, so two). This encoding is static, so we know in advance how to decode them to
Haskell datatypes. See the instance below for the encoding/decoding process:
```
instance AsUnion MultipleChoicesIntResponses MultipleChoicesIntResult where
toUnion NegativeNumber = Z (I ())
toUnion (Even b) = S (Z (I b))
toUnion (Odd i) = S (S (Z (I i)))
fromUnion (Z (I ())) = NegativeNumber
fromUnion (S (Z (I b))) = Even b
fromUnion (S (S (Z (I i)))) = Odd i
fromUnion (S (S (S x))) = case x of {}
```
## Integration in a routing table
We want to integrate our endpoint into a wider routing table with another
Expand Down
2 changes: 1 addition & 1 deletion servant/src/Servant/API/MultiVerb.hs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ type MultiVerb1 m cs a = MultiVerb m cs '[a] (ResponseType a)
-- There, we derived the 'AsUnion' instance with the help of Generics.
-- The manual way of implementing the instance is:
--
-- > instance Responses ~ res => AsUnion res Result where
-- > instance AsUnion Responses Result where
-- > toUnion NegativeNumber = Z (I ())
-- > toUnion (Even b) = S (Z (I b))
-- > toUnion (Odd i) = S (S (Z (I i)))
Expand Down

0 comments on commit 6b40fb5

Please sign in to comment.