ploeh blog danish software design
Fractal trees with PureScript
A fractal tree drawn with PureScript
Last week, I attended Mathias Brandewinder's F# (and Clojure) dojo in Copenhagen, and had great fun drawing a fractal tree in F# together with other attendees. Afterwards, I started thinking that it'd be fairly easy to port the F# code to Haskell, but then I reconsidered. The combination of Haskell, Windows, and drawing sounded intimidating. This seemed a good opportunity to take PureScript for a spin, because that would enable me to draw the tree on an HTML canvas.
In case you're wondering, a fractal tree is simply a tree that branches infinitely in (I suppose) a deterministic fashion. Here's an example of the output of the code of this article:
This is my first attempt at PureScript, and I think I spent between five and ten hours in total. Most of them I used to figure out how to install PureScript, how to set up a development environment, and so on. All in all I found the process pleasing.
While it's a separate, independent language, PureScript is clearly a descendant of Haskell, and the syntax is similar.
Separating data from effects #
In functional programming, you routinely separate data from effects. Instead of trying to both draw and calculate branches of a tree in a single operation, you figure out how to first define a fractal tree as data, and then subsequently you can draw it.
A generic binary tree is a staple of functional programming. Here's one way to do it:
data Tree a = Leaf a | Node a (Tree a) (Tree a)
Such a tree is either a leaf node with a generically typed value, or an (intermediate) node with a value and two branches, which are themselves (sub)trees.
In a sense, this definition is an approximation, because a 'real' fractal tree has no leafs. In Haskell you can easily define infinite trees, because Haskell is lazily evaluated. PureScript, on the other hand, is eagerly evaluated, so infinite recursion would require jumping through some hoops, and I don't think it's important in this exercise.
While the above tree type can contain values of any type, in this exercise, it should contain line segments. One way to do this is to define a Line
record type:
data Line = Line { x :: Number, y :: Number, angle :: Number, length :: Number, width :: Number }
This is a type with five labelled values, all of them numbers. x
and y
are coordinates for the origin of the line, angle
defines the angle (measured in radians) from the origin, and length
the length of the line. In a similarly obvious vein, width
denotes the width of the line, although this data element has no impact on the calculation of the tree. It's purely a display concern.
Given the first four numbers in a Line
value, you can calculate the endpoint of a line:
endpoint :: forall r. { x :: Number , y :: Number , angle :: Number , length :: Number | r } -> Tuple Number Number endpoint line = -- Flip the y value because Canvas coordinate system points down from upper -- left corner Tuple (line.x + line.length * cos line.angle) (-(-line.y + line.length * sin line.angle))
This may look more intimidating than it really is. The first seven lines are simply the (optional) type declaration; ignore that for a minute. The function itself is a one-liner, although I've formatted it on several lines in order to stay within an 80 characters line width. It simply performs a bit of trigonometry in order to find the endpoint of a line with an origin, angle, and length. As the code comment states, it negates the y
value because the HTML canvas coordinate system points down instead of up (larger y
values are further towards the bottom of the screen than smaller values).
The function calculates a new set of coordinates for the endpoint, and returns them as a tuple. In PureScript, tuples are explicit and created with the Tuple
data constructor.
In general, as far as I can tell, you have less need of tuples in PureScript, because instead, you have row type polymorphism. This is still a new concept to me, but as far as I can tell, it's a sort of static duck typing. You can see it in the type declaration of the endpoint
function. The function takes a line
argument, the type of which is any record type that contains x
, y
, angle
, and length
labels of the Number
type. For instance, as you'll soon see, you can pass a Line
value to the endpoint
function.
Creating branches #
In a fractal tree, you calculate two branches for any given branch. Typically, in order to draw a pretty picture, you make the sub-branches smaller than the parent branch. You also incline each branch an angle from the parent branch. While you can hard-code the values for these operations, you can also pass them as arguments. In order to prevent an explosion of primitive function arguments, I collected all such parameters in a single data type:
data FractalParameters = FractalParameters { leftAngle :: Number, rightAngle :: Number, shrinkFactor :: Number }
This is a record type similar to the Line
type you've already seen.
When you have a FractalParameters
value and a (parent) line, you can calculate its branches:
createBranches :: FractalParameters -> Line -> Tuple Line Line createBranches (FractalParameters p) (Line line) = Tuple left right where Tuple x y = endpoint line left = Line { x: x, y: y, angle: pi * (line.angle / pi + p.leftAngle), length: (line.length * p.shrinkFactor), width: (line.width * p.shrinkFactor) } right = Line { x: x, y: y, angle: pi * (line.angle / pi - p.rightAngle), length: (line.length * p.shrinkFactor), width: (line.width * p.shrinkFactor) }
The createBranches
function returns a tuple of Line
values, one for the left branch, and one for the right branch. First, it calls endpoint
with line
. Notice that line
is a Line
value, and because Line
defines x
, y
, angle
, and length
labels, it can be used as an input argument. This type-checks because of row type polymorphism.
Given the endpoint of the parent line, createBranches
then creates two new Line
values (left
and right
) with that endpoint as their origins. Both of these values are modified with the FractalParameters
argument, so that they branch off to the left and the right, and also shrink in an aesthetically pleasing manner.
Creating a tree #
Now that you can calculate the branches of a line, you can create a tree using recursion:
createTree :: Int -> FractalParameters -> Line -> Tree Line createTree depth p line = if depth <= 0 then Leaf line else let Tuple leftLine rightLine = createBranches p line left = createTree (depth - 1) p leftLine right = createTree (depth - 1) p rightLine in Node line left right
The createTree
function takes a depth
argument, which specifies the depth (or is it the height?) of the tree. The reason I called it depth
is because createTree
is recursive, and depth
controls the depth of the recursion. If depth
is zero or less, the function returns a Leaf
node containing the input line
.
Otherwise, it calls createBranches
with the input line
, and recursively calls createTree
for each of these branches, but with a decremented depth
. It then returns a Node
containing the input line
and the two sub-trees left
and right
.
This implementation isn't tail-recursive, but the above image was generated with a recursion depth of only 10, so running out of stack space wasn't my biggest concern.
Drawing the tree #
With the createTree
function you can create a fractal tree, but it's no fun if you can't draw it. You can use the Graphics.Canvas
module in order to draw on an HTML canvas. First, here's how to draw a single line:
drawLine :: Context2D -> Line -> Eff (canvas :: CANVAS) Unit drawLine ctx (Line line) = do let Tuple x' y' = endpoint line void $ strokePath ctx $ do void $ moveTo ctx line.x line.y void $ setLineWidth line.width ctx void $ lineTo ctx x' y' closePath ctx
While hardly unmanageable, I was surprised that I wasn't able to find a pre-defined function that would let me draw a line. Perhaps I was looking in the wrong place.
With this helper function, you can now draw a tree using pattern matching:
drawTree :: Context2D -> Tree Line -> Eff (canvas :: CANVAS) Unit drawTree ctx (Leaf line) = drawLine ctx line drawTree ctx (Node line left right) = do drawLine ctx line drawTree ctx left drawTree ctx right
If the input tree is a Leaf
value, the first line matches and the function simply draws the line, using the drawLine
function.
When the input tree is a Node
value, the function first draws the line
associated with that node, and then recursively calls itself with the left
and right
sub-trees.
Execution #
The drawTree
function enables you to draw using a Context2D
value, which you can create from an HTML canvas:
mcanvas <- getCanvasElementById "canvas" let canvas = unsafePartial (fromJust mcanvas) ctx <- getContext2D canvas
From where does the "canvas"
element come? Ultimately, PureScript compiles to JavaScript, and you can put the compiled script in an HTML file together with a canvas
element:
<body> <canvas id="canvas" width="800" height="800"></canvas> <script src="index.js" type="text/javascript"></script> </body>
Once you have a Context2D
value, you can draw a tree. Here's the whole entry point, including the above canvas-finding code:
main :: Eff (canvas :: CANVAS) Unit main = do mcanvas <- getCanvasElementById "canvas" let canvas = unsafePartial (fromJust mcanvas) ctx <- getContext2D canvas let trunk = Line { x: 300.0, y: 600.0, angle: (pi / 2.0), length: 100.0, width: 4.0 } let p = FractalParameters { leftAngle: 0.1, rightAngle: 0.1, shrinkFactor: 0.8 } let tree = createTree 10 p trunk drawTree ctx tree
After it finds the Context2D
value, it hard-codes a trunk
Line
and a set of FractalParameters
. From these, it creates a tree of size 10 and draws the tree in the beginning of this article.
You can fiddle with the parameters to your liking. For example, you can make the right angle wider than the left angle:
let p = FractalParameters { leftAngle: 0.1, rightAngle: 0.2, shrinkFactor: 0.8 }
This produces an asymmetric tree:
In order to compile the code, I used this command:
$ pulp browserify -t html/index.js
This compiles the PureScript code to a single index.js
file, which is output to the html
directory. This directory also contains the HTML file with the canvas.
You can find the entire PureScript file in this Gist.
Summary #
It was fun to try PureScript. I've been staying away from JavaScript-based development for many years now, but if I ever have to do some client-side development, I may consider it. So far, I've found that PureScript seems viable for drawing. How good it is if you need to interact with 'normal' web-pages or SPAs, I don't know (yet).
If you have some experience with Haskell, it looks like it's easy to get started with PureScript.
Using Polly with F# async workflows
How to use Polly as a Circuit Breaker in F# async workflows.
Release It! describes a stability design pattern called Circuit Breaker, which is used to fail fast if a downstream service is experiencing problems.
I recently had to add a Circuit Breaker to an F# async workflow, and although Circuit Breaker isn't that difficult to implement (my book contains an example in C#), I found it most prudent to use an existing implementation. Polly seemed a good choice.
In my F# code base, I was already working with an 'abstraction' of the type HttpRequestMessage -> Async<HttpResponseMessage>
: given an HttpClient
called client
, the implementation is as simple as client.SendAsync >> Async.AwaitTask
. Since SendAsync
can throw HttpRequestException
or WebException
, I wanted to define a Circuit Breaker policy for these two exception types.
While Polly supports policies for Task-based APIs, it doesn't automatically work with F# async workflows. The problem is that whenever you convert an async workflow into a Task (using Async.AwaitTask
), or a Task into an async workflow (using Async.StartAsTask
), any exceptions thrown will end up buried within an AggregateException
. In order to dig them out again, I first had to write this function:
// Exception -> bool let rec private isNestedHttpException (e : Exception) = match e with | :? AggregateException as ae -> ae.InnerException :: Seq.toList ae.InnerExceptions |> Seq.exists isNestedHttpException | :? HttpRequestException -> true | :? WebException -> true | _ -> false
This function recursively searches through all inner exceptions of an AggregateException
and returns true
if it finds one of the exception types I'm interested in handling; otherwise, it returns false
.
This predicate enabled me to write the Polly policy I needed:
open Polly // int -> TimeSpan -> CircuitBreaker.CircuitBreakerPolicy let createPolicy exceptionsAllowedBeforeBreaking durationOfBreak = Policy .Handle<AggregateException>(fun ae -> isNestedHttpException ae) .CircuitBreakerAsync(exceptionsAllowedBeforeBreaking, durationOfBreak)
Since Polly exposes an object-oriented API, I wanted a curried alternative, so I also wrote this curried helper function:
// Policy -> ('a -> Async<'b>) -> 'a -> Async<'b> let private execute (policy : Policy) f req = policy.ExecuteAsync(fun () -> f req |> Async.StartAsTask) |> Async.AwaitTask
The execute
function executes any function of the type 'a -> Async<'b>
with a Polly policy. As you can see, there's some back-and-forth between Tasks and async workflows, so this is probably not the most efficient Circuit Breaker ever configured, but I wagered that since the underlying operation was going to involve an HTTP request and response, the overhead would be insignificant. No one has complained yet.
When Polly opens the Circuit Breaker, it throws an exception of the type BrokenCircuitException
. Again because of all the marshalling, this also gets wrapped within an AggregateException
, so I had to write another function to unwrap it:
// Exception -> bool let rec private isNestedCircuitBreakerException (e : Exception) = match e with | :? AggregateException as ae -> ae.InnerException :: Seq.toList ae.InnerExceptions |> Seq.exists isNestedCircuitBreakerException | :? CircuitBreaker.BrokenCircuitException -> true | _ -> false
The isNestedCircuitBreakerException
is similar to isNestedHttpException
, so it'd be tempting to refactor. I decided, however, to rely on the rule of three and leave both functions as they were.
In my F# code I prefer to handle application errors using Either values instead of relying on exceptions, so I wanted to translate any BrokenCircuitException
to a particular application error. With the above isNestedCircuitBreakerException
predicate, this was now possible with a try/with
expression:
// Policy -> ('a -> Async<'b>) -> 'a -> Async<Result<'b, BoundaryFailure>> let sendUsingPolicy policy send req = async { try let! resp = req |> execute policy send return Result.succeed resp with e when isNestedCircuitBreakerException e -> return Result.fail Controllers.StabilityFailure }
This function takes a policy, a send
function that actually sends the request, and the request to send. If all goes well, the resp
onse is lifted into a Success
case and returned. If the Circuit Breaker is open, a StabilityFailure
value is returned instead.
Since the with
expression uses an exception filter, all other exceptions will still be thrown from this function.
It might still be worthwhile to look into options for a more F#-friendly Circuit Breaker. One option would be to work with the Polly maintainers to add such an API to Polly itself. Another option would be to write a separate F# implementation.
Simple holidays
A story about arriving at the simplest solution that could possibly work.
The Zen of Python states: Simple is better than complex. If I've met a programmer who disagrees with that, I'm not aware of it. It's hardly a controversial assertion, but what does 'simplicity' mean? Can you even identify a simple solution?
I often see software developers defaulting to complex solutions, because a simpler solution isn't immediately obvious. In retrospect, a simple solution often is obvious, but only once you've found it. Until then, it's elusive.
I'd like to share a story in which I arrived at a simple solution after several false starts. I hope it can be an inspiration.
Dutch holidays #
Recently, I had to write some code that takes into account Dutch holidays. (In order to address any confusion that could arise from this: No, I'm not Dutch, I'm Danish, but currently, I'm doing some work on a system targeting a market in the Netherlands.) Specifically, given a date, I had to find the latest possible Dutch bank day on or before that date.
For normal week days (Monday to Friday), it's easy, because such a date is already a bank date. In other words, in that case, you can simply return the input date. Also, in normal weeks, given a Saturday or Sunday, you should return the preceding Friday. The problem is, however, that some Fridays are holidays, and therefore not bank days.
Like many other countries, the Netherlands have complicated rules for determining official holidays. Here are some tricky parts:
- Some holidays always fall on the same date. One example is Bevrijdingsdag (Liberation Day), which always falls on May 5. This holiday celebrates a historic event (the end of World War II in Europe), so if you wanted to calculate bank holidays in the past, you'd have to figure out in which year this became a public holiday. Surely, at least, it must have been 1945 or later.
- Some holidays fall on specific week days. The most prominent example is Easter, where Goede Vrijdag (Good Friday) always (as the name implies) falls on a Friday. Which Friday exactly can be calculated using a complicated algorithm.
- One holiday (Koningsdag (King's Day)) celebrates the king's birthday. The date is determined by the currently reigning monarch's birthday, and it's called Queen's Day when the reigning monarch is a queen. Obviously, the exact date changes depending on who's king or queen, and you can't predict when it's going to change. And what will happen if the current monarch abdicates or dies before his or her birthday, but after the new monarch's birthday? Does that mean that there will be no such holiday that year? Or what about the converse? Could there be two such holidays if a monarch abdicates after his or her birthday, and the new monarch's birthday falls later the same year?
Figuring out if a date is a bank day, then, is what you might call an 'interesting' problem. How would you solve it? Before you read on, take a moment to consider how you'd attempt to solve the problem. If you will, you can consider the test cases immediately below to get a better sense of the problem.
Test cases #
Here's a small set of test cases that I wrote in order to describe the problem:
Test case | Input date | Expected output |
---|---|---|
Monday | 2017-03-06 | 2017-03-06 |
Tuesday | 2017-03-07 | 2017-03-07 |
Wednesday | 2017-03-08 | 2017-03-08 |
Thursday | 2017-03-09 | 2017-03-09 |
Friday | 2017-03-10 | 2017-03-10 |
Saturday | 2017-03-11 | 2017-03-10 |
Sunday | 2017-03-12 | 2017-03-10 |
Good Friday | 2017-04-14 | 2017-04-13 |
Saturday after Good Friday | 2017-04-15 | 2017-04-13 |
Sunday after Good Friday | 2017-04-16 | 2017-04-13 |
Easter Monday | 2017-04-17 | 2017-04-13 |
Ascension Day - Thursday | 2017-05-25 | 2017-05-24 |
Whit Monday | 2110-05-26 | 2110-05-23 |
Liberation Day | 9713-05-05 | 9713-05-04 |
Option: query a third-party service #
How would you solve the problem? The first solution that occurred to me was to use a third-party service. My guess is that most developers would consider this option. After all, it's essentially third-part data. The official holidays are determined by a third party, in this case the Dutch state. Surely, some Dutch official organisation would publish the list of official holidays somewhere. Perhaps, if you're lucky, there's even an on-line service you can query in order to download the list of holidays in some machine-readable format.
There are, however, problems with this alternative: if you query such a service each time you need to find an appropriate bank date, how are you going to handle network errors? What if the third-part service is (temporarily) unavailable?
Since I'm trying to figure out bank dates, you may already have guessed that I'm handling money, so it's not desirable to simple throw an exception and say that a caller would have to try again later. This could lead to loss of revenue.
Querying a third-party service every time you need to figure out a Dutch bank holiday is out of the question for that reason. It's also likely to be inefficient.
Option: cache third-party data #
Public holidays rarely change, so your next attempt could be a variation of the previous. Use third-party data, but instead of querying a third-party service every time you need the information, cache it.
The problem with caching is that you're not guaranteed that the data you seek is in the cache. At application start, caches are usually empty. You'd have to rely on making one good query to the third-party data source in order to put the data in the cache. Only if that succeeds can you use the cache. This, again, leaves you vulnerable to the normal failure modes of distributed computing. If you can't reach the third-party data source, you have nothing to put in the cache.
This can be a problem at application start, or when the cache data expires.
Using a cache reduces the risk that the data is unavailable, but it doesn't eliminate it. It also adds complexity in the form of a cache that has to be configured and managed. Granted, you can use a reusable cache library or service to minimise that cost, so it may not be a big deal. Still, when making a decision about application architecture, I think it helps to explicitly identify advantages and disadvantages.
Using a cache felt better to me, but I still wasn't happy. Too many things could go wrong.
Option: persistent cache #
An incremental improvement on the previous option would be to write the cache data to persistent storage. This takes care of the issue with the cache being empty at application start-up. You can even deal with cache expiry by keep using stale data if you can't reach the 'official' source of the data.
It leaves me a bit concerned, though, because if you allow the system to continue working with stale data, perhaps the application could enter a state where the data never updates. This could happen if the official data source moves, or changes format. In such a case, your application would keep trying to refresh the cache, and it would permanently fail. It would permanently run with stale data. Would you ever discover that problem?
My concern is that the application could silently fail. You could counter that by logging a warning somewhere, but that would introduce a permanent burden on the team responsible for operating the application. This isn't impossible, but it does constitute an extra complexity. This alternative still didn't feel good to me.
Option: cron #
Because I wasn't happy with any of the above alternatives, I started looking for different approaches to the problem. For a short while, I considered using a .NET implementation of cron, with a crontab file. As far as I can tell, though there's no easy way to define Easter using cron, so I quickly abandoned that line of inquiry.
Option: Nager.Date #
I wasn't entirely done with idea of calculating holidays on the fly. While calculating Easter is complicated, it can be done; there is a well-defined algorithm for it. Whenever I run into a general problem like this, I assume that someone has already done the required work, and this is also the case here. I quickly found an open source library called Nager.Date; I'm sure that there are other alternatives, but Nager.Date looks like it's more than good enough.
Such a library would be able to calculate all holidays for a given year, based on the algorithms embedded in it. That looked really promising.
And yet... again, I was concerned. Official holidays are, as we've already established, politically decided. Using an algorithmic approach is fundamentally wrong, because that's not really how the holidays are determined. Holidays are defined by decree; it just so happens that some of the decrees take the form of an algorithm (such as Easter).
What would happen if the Dutch state decides to add a new holiday? Or to take one away? Of when a new monarch is crowned? In order to handle such changes, we'd now have to hope that Nager.Date would be updated. We could try to make that more likely to happen by sending a pull request, but we'd still be vulnerable to a third party. What if the maintainer of Nager.Date is on vacation?
Even if you can get a change into a library like Nager.Date, how is the algorithmic approach going to deal with historic dates? If the monarch changes, you can update the library, but does it correctly handle dates in the past, where the King's Day was different?
Using an algorithm to determine a holiday seemed promising, but ultimately, I decided that I didn't like this option either.
Option: configuration file #
My main concern about using an algorithm is that it'd make it difficult to handle arbitrary changes and exceptional cases. If we'd use a configuration file, on the other hand, we could always edit the configuration file in order to add or remove holidays for a given year.
In essence, I was envisioning a configuration file that simply contained a list of holidays for each year.
That sounds fairly simple and maintainable, but from where should the data come?
You could probably download a list of official holidays for the next few years, like 2017, 2018, 2019, and so on, but the list would be finite, and probably not cover more than a few years into the future.
What if, for example, I'd only be able to find an official list that goes to 2020? What will happen, then, when our application enters 2021? To the rest of the code base, it'd look like there were no holidays in 2021.
At this time we can expect that new official lists have been published, so a programmer could obtain such a list and update the configuration file when it's time. This, unfortunately, is easy to forget. Four years in the future, perhaps none of the original programmers are left. It's more than likely that no one will remember to do this.
Option: algorithm-generated configuration file #
The problem that the configuration data could 'run out' can be addressed by initialising the configuration file with data generated algorithmically. You could, for example, ask Nager.Date to generate all the holidays for the next many years. In fact, the year 9999 is the maximum year handled by .NET's System.DateTime
, so you could ask it to generate all the holidays until 9999.
That sounds like a lot, but it's only about half a megabyte of data...
This solves the problem of 'running out' of holiday data, but still enables you to edit the holiday data when it changes in the future. For example, if the King's Day changes in 2031, you can change all the King's Day values from 2031 onward, while retaining the correct values for the previous years.
This seems promising...
Option: hard-coded holidays #
I almost decided to use the previous, configuration-based solution, and I was getting ready to invent a configuration file format, and a reader for it, and so on. Then I recalled Mike Hadlow's article about the configuration complexity clock.
I'm fairly certain that the only people who would be editing a hypothetical holiday configuration file would be programmers. In that case, why put the configuration in a proprietary format? Why deal with the hassle of reading and parsing such a file? Why not put the data in code?
That's what I decided to do.
It's not a perfect solution. It's still necessary to go and change that code file when the holiday rules change. For example, when the King's Day changes, you'd have to edit the file.
Still, it's the simplest solution I could come up with. It has no moving parts, and uses a 'best effort' approach in order to guarantee that holidays will always be present. If you can come up with a better alternative, please leave a comment.
Data generation #
Nager.Date seemed useful for generating the initial set of holidays, so I wrote a small F# script that generated the necessary C# code snippets:
#r @"packages/Nager.Date.1.3.0/lib/net45/Nager.Date.dll" open System.IO open Nager.Date let formatHoliday (h : Model.PublicHoliday) = let y, m, d = h.Date.Year, h.Date.Month, h.Date.Day sprintf "new DateTime(%i, %2i, %2i), // %s/%s" y m d h.Name h.LocalName let holidays = [2017..9999] |> Seq.collect (fun y -> DateSystem.GetPublicHoliday (CountryCode.NL, y)) |> Seq.map formatHoliday File.WriteAllLines (__SOURCE_DIRECTORY__ + "/dutch-holidays.txt", holidays)
This script simply asks Nager.Date to calculate all Dutch holidays for the years 2017 to 9999, format them as C# code snippets, and write the lines to a text file. The size of that file is 4 MB, because the auto-generated code comments also take up some space.
First implementation attempt #
The next thing I did was to copy the text from dutch-holidays.txt
to a C# code file, which I had already prepared with a class and a few methods that would query my generated data. The result looked like this:
public static class DateTimeExtensions { public static DateTimeOffset AdjustToLatestPrecedingDutchBankDay( this DateTimeOffset value) { var candidate = value; while (!(IsDutchBankDay(candidate.DateTime))) candidate = candidate.AddDays(-1); return candidate; } private static bool IsDutchBankDay(DateTime date) { if (date.DayOfWeek == DayOfWeek.Saturday) return false; if (date.DayOfWeek == DayOfWeek.Sunday) return false; if (dutchHolidays.Contains(date.Date)) return false; return true; } #region Dutch holidays private static DateTime[] dutchHolidays = { new DateTime(2017, 1, 1), // New Year's Day/Nieuwjaarsdag new DateTime(2017, 4, 14), // Good Friday/Goede Vrijdag new DateTime(2017, 4, 17), // Easter Monday/ Pasen new DateTime(2017, 4, 27), // King's Day/Koningsdag new DateTime(2017, 5, 5), // Liberation Day/Bevrijdingsdag new DateTime(2017, 5, 25), // Ascension Day/Hemelvaartsdag new DateTime(2017, 6, 5), // Whit Monday/Pinksteren new DateTime(2017, 12, 25), // Christmas Day/Eerste kerstdag new DateTime(2017, 12, 26), // St. Stephen's Day/Tweede kerstdag new DateTime(2018, 1, 1), // New Year's Day/Nieuwjaarsdag new DateTime(2018, 3, 30), // Good Friday/Goede Vrijdag // Lots and lots of dates... new DateTime(9999, 5, 6), // Ascension Day/Hemelvaartsdag new DateTime(9999, 5, 17), // Whit Monday/Pinksteren new DateTime(9999, 12, 25), // Christmas Day/Eerste kerstdag new DateTime(9999, 12, 26), // St. Stephen's Day/Tweede kerstdag }; #endregion }
My old computer isn't happy about having to compile 71,918 lines of C# in a single file, but it's doable, and as far as I can tell, Visual Studio caches the result of compilation, so as long as I don't change the file, there's little adverse effect.
Unit tests #
In order to verify that the implementation works, I wrote this parametrised test:
public class DateTimeExtensionsTests { [Theory] [InlineData("2017-03-06", "2017-03-06")] // Monday [InlineData("2017-03-07", "2017-03-07")] // Tuesday [InlineData("2017-03-08", "2017-03-08")] // Wednesday [InlineData("2017-03-09", "2017-03-09")] // Thursday [InlineData("2017-03-10", "2017-03-10")] // Friday [InlineData("2017-03-11", "2017-03-10")] // Saturday [InlineData("2017-03-12", "2017-03-10")] // Sunday [InlineData("2017-04-14", "2017-04-13")] // Good Friday [InlineData("2017-04-15", "2017-04-13")] // Saturday after Good Friday [InlineData("2017-04-16", "2017-04-13")] // Sunday after Good Friday [InlineData("2017-04-17", "2017-04-13")] // Easter Monday [InlineData("2017-05-25", "2017-05-24")] // Ascension Day - Thursday [InlineData("2110-05-26", "2110-05-23")] // Whit Monday [InlineData("9713-05-05", "9713-05-04")] // Liberation Day public void AdjustToLatestPrecedingDutchBankDayReturnsCorrectResult( string sutS, string expectedS) { var sut = DateTimeOffset.Parse(sutS); var actual = sut.AdjustToLatestPrecedingDutchBankDay(); Assert.Equal(DateTimeOffset.Parse(expectedS), actual); } }
All test cases pass. This works in [Theory]
, but unfortunately, it turns out, it doesn't work in practice.
When used in an ASP.NET Web API application, AdjustToLatestPrecedingDutchBankDay
throws a StackOverflowException
. It took me a while to figure out why, but it turns out that the stack size is smaller in IIS than when you run a 'normal' .NET process, such as an automated test.
System.DateTime
is a value type, and as far as I can tell, it uses some stack space during initialisation. When the DateTimeExtensions
class is first used, the static dutchHolidays
array is initialised, and that uses enough stack space to exhaust the stack when running in IIS.
Final implementation #
The stack space problem seems to be related to DateTime
initialisation. If I store a similar number of 64-bit integers in an array, it seems that there's no problem.
First, I had to modify the formatHoliday
function:
let formatHoliday (h : Model.PublicHoliday) = let t, y, m, d = h.Date.Ticks, h.Date.Year, h.Date.Month, h.Date.Day sprintf "%19iL, // %i-%02i-%02i, %s/%s" t y m d h.Name h.LocalName
This enabled me to generate a new file with C# code fragments, but now containing ticks instead of DateTime
values. Copying those C# fragments into my file gave me this:
public static class DateTimeExtensions { public static DateTimeOffset AdjustToLatestPrecedingDutchBankDay( this DateTimeOffset value) { var candidate = value; while (!(IsDutchBankDay(candidate.DateTime))) candidate = candidate.AddDays(-1); return candidate; } private static bool IsDutchBankDay(DateTime date) { if (date.DayOfWeek == DayOfWeek.Saturday) return false; if (date.DayOfWeek == DayOfWeek.Sunday) return false; if (dutchHolidays.Contains(date.Date.Ticks)) return false; return true; } #region Dutch holidays private static long[] dutchHolidays = { 636188256000000000L, // 2017-01-01, New Year's Day/Nieuwjaarsdag 636277248000000000L, // 2017-04-14, Good Friday/Goede Vrijdag 636279840000000000L, // 2017-04-17, Easter Monday/ Pasen 636288480000000000L, // 2017-04-27, King's Day/Koningsdag 636295392000000000L, // 2017-05-05, Liberation Day/Bevrijdingsdag 636312672000000000L, // 2017-05-25, Ascension Day/Hemelvaartsdag 636322176000000000L, // 2017-06-05, Whit Monday/Pinksteren 636497568000000000L, // 2017-12-25, Christmas Day/Eerste kerstdag 636498432000000000L, // 2017-12-26, St. Stephen's Day/Tweede kerstdag 636503616000000000L, // 2018-01-01, New Year's Day/Nieuwjaarsdag 636579648000000000L, // 2018-03-30, Good Friday/Goede Vrijdag // Lots and lots of dates... 3155171616000000000L, // 9999-05-06, Ascension Day/Hemelvaartsdag 3155181120000000000L, // 9999-05-17, Whit Monday/Pinksteren 3155372928000000000L, // 9999-12-25, Christmas Day/Eerste kerstdag 3155373792000000000L, // 9999-12-26, St. Stephen's Day/Tweede kerstdag }; #endregion }
That implementation still passes all tests, and works at in practice as well.
Conclusion #
It took me some time to find a satisfactory solution. I had more than once false start, until I ultimately arrived at the solution I've described here. I consider it simple because it's self-contained, deterministic, easy to understand, and fairly easy to maintain. I even left a comment in the code (not shown here) that described how to recreate the configuration data using the F# script shown here.
The first solution that comes into your mind may not be the simplest solution, but if you take some time to consider alternatives, you may save yourself and your colleagues some future grief.
Comments
What do you think about creating an "admin page" that would allow users to configure the bank holidays themselves (which would then be persisted in the application database)? This moves the burden of correctness to the administrators of the application, who I'm sure are highly motivated to get this right - as well as maintain it. It also removes the need for a deployment in the face of changing holidays.
For the sake of convenience, you could still "seed" the database with the values generated by your F# script
jadarnel27, thank you for writing. Your suggestion could be an option as well, but is hardly the simplest solution. In order to implement that, I'd need to add an administration web site for my application, program the user interface, connect the administration site and my original application (a REST API) to a persistent data source, write code for input validation, etcetera.
Apart from all that work, the bank holidays would have to be stored in an out-of-process data store, such as a database or NoSQL data store, because the REST API that needs this feature is running in a server farm. This adds latency to each lookup, as well as a potential error source. What should happen if the connection to the data store is broken? Additionally, such a data store should be backed up, so we'd also need to establish an operational procedure to ensure that that happens.
It was never a requirement that the owners of the application should be able to administer this themselves. It's certainly an option, but it's so much more complex than the solution outlined above that I think one should start by making a return-on-investment analysis.
Another option: Calculate the holidays! I think you might find som useful code in my HolidaysAPI. It is even in F#, and the Web project is based upon a course or blog post by you.
Alf, thank you for writing. Apart from the alternative library, how is that different from the option I covered under the heading Option: Nager.Date?
Great post, thanks. The minor point here is that it is probably not so effective to do Contains over long[]. I'd consider using something that can check the value existance faster.
EQR, thank you for writing. The performance profile of the implementation wasn't my main concern with this article, so it's likely that it can be improved.
I did do some lackadaisical performance testing, but didn't detect any measurable difference between the implementation shown here, and one using a HashSet. On the other hand, there are other options I didn't try at all. One of these could be to perform a binary search, since the array is already ordered.
Hi Mark, thanks for this post. One small note on your selection of bank holidays. Liberation day is an official bank holiday, but only for civil servants, for us 'normal' people this only happens every 5 years. This is reflected in Nager.Date wrong here
Thomas, thank you for writing. That just goes to show, I think, that holiday calculation is as complicated as any other 'business logic'. It should be treated as such, and not as an algorithmic calculation.
A reusable ApiController Adapter
Refactor ApiController Adapters to a single, reusable base class.
Regular readers of this blog will know that I write many RESTful APIs in F#, using ASP.NET Web API. Since I like to write functional F#, but ASP.NET Web API is an object-oriented framework, I prefer to escape the object-oriented framework as soon as possible. (In general, it makes good architectural sense to write most of your code as framework-independent as possible.)
(Regular readers will also have seen the above paragraph before.)
To bridge the gap between the object-oriented framework and my functional code, I implement Controller classes like this:
type CreditCardController (imp) = inherit ApiController () member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult = match imp portalId req with | Success (resp : PaymentDtr) -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _
The above example is a Controller that handles incoming payment data. It immediately delegates all work to an injected imp
function, and pattern matches on the return value in order to return correct responses.
This CreditCardController
extends ApiController in order to work nicely with ASP.NET Web API, while the injected imp
function is written using functional programming. If you want to be charitable, you could say that the Controller is an Adapter between ASP.NET Web API and the functional F# API that actually implements the service. If you want to be cynical, you could also call it an anti-corruption layer.
This works well, but tends to become repetitive:
open System open System.Web.Http type BoundaryFailure = | RouteFailure | ValidationFailure of string | IntegrationFailure of string type HomeController (imp) = inherit ApiController () [<AllowAnonymous>] member this.Get () : IHttpActionResult = match imp () with | Success (resp : HomeDtr) -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _ type CreditCardController (imp) = inherit ApiController () member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult = match imp portalId req with | Success (resp : PaymentDtr) -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _ type CreditCardRecurrentStartController (imp) = inherit ApiController () member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult = match imp portalId req with | Success (resp : PaymentDtr) -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _ type CreditCardRecurrentController (imp) = inherit ApiController () member this.Post (portalId : string, transactionKey : string, req : PaymentDtr) : IHttpActionResult = match imp portalId transactionKey req with | Success (resp : PaymentDtr) -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _ type PushController (imp) = inherit ApiController () member this.Post (portalId : string, req : PushRequestDtr) : IHttpActionResult = match imp portalId req with | Success () -> this.Ok () :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _
At this point in our code base, we had five Controllers, and they all had similar implementations. They all pass their input parameters to their injected imp
functions, and they all pattern match in exactly the same way. There are, however, small variations. Notice that one of the Controllers expose an HTTP GET operation, whereas the other four expose a POST operation. Conceivably, there could also be Controllers that allow both GET and POST, and so on, but this isn't the case here.
The parameter list for each of the action methods also vary. Some take two arguments, one takes three, and the GET method takes none.
Another variation is that HomeController.Get
is annotated with an [<AllowAnonymous>]
attribute, while the other action methods aren't.
Despite these variations, it's possible to make the code less repetitive.
You'd think you could easily refactor the code by turning the identical pattern matches into a reusable function. Unfortunately, it's not so simple, because the methods used to return HTTP responses are all protected
(to use a C# term for something that F# doesn't even have). You can call this.Ok ()
or this.NotFound ()
from a derived class, but not from a 'free' let
-bound function.
After much trial and error, I finally arrived at this reusable base class:
type private Imp<'inp, 'out> = 'inp -> Result<'out, BoundaryFailure> [<AbstractClass>] type FunctionController () = inherit ApiController () // Imp<'a,'b> -> 'a -> IHttpActionResult member this.Execute imp req : IHttpActionResult = match imp req with | Success resp -> this.Ok resp :> _ | Failure RouteFailure -> this.NotFound () :> _ | Failure (ValidationFailure msg) -> this.BadRequest msg :> _ | Failure (IntegrationFailure msg) -> this.InternalServerError (InvalidOperationException msg) :> _
It's been more than a decade, I think, since I last used inheritance to enable reuse, but in this case I could find no other way because of the design of ApiController. It gets the job done, though:
type HomeController (imp : Imp<_, HomeDtr>) = inherit FunctionController () [<AllowAnonymous>] member this.Get () = this.Execute imp () type CreditCardController (imp : Imp<_, PaymentDtr>) = inherit FunctionController () member this.Post (portalId : string, req : PaymentDtr) = this.Execute imp (portalId, req) type CreditCardRecurrentStartController (imp : Imp<_, PaymentDtr>) = inherit FunctionController () member this.Post (portalId : string, req : PaymentDtr) = this.Execute imp (portalId, req) type CreditCardRecurrentController (imp : Imp<_, PaymentDtr>) = inherit FunctionController () member this.Post (portalId : string, transactionKey : string, req : PaymentDtr) = this.Execute imp (portalId, transactionKey, req) type PushController (imp : Imp<_, unit>) = inherit FunctionController () member this.Post (portalId : string, req : PushRequestDtr) = this.Execute imp (portalId, req)
Notice that all five Controllers now derive from FunctionController
instead of ApiController
. The HomeController.Get
method is still annotated with the [<AllowAnonymous>]
attribute, and it's still a GET operation, whereas all the other methods still implement POST operations. You'll also notice that the various Post
methods have retained their varying number of parameters.
In order to make this work, I had to make one trivial change: previously, all the imp
functions were curried, but that doesn't fit into a single reusable base implementation. If you consider the type of FunctionController.Execute
, you can see that the imp
function is expected to take a single input value of the type 'a
(and return a value of the type Result<'b, BoundaryFailure>
). Since any imp
function can only take a single input value, I had to uncurry them all. You can see that now all the Post
methods pass their input parameters as a single tuple to their injected imp
function.
You may be wondering about the Imp<'inp, 'out>
type alias. It's not strictly necessary, but helps keep the code clear. As I've attempted to indicate with the code comment above the Execute
method, it's generic. When used in a derived Controller, the compiler can infer the type of 'a
because it already knows the types of the input parameters. For example, in CreditCardController.Post
, the input parameters are already annotated as string
and PaymentDtr
, so the compiler can easily infer the type of (portalId, req)
.
On the other hand, the compiler can't infer the type of 'b
, because that value doesn't relate to IHttpActionResult
. To help the compiler, I introduced the Imp
type, because it enabled me to concisely annotate the type of the output value, while using a wild-card type for the input type.
I wouldn't mind getting rid of Controllers altogether, but this is as close as I've been able to get with ASP.NET Web API.
Comments
I find a few issues with your blog post. Technically, I don't think anything you write in it is wrong, but there are a few of the statements I find problematic in the sense that people will probably read the blog post and conclude that "this is the only solution because of X" when in fact X is trivial to circumvent.
The main issue I have with your post is the following:
You'd think you could easily refactor the code by turning the identical pattern matches into a reusable function. Unfortunately, it's not so simple, because the methods used to return HTTP responses are all protected
(to use a C# term for something that F# doesn't even have). You can call this.Ok ()
or this.NotFound ()
from a derived class, but not from a 'free' let-bound function.
As stated earlier, there is nothing in your statement that is technically wrong, but I'd argue that you've reached the wrong conclusion due to lack of data. Or in this case familiarity with Web API and it's source code. If you go look at the source for ApiController
, you will notice that while true, the methods are protected
, they are also trivial one-liners (helper methods) calling public APIs. It is completely possible to create a helper ala:
// NOTE: Untested let handle (ctrl: ApiController) = function | Success of result -> OkNegotiatedContentResult (result, ctrl) | Failure RouteFailure -> NotFoundResult (ctrl) | Failure (ValidationFailure msg) -> BadRequestErrorMessageResult (msg, ctrl) | Failure (IntegrationFailure msg) -> ExceptionResult ((InvalidOperationException msg), ctrl)
Another thing you can do is implement IHttpActionResult on your discriminate union, so you can just return it directly from you controller, in which case you'd end up with code like this:
member this.Post (portalId : string, req : PaymentDtr) = imp portalId req
It's definitely a bit more work, but in no way is it undoable. Thirdly, Web API (and especially the new MVC Core which has taken over for it) is incredibly pluggable through it's DI. You don't have to use the base ApiController
class if you don't want to. You can override the resolution logic, the handing of return types, routing, you name it. It should for instance be entirely doable to write a handler for "controllers" that look like this:
[<Controller>] module MyController = [<AllowAnonymous>] // assume impl is imported and in scope here - this controller has no DI let post (portalId : string) (req : PaymentDtr) = impl portalId req
This however would probably be a bit of work requiring quite a bit of reflection voodoo. But somebody could definitely do it and put it in a library, and it would be nuget-installable for all.
Anyways, I hope this shows you that there are more options to solve the problem. And who knows, you may have considered all of them and concluded them unviable. I've personally dug through the source code of both MVC and Web API a few times which is why I know a bunch of this stuff, and I just figured you might want to know some of it too :).
Aleksander, thank you for writing. I clearly have some scars from working with ASP.NET Web API since version 0.6 (or some value in that neighbourhood). In general, if a method is defined on ApiController
, I've learned the hard way that such methods tend to be tightly coupled to the Controller. Often, such methods even look into the untyped context dictionary in the request message object, so I've gotten used to use them whenever they're present.
These scars have prevented me from pushing the envelope on the 'new' methods that return various IHttpActionResult
objects, but as you write, they truly are thin wrappers over constructors.
This does, indeed, enable us to write the mapping of result values as a let-bound function. Thank you for pointing that out!
Mine ended up looking like the following. We've added a few more success and error cases since I originally wrote this article, so it looks more complex than the initial example.
let toHttpResult (controller : ApiController) result : IHttpActionResult = match result with | Success (OK resp) -> OkNegotiatedContentResult (resp, controller) :> _ | Success (Created (resp, location)) -> CreatedNegotiatedContentResult (location, resp, controller) :> _ | Failure RouteFailure -> NotFoundResult controller :> _ | Failure (ValidationFailure msg) -> BadRequestErrorMessageResult (msg, controller) :> _ | Failure (IntegrationFailure msg) -> let resp = controller.Request.CreateErrorResponse ( HttpStatusCode.InternalServerError, msg) ResponseMessageResult resp :> _ | Failure StabilityFailure -> let resp = new HttpResponseMessage (HttpStatusCode.ServiceUnavailable) resp.Headers.RetryAfter <- RetryConditionHeaderValue (TimeSpan.FromMinutes 5.) ResponseMessageResult resp :> _
Letting my BoundaryFailure
discriminated union implement IHttpActionResult
sounds promising, but how would that be possible?
The interface has a single method with the type CancellationToken -> Task<HttpResponseMessage>
. In order to create e.g. NotFoundResult
, you need either an HttpRequestMessage
or an ApiController
, and none of those are available when IHttpActionResult.ExecuteAsync
is invoked.
When it comes to not having to derive from ApiController
, I'm aware that even on the full .NET framework (we're not on .NET Core), 'all' the framework needs are IHttpController
instances. As you imply, plugging into the framework is likely to be non-trivial, and so far, I haven't found that such an effort would be warranted. I might use it if someone else wrote it, though :)
Dependency rejection
In functional programming, the notion of dependencies must be rejected. Instead, applications should be composed from pure and impure functions.
This is the third article in a small article series called from dependency injection to dependency rejection. In the previous article in the series, you learned that dependency injection can't be functional, because it makes everything impure. In this article, you'll see what to do instead.
Indirect input and output #
One of the first concepts you learned when you learned to program was that units of operation (functions, methods, procedures) take input and produce output. Input is in the form of input parameters, and output is in the form of return values. (Sometimes, though, a method returns nothing, but we know from category theory that nothing is also a value (called unit).)
In addition to such input and output, a unit with dependencies also take indirect input, and produce indirect output:
When a unit queries a dependency for data, the data returned from the dependency is indirect input. In the restaurant reservation example used in this article series, when tryAccept
calls readReservations
, the returned reservations are indirect input.
Likewise, when a unit invokes a dependency, all arguments passed to that dependency constitute indirect output. In the example, when tryAccept
calls createReservation
, the reservation value it uses as input argument to that function call becomes output. The intent, in this case, is to save the reservation in a database.
From indirect output to direct output #
Instead of producing indirect output, you can refactor functions to produce direct output.
Such a refactoring is often problematic in mainstream object-oriented languages like C# and Java, because you wish to control the circumstances in which the indirect output must be produced. Indirect output often implies side-effects, but perhaps the side-effect must only happen when certain conditions are fulfilled. In the restaurant reservation example, the desired side-effect is to add a reservation to a database, but this must only happen when the restaurant has sufficient remaining capacity to serve the requested number of people. Since languages like C# and Java are statement-based, it can be difficult to separate the decision from the action.
In expression-based languages like F# and Haskell, it's trivial to decouple decisions from effects.
In the previous article, you saw a version of tryAccept
with this signature:
// int -> (DateTimeOffset -> Reservation list) -> (Reservation -> int) -> Reservation // -> int option
The second function argument, with the type Reservation -> int
, produces indirect output. The Reservation
value is the output. The function even violates Command Query Separation and returns the database ID of the added reservation, so that's additional indirect input. The overall function returns int option
: the database ID if the reservation was added, and None
if it wasn't.
Refactoring the indirect output to direct output is easy, then: just remove the createReservation
function and return the Reservation
value instead:
// int -> (DateTimeOffset -> Reservation list) -> Reservation -> Reservation option let tryAccept capacity readReservations reservation = let reservedSeats = readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity) if reservedSeats + reservation.Quantity <= capacity then { reservation with IsAccepted = true } |> Some else None
Notice that this refactored version of tryAccept
returns a Reservation option
value. The implication is that the reservation was accepted if the return value is a Some
case, and rejected if the value is None
. The decision is embedded in the value, but decoupled from the side-effect of writing to the database.
This function clearly never writes to the database, so at the boundary of your application, you'll have to connect the decision to the effect. To keep the example consistent with the previous article, you can do this in a tryAcceptComposition
function, like this:
// Reservation -> int option let tryAcceptComposition reservation = reservation |> tryAccept 10 (DB.readReservations connectionString) |> Option.map (DB.createReservation connectionString)
Notice that the type of tryAcceptComposition
remains Reservation -> int option
. This is a true refactoring. The overall API remains the same, as does the behaviour. The reservation is added to the database only if there's sufficient remaining capacity, and in that case, the ID of the reservation is returned.
From indirect input to direct input #
Just as you can refactor from indirect output to direct output can you refactor from indirect input to direct input.
Again, in statement-based languages like C# and Java, this may be problematic, because you may wish to defer a query, or base it on a decision inside the unit. In expression-based languages you can decouple decisions from effects, and deferred execution can always be done by lazy evaluation, if that's required. In the case of the current example, however, the refactoring is easy:
// int -> Reservation list -> Reservation -> Reservation option let tryAccept capacity reservations reservation = let reservedSeats = reservations |> List.sumBy (fun x -> x.Quantity) if reservedSeats + reservation.Quantity <= capacity then { reservation with IsAccepted = true } |> Some else None
Instead of calling a (potentially impure) function, this version of tryAccept
takes a list of existing reservations as input. It still sums over all the quantities, and the rest of the code is the same as before.
Obviously, the list of existing reservations must come from somewhere, like a database, so tryAcceptComposition
will still have to take care of that:
// ('a -> 'b -> 'c) -> 'b -> 'a -> 'c let flip f x y = f y x // Reservation -> int option let tryAcceptComposition reservation = reservation.Date |> DB.readReservations connectionString |> flip (tryAccept 10) reservation |> Option.map (DB.createReservation connectionString)
The type and behaviour of this composition is still the same as before, but the data flow is different. First, the function queries the database, which is an impure operation. Then, it pipes the resulting list of reservations to tryAccept
, which is now a pure function. It returns a Reservation option
that's finally mapped to another impure operation, which writes the reservation to the database if the reservation was accepted.
You'll notice that I also added a flip
function in order to make the composition more concise, but I could also have used a lambda expression when invoking tryAccept
. The flip
function is a part of Haskell's standard library, but isn't in F#'s core library. It's not crucial to the example, though.
Evaluation #
Did you notice that in the previous diagram, above, all arrows between the unit and its dependencies were gone? This means that the unit no longer has any dependencies:
Dependencies are, by their nature, impure, and since pure functions can't call impure functions, functional programming must reject the notion of dependencies. Pure functions can't depend on impure functions.
Instead, pure functions must take direct input and produce direct output, and the impure boundary of an application must compose impure and pure functions together in order to achieve the desired behaviour.
In the previous article, you saw how Haskell can be used to evaluate whether or not an implementation is functional. You can port the above F# code to Haskell to verify that this is the case.
tryAccept :: Int -> [Reservation] -> Reservation -> Maybe Reservation tryAccept capacity reservations reservation = let reservedSeats = sum $ map quantity reservations in if reservedSeats + quantity reservation <= capacity then Just $ reservation { isAccepted = True } else Nothing
This version of tryAccept
is pure, and compiles, but as you learned in the previous article, that's not the crucial question. The question is whether the composition compiles?
tryAcceptComposition :: Reservation -> IO (Maybe Int) tryAcceptComposition reservation = runMaybeT $ liftIO (DB.readReservations connectionString $ date reservation) >>= MaybeT . return . flip (tryAccept 10) reservation >>= liftIO . DB.createReservation connectionString
This version of tryAcceptComposition
compiles, and works as desired. The code exhibits a common pattern for Haskell: First, gather data from impure sources. Second, pass pure data to pure functions. Third, take the pure output from the pure functions, and do something impure with it.
It's like a sandwich, with the best parts in the middle, and some necessary stuff surrounding it.
Summary #
Dependencies are, by nature, impure. They're either non-deterministic, have side-effects, or both. Pure functions can't call impure functions (because that would make them impure as well), so pure functions can't have dependencies. Functional programming must reject the notion of dependencies.
Obviously, software is only useful with impure behaviour, so instead of injecting dependencies, functional programs must be composed in impure contexts. Impure functions can call pure functions, so at the boundary, an application must gather impure data, and use it to call pure functions. This automatically leads to the ports and adapters architecture.
This style of programming is surprisingly often possible, but it's not a universal solution; other alternatives exist.
Comments
Hi, Thank you for this blog post series. I also read your other posts on ports and adapters and the proposed architecture makes sense in terms of how it works, but I struggle to see the benefit in a real world application. Maybe let me explain my question with a quick example.
In the 2nd blog post of this series you demonstrated this function:
// int -> (DateTimeOffset -> Reservation list) -> (Reservation -> int) -> Reservation
// -> int option
let tryAccept capacity readReservations createReservation reservation =
let reservedSeats =
readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity)
if reservedSeats + reservation.Quantity <= capacity
then createReservation { reservation with IsAccepted = true } |> Some
else None
If I understand it correctly this function is pure if readReservations
and createReservation
are both pure otherwise it is impure.
I also understand the benefit of having a pure function, because it is a lot easier to understand the code, test the code and reason about it. That makes sense as well :).
So in the 3rd blog post you make tryAccept
a pure function, by removing the function dependencies and replacing it with simple values:
// int -> Reservation list -> Reservation -> Reservation option
let tryAccept capacity reservations reservation =
let reservedSeats = reservations |> List.sumBy (fun x -> x.Quantity)
if reservedSeats + reservation.Quantity <= capacity
then { reservation with IsAccepted = true } |> Some
else None
However this was only possible because you essentially moved the impure code into another new function:
// Reservation -> int option
let tryAcceptComposition reservation =
reservation.Date
|> DB.readReservations connectionString
|> flip (tryAccept 10) reservation
|> Option.map (DB.createReservation connectionString)
So after all the application hasn't really reduced the total number of impure functions (still 3 in each case - readReservations
, createReservation
and tryAccept[Composition]
).
The only difference I see is that one impure function has been refactored into 2 functions - one pure and one impure. Considering that the original tryAccept
function was already fully testable from a unit testing point of view and quite readable what is the benefit of this additional step? I would almost argue that the original tryAccept
function was even easier to read/understand than the combination of tryAccept
and tryAcceptComposition
. I understand that impure functions like this are not truly functional, but in a real world application you must have some impure functions and I would like to better understand where trade-off benefit of that additional step is? Am I missing something else?
Dustin, thank you for writing. There are several answers to your question, depending on the perspective one is interested in. I'll see if I can cover the most important ones.
Is it functional? #
On the most fundamental level, I'm interested in learning functional programming. In order to do this, I seek out strictly functional solutions to problems. Haskell is a great help in that endeavour, because it's not a hybrid language. It only allows you to do functional programming.
Does it make sense to back-port Haskell solutions to F#, then? That depends on what one is trying to accomplish, but if the goal is nothing but learning how to do it functionally, then that goal is accomplished.
Toy examples #
On another level, the example I've presented here is obviously nothing but a toy example. It's simplified, because if I presented readers with a more realistic example, the complexity of the real problem could easily drown out the message of the example. Additionally, most readers would probably give up reading.
I'm asking my readers to pretend that the problem is more complex than the one I present here; pretend that this problem is a stand-in for a harder problem.
In this particular context, there could be all sorts of complications:
- Reservations could be for time slots instead of whole dates. In order to keep the example simple, I treat each reservation as simply blocking out an entire date. I once dined at a restaurant where they started serving at 19:00, and if you weren't there on time, you'd miss the first courses. Most restaurants, though, allow you to make reservations for a particular time, and many have more than one serving on a single evening.
- Most restaurants have tables, not seats. Again, the same restaurant I mentioned above seated 12 people at a bar-like arrangement facing the kitchen, but most restaurants have tables of varying sizes. If they get a reservation for three people, they may have to reserve a table for four.
- Perhaps the restaurant would like to implement a feature where, if it receives a reservation that doesn't fill out a table (like a reservation for three people, and only four-people tables are left), it'd defer the decision to see if a 'better' reservation arrives later.
- Some people make reservations, but never show up. For that reason, a restaurant may want to allow a degree of overbooking, just like airlines. How much overbooking to allow is a business decision.
- A further wrinkle on the overbooking business rule is that you may have a different overbooking policy for Fridays than for, say, Wednesdays.
- Perhaps the restaurant would like to implement a waiting-list feature as well.
Separation of concerns #
In my experience, there's an entire category of software defects that occur because of state mutation in business logic. You could have an area of your code that calls other code, which calls other code, and so on, for several levels of nesting. Somewhere, deep in the bowels of such a system, a conditional statement flips a boolean flag that consequently impact how the rest of the program runs. I've seen plenty of examples of such software, and it's inhumane; it doesn't fit within human cognitive limits.
Code that allows arbitrary side-effects is difficult to reason about.
Knowing that an subgraph of your call tree is pure reduces defects like that. This is nothing but another way to restate the command-query separation principle. In F#, we still can't be sure unless we exert some discipline, but in Haskell, all it takes is a look at the type of a function or value. If it doesn't include IO
, you know that it's pure.
Separating pure code from impure code is separation of concern. Business logic is one concern, and I/O is another concern, and the better you can separate these, the fewer sources of defects you'll have. True, I haven't reduced the amount of code by much, but I've separated concerns by separating the code that contains (side) effects from the pure code.
Testability #
It's true that the partial application version of tryAccept
is testable, because it has isolation, but the tests are more complicated than they have to be:
[<Property(QuietOnSuccess = true)>] let ``tryAccept behaves correctly when it can accept`` (NonNegativeInt excessCapacity) (expected : int) = Tuple2.curry id <!> Gen.reservation <*> Gen.listOf Gen.reservation |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) -> let capacity = excessCapacity + (reservations |> List.sumBy (fun x -> x.Quantity)) + reservation.Quantity let readReservations = ((=!) reservation.Date) >>! reservations let createReservation = ((=!) { reservation with IsAccepted = true }) >>! expected let actual = tryAccept capacity readReservations createReservation reservation Some expected =! actual [<Property(QuietOnSuccess = true)>] let ``tryAccept behaves correctly when it can't accept`` (PositiveInt lackingCapacity) = Tuple2.curry id <!> Gen.reservation <*> Gen.listOf Gen.reservation |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) -> let capacity = (reservations |> List.sumBy (fun x -> x.Quantity)) - lackingCapacity let readReservations _ = reservations let createReservation _ = failwith "Mock shouldn't be called." let actual = tryAccept capacity readReservations createReservation reservation None =! actual
(You can find these tests in commit d2387cceb81eabc349a63ab7df1249236e9b1d13 in the accompanying sample code repository.) Contrast those dependency-injection style tests to these tests against the pure version of tryAccept
:
[<Property(QuietOnSuccess = true)>] let ``tryAccept behaves correctly when it can accept`` (NonNegativeInt excessCapacity) = Tuple2.curry id <!> Gen.reservation <*> Gen.listOf Gen.reservation |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) -> let capacity = excessCapacity + (reservations |> List.sumBy (fun x -> x.Quantity)) + reservation.Quantity let actual = tryAccept capacity reservations reservation Some { reservation with IsAccepted = true } =! actual [<Property(QuietOnSuccess = true)>] let ``tryAccept behaves correctly when it can't accept`` (PositiveInt lackingCapacity) = Tuple2.curry id <!> Gen.reservation <*> Gen.listOf Gen.reservation |> Arb.fromGen |> Prop.forAll <| fun (reservation, reservations) -> let capacity = (reservations |> List.sumBy (fun x -> x.Quantity)) - lackingCapacity let actual = tryAccept capacity reservations reservation None =! actual
They're simpler, and since they don't use mocks, they're more robust. They were easier to write, and I subscribe to the spirit of GOOS: if test are difficult to write, the system under test should be simplified.
Hi Mark,
Thanks for your talk at NDC last month, and for writing this series! I feel that the functional community (myself included) has a habit of using examples that aren't obviously relevant to the sort of line-of-business programming most of us do in our day jobs, so articles like this are sorely needed.
We talked a little about this in person after your talk at the conference: I wanted to highlight a potential criticism of this style of programming. Namely, there's still some important business logic being carried out by your tryAcceptComposition
function, like checking the capacity on the requested reservation date. How do you unit test that readReservations
is called with the correct date? Likewise, how do you unit test that rejected reservations don't get saved? Real world business logic isn't always purely functional in nature. Sometimes the side effects that your code performs are part of the requirements.
The Haskell philosophy isn't about rejecting side effects outright - it's about measuring and controlling them. I wouldn't write tryAcceptComposition
using IO
. Instead I'd program to the interface, not the implementation, using an mtl-style class to abstract over monads which support saving and loading reservations.
class Monad m => MonadReservation m where
readReservations :: ConnectionString -> Date -> m [Reservation]
createReservation :: ConnectionString -> Reservation -> m ReservationId
tryAcceptComposition :: MonadReservation m => Reservation -> m (Maybe ReservationId)
tryAcceptComposition r = runMaybeT $ do
reservations <- lift $ readReservations connectionString (date r)
accepted <- MaybeT $ return $ tryAccept 10 reservations r
lift $ createReservation connectionString accepted
Code that lives in a MonadReservation
context can read and create reservations in the database but nothing else; it doesn't have all the power of IO
. During unit testing I can use an instance of MonadReservation
that returns canned values, and in production I can use a monad that actually talks to the database.
Since type classes are syntactic sugar for passing an argument, this is really just a nicer way of writing your original DI-style code. I don't advocate the "free monad" style that's presently trendy in Scala-land because I find it unnecessarily complex. 90% of the purported advantages of free monads are already supported by simpler language features.
I suppose the main downside of this design is that you can't express it in F#, at least not cleanly. It relies on type classes and higher-kinded types.
Hope you find this interesting, I'd love to hear what you think!
Benjamin
Benjamin, thank you for writing. The alternative you propose looks useful in Haskell, but, as you've already suggested, it doesn't translate well into F#.
I write F# code professionally, whereas so far, I've only used Haskell to critique my F# code. (If someone who reads this comment would offer to pay me to write some Haskell code, please get in touch.) In other words, I still have much to learn about Haskell. I think I understand as much, however, that I'd be able to use your suggested design to unit test tryAcceptComposition
using the Identity
monad for Stubs, or perhaps MonadWriter
or MonadState
for Mocks. I'll have to try that one day...
In F#, I write integration tests. Such tests are important regardless, and often they more closely relate to actual requirements, so I find this a worthwhile effort anyway.
Hi Mark,
thanks for the post series, which I find interesting and needed. There is one part of your post that I find deserves further exploration. You write:
in statement-based languages like C# and Java, this may be problematic, because you may wish to defer a query, or base it on a decision inside the unit. In expression-based languages you can decouple decisions from effects, and deferred execution can always be done by lazy evaluation, if that's required.Firstly, I would say that you can write expression-based programs in any language that has expressions, which naturally includes C# and Java. But that's not particularly relevant to this discussion.
More to the point, you're glossing over this as though it were a minor detail, when in fact I don't think it is. Let's explore the case in which "you may wish to defer a query, or base it on a decision inside the unit". The way you do this "by lazy evaluation" would be - I assume - by passing a function as an argument to your unit. But this is then effectively dependency injection, because you're passing in a function which has side effects, which will be called (or not) from the unit.
So, it seems to me that your technique of extracting side effects out of the unit provides a good general guideline, but not a completely general way to replace dependency injection.
Enrico, thank you for writing. There's a lot to unpack in that quote, which was one of the reasons I didn't expand it. It would have made the article too long, and wandered off compared to its main point. I don't mind going into these details here, though.
Direction of data #
In order to get the obvious out of the way first, the issue you point out is with my refactoring of indirect input to direct input. Refactoring from indirect to output to direct output is, as far as I can tell, not on your agenda. Designing with direct input in mind seems uncontroversial to me, so that makes sense.
No hard rules #
On this blog, I often write articles as I figure out how to deal with problems. Sometimes, I report on my discoveries at a time where I've yet to accumulate years of experience. What I've learned so far is that dependency injection isn't functional. What I'm still exploring is what to do instead.
It's my experience that the type of refactoring I demonstrate here can surprisingly often be performed. I don't want to claim that it's always possible to do it like this. In fact, I'm still looking for good examples where this will not be possible. Whenever I think of a simple enough example that I could share it here, I always realise that if only I simplify the problem, I can put it into the shape seen here.
My thinking is, however, constrained by my professional experience. I've been doing web (service) development for so many years now that it constraints my imagination. When you execution scope is exclusively a single HTTP request at a time, you tend to keep things simple. I'd welcome a simplified, but still concrete example where the impure/pure/impure sandwich described here isn't going to be possible.
This may seem like a digression, but my point is that I don't claim to be the holder of a single, undeniable truth. Still, I find that this article describes a broadly applicable design and implementation technique.
Language specifics #
The next topic we need to consider is our choice of language. When I wrote that deferred execution can always be done by lazy evaluation, that's exactly how Haskell works. Haskell is lazily evaluated, so any value passed as direct input can be unevaluated until required. That goes for IO
as well, but then, as we've learned, you can't pass impure data to a pure function.
All execution is, in that sense, deferred, unless explicitly forced. Thus, any potential need for deferred execution has no design implications.
F#, on the other hand, is an eagerly evaluated language, so there, deferred execution may have design implications.
Performance #
Perhaps it's my lack of imagination again, but I can't think of a well-designed system where deferred execution is required for purposes of correctness. As far as I can tell, deferred execution is a performance concern. You wish to defer execution of a query because that operation takes significant time.
That's a real concern, but I often find that people worry too much about performance. Again, this is probably my lack of wider experience, as I realise that performance can be important in smart phone apps, games, and the like. Clearly, performance is also important in the world of REST APIs, but I've met a lot of people who worry about performance without ever measuring it.
When you start measuring performance, you'll often be surprised to discover where your code spends actual time. So my design approach is always to prioritise making the system work first, and then, if there are performance problems, figure out how to tweak it so that it becomes satisfactory. In my experience, such tweaking is only necessary now and then. I'm not claiming that my code is the fastest it could be, but it's often fast enough, and as easy to maintain as I can make it.
The need for data #
Another concern is the need for data. If you consider the above tryAccept
function, it always uses reservations
. Thus, there's no gain in deferring the database query, because you'll always need the data.
Deferred execution is only required in those cases where you have conditional branching, and only in certain cases do you need to read a particular piece of data.
Even conditional branching isn't enough of a criterion, though, because you could have branching where, in 99.9 % of the cases, you'd be performing the query anyway. Would you, then, need deferred execution for the remaining 0.1 % of the cases?
Lazy sequences #
Still, let's assume that we've implemented a system using pure functions that take pure data, but to our dismay we discover that there's one query that takes time to execute, and that we truly only need it some of the time. In .NET, there are two distinct situations:
- We need a scalar value
- We need a collection of values
IEnumerable<T>
in C#) as input. These can be implemented as lazily evaluated sequences, which gives us the deferred execution we need.
Lazy scalar values #
This leaves the corner case where we need a lazily evaluated scalar value. In such cases, I may have to make a concession to performance in my function design, but I wouldn't change the argument to a function, but rather to a lazy value.
Lazy values are deferred, but memoised, which is the reason I'd prefer them over function arguments.
In a previous comment, you said:
I'd welcome a simplified, but still concrete example where the impure/pure/impure sandwich described here isn't going to be possible.
I have on two occasions stumbled into cases where I can't find a good way to pull this off. The reason may be that there's a workflow seemingly consisting of several impure steps interleaved with pure decisions. The cases are similar, so I will share one of them as an example.
We have an API for allowing users to register. We also have a separate API for two-factor authentication (2FA). When users register, they have to complete a "proof" using the 2FA API to verify ownership of their mobile number.
The 2FA API has two relevant endpoints used internally by our other APIs: One is used for creating a proof, and returns a proof ID that can be passed on to the API client. This endpoint is used when a client makes a request without a proof ID, or with an invalid proof ID. The other endpoint is used for verifying that a proof has been completed. This endpoint is used when a client supplies a proof ID in the request.
(The 2FA API also has endpoints the client uses to complete the proof, but that is not relevant here.)
When a user registers, this is the workflow:
Here is a simple implementation of the workflow using "dependency injection" (I will skip the actual composition function, similar to your tryAcceptComposition
, which is not interesting here):
let completeRegistrationWorkflow (createProof: Mobile -> Async<ProofId>) (verifyProof: Mobile -> ProofId -> Async<bool>) (completeRegistration: Registration -> Async<unit>) (proofId: ProofId option) (registration: Registration) : Async<CompleteRegistrationResult> = async { match proofId with | None -> let! proofId = createProof registration.Mobile return ProofRequired proofId | Some proofId -> let! isValid = verifyProof registration.Mobile proofId if isValid then do! completeRegistration registration return RegistrationCompleted else let! proofId = createProof registration.Mobile return ProofRequired proofId }
There are two decisions that are pure and could conceivably be extracted to a pure (non-DI) function, indicated by blue in the flowchart:
- Initially: Should we create a new proof or verify an existing proof? (based on whether the client supplied a proof ID)
- After verifying a supplied proof: Should we complete the registration or create a new proof? (based on whether the supplied proof ID was valid)
My question then, is this: Is it possible to refactor this to direct input/output, in a way that actually reduces complexity where it matters? (In other words, in a way where the decisions mentioned above are part of a pure, non-DI function?)
Otherwise, I just want to say that this series has helped me become a better F# programmer. Since originally reading it, I have consistently tried to design for direct input/output as opposed to using "dependency injection", and my code has become better for it. Thanks!
Christer, thank you for writing. That was such a fruitful question that I wrote a new article in order to answer it. I hope you find it useful, but if not, let's continue the discussion there or here, dependening on what makes most sense.
Hi Mark, excelent post series, thank you. Right after finishing this post I had the same questions Dustin Moris Gorski had and I think I still have some, even after your answer.
For simplicity's sake, I'll represent TryAccept as 3 operations: read, calculate and maybe create. Both read and create are injected so that I can change their implementations at runtime, if necessary. That is the same exact representation for both the C# and F# codes that use DI, and also the tryAcceptComposition code, with the difference that tryAcceptComposition depends on the actual implementation of DB, so it's relatively more brittle than the DI alternatives.
Although TryAccept and TryAcceptComposition are doing the same thing, I'm still trying to think why the latter looks more functional than the first and whether DI is really not necessary. I got to 2 conclusions and I wanted to know your opinion about them:
1) The difference between the 2 implementations (TryAccept and TryAcceptComposition) is that the first is following an imperative style, while the second is purely declaritive, with one big composition. Both implementations perform the exact same operations, evoking the same dependencies, but with different code styles.
2) If we try and take your style of sandwich to extremes and push dependencies to the very edges of the application, to the point where it's just a simple big composition with dependencies at the beginning and end, we my replace "injection" with import statements, importing our dependencies in the same place the composition is written. I don't think this would work in every scenario as operations in the middle of this composition would need access to dependencies too (which could them be pushed out like TryAccept, but could make the code less readable). Do you think this is doable?
Danilo, thank you for writing. Regarding your first point, I'm not sure I follow. Both functions are written in F#, which is an expression-based language (at least when you ignore the interop parts). Imperative code is usually defined as coding with assignments rather than expressions. There's no assignments here.
Also, tryAccept
and tryAcceptComposition
aren't equivalent. One is a specialisation of the other, so to speak. You can't change the behaviour of tryAcceptComposition
unless you edit the actual code. You can, on the other hand, change the observable behaviour of tryAccept
by composing it with different impure actions.
As to your second observation:
"I don't think this would work in every scenario as operations in the middle of this composition would need access to dependencies too"I'm still keen on getting some compelling examples of this. Christer van der Meeren kindly tried to supply such an example, but it turned out as another fine sandwich. This happens conspicuously often.
I've never claimed that the sandwich architecture is always possible, but every time I try to find a compelling counter-example, it usually turns out to be refactorable into a sandwich after all. Do, however, refer to the comments to that later article.
Hi Mark, I found myself thinking about this early this morning and wondering about scenarios where injecting dependencies may still be "functional" (i.e. pure) and would like your input. It also incorporates TDD (or at least testing strategies).
Here's my example: I was recently refactoring some code that was structured like this (C#-ish pseudo-code):
if (shouldNotify(record, supportingRecord, settings)) { generateEmail(...); record.status = calculateStatusAfterEmail(record, supportingRecord); } else { record.status = calculateStatusWithoutEmail(record, supportingRecord); }Let's gloss over the side-effects all over the place here and just focus on the flow.
To ease unit testing, I extracted these private methods into a separate interface and am injecting it into this flow. Call me lazy, but I wanted to just mock/stub the results of these pure calls when testing the overall flow and wrote separate tests on the implementation of the new interface that could focus on all the different combinations of values. Is that a strategy you would support/recommend, or would you hesitate to extract pure functions from an implementation just to ease testing? I also convinced myself that I could justify this on the grounds of following the Single Responsibility Principle (i.e. this flow should not need to be modified if the status calculation logic changes).
Your thoughts? By the way, your new dependency injection strategies is on its way to me, perhaps that book will provide me with your views on that, but I couldn't resist posting it here.
Sven, thank you for writing. I think that your question deserves a longer answer than what you'll get here. It's a topic that for some time I've been contemplating giving a more detailed treatment. It's a rich enough subject that a couple of articles would be required. I'm still pondering how to organise such content.
The short answer is that I'm increasingly looking for alternatives to the sort of interaction-based testing you imply. I think that my article series on the benefits of state-based testing outlines most of my current thinking on the topic.
I read over all the comments and I would like clarification on when logic is required for resolving the dependency being provided. Benjamin Hodgson asked this:
Namely, there's still some important business logic being carried out by your tryAcceptComposition function, like checking the capacity on the requested reservation date. How do you unit test that readReservations is called with the correct date?
Suppose the readRervations didn't just get all reservations from the dabatase just to process for one restaurant or on one day. Thus the restaurant ID and date may be needed to fetch the correct reservations, and this logic would have to happen outside of the unit. I think you touched on this complication in a response above, but I'd like to confirm if I am understanding your responses correctly: you simply perform this logic outside of the unit and have an integration test for the entire workflow?
Chris, thank you for writing. There are more nuances to that question than might be immediately apparent. At least, I'll answer the question in several ways.
First, I'd like to challenge the implicit assumption that one must always test everything. What to test and what not to test depend on multiple independent forces, such as how costly an error might be, how likely an error is to occur, and so on.
Take, as an example, the code base that accompanies my book Code That Fits in Your Head. This code base is also a restaurant reservation system, but has a realistic level of complexity, so I think it might better highlight the sort of considerations that might be warranted. The code that is most relevant to the question is this snippet from ReservationsController.TryCreate
:
var reservations = await Repository .ReadReservations(restaurant.Id, reservation.At) .ConfigureAwait(false); var now = Clock.GetCurrentDateTime(); if (!restaurant.MaitreD.WillAccept(now, reservations, reservation)) return NoTables500InternalServerError(); await Repository.Create(restaurant.Id, reservation) .ConfigureAwait(false);
How likely is it that the code changes in such a way that it calls ReadReservations
with the wrong input? Either because the original programmer who wrote the code made a mistake, or because a later developer introduced a change in which this code calls ReadReservations
with incorrect input?
Now, such errors certainly do occur. Programmers are human, and to err is human.
Still, it's important to keep in mind that the risk of introducing an error into a code base is proportional to the amount of code. The more code, the more errors you'll have. This goes for test code as well. You can also, inadvertently, introduce errors into a test.
You can counteract some of that by writing the test first. That's the reasons it's so important to see a test fail. Only by seeing it fail can you feel confident that it verifies something real.
So, depending on circumstances, it may be relevant and important to write a test that verifies that ReadReservations
is called with the correct arguments. Still, I think it's important to weigh advantages and disadvantages before blindly insisting that it's necessary to protect against such a contingency.
When I originally wrote the above code, I wrote no unit test that explicitly verify whether ReadReservations
is called with the correct arguments. After all, in that context, there's only one DateTime
value in scope: reservation.At
. (This is not entirely true, because now
is also a DateTime
value, but as the code is written, it's not available for ReadReservations
because it's retrieved after the ReadReservations
call. But see below.)
In a context like this, you'd have to go out of your way to call ReadReservations
with the wrong date. You could create a value on the spot, like this:
var reservations = await Repository .ReadReservations(restaurant.Id, new DateTime(2021, 12, 19, 18, 30, 0)) .ConfigureAwait(false);
or this:
var reservations = await Repository .ReadReservations(restaurant.Id, reservation.At.AddDays(1)) .ConfigureAwait(false);
None of these are impossible, but I do consider them to be conspicuous. You don't easily make those kinds of mistakes. You'd almost have to go out of your way to make these mistakes, and a good pairing partner or code review ought to catch such a mistake.
Another option is if someone finds a good reason to reorder the code:
var now = Clock.GetCurrentDateTime(); var reservations = await Repository .ReadReservations(restaurant.Id, now) .ConfigureAwait(false);
In this example, now
is available to ReadReservations
, and perhaps a later edit might introduce the error of calling it with now
.
This is perhaps a more realistic, honest mistake.
None of the 171 unit tests in the code base catch any of the above three mistakes.
Is the code base unsafe, then?
No, because as luck would have it, an integration test (NoOverbookingRace
) fails on any of these three edits. I admit that this is just a fortunate accident. I added that particular integration test for another reason: to reproduce an unrelated bug that occurred in 'production'.
I usually try to base the amount of testing I ask from a team on the quality of the team. The more I trust that they pair-program or perform regular code reviews, the less I insist on test coverage, and vice versa.
In summary, though, if I find that it's important to verify that ReadReservations
was called with the correct arguments, I'd favour a state-based integration test.
Partial application is dependency injection
The equivalent of dependency injection in F# is partial function application, but it isn't functional.
This is the second article in a small article series called from dependency injection to dependency rejection.
People often ask me how to do dependency injection in F#. That's only natural, since I wrote Dependency Injection in .NET some years ago, and also since I've increasingly focused my energy on F# and other functional programming languages.
Over the years, I've seen other F# experts respond to that question, and often, the answer is that partial function application is the F# way to do dependency injection. For some years, I believed that as well. It turns out to be true in one sense, but incorrect in another. Partial application is equivalent to dependency injection. It's just not a functional solution to dealing with dependencies.
(To be as clear as I can be: I'm not claiming that partial application isn't functional. What I claim is that partial application used for dependency injection isn't functional.)
Attempted dependency injection using functions #
Returning to the example from the previous article, you could try to rewrite MaîtreD.TryAccept
as a function:
// int -> (DateTimeOffset -> Reservation list) -> (Reservation -> int) -> Reservation // -> int option let tryAccept capacity readReservations createReservation reservation = let reservedSeats = readReservations reservation.Date |> List.sumBy (fun x -> x.Quantity) if reservedSeats + reservation.Quantity <= capacity then createReservation { reservation with IsAccepted = true } |> Some else None
You could imagine that this tryAccept
function is part of a module called MaîtreD
, just to keep the examples as equivalent as possible.
The function takes four arguments. The first is the capacity of the restaurant in question; a primitive integer. The next two arguments, readReservations
and createReservation
fill the role of the injected IReservationsRepository
in the previous article. In the object-oriented example, the TryAccept
method used two methods on the repository: ReadReservations
and Create
. Instead of using an interface, in the F# function, I make the function take two independent functions. They have (almost) the same types as their C# counterparts.
The first three arguments correspond to the injected dependencies in the previous MaîtreD
class. The fourth argument is a Reservation
value, which corresponds to the input to the previous TryAccept
method.
Instead of returning a nullable integer, this F# version returns an int option
.
The implementation is also equivalent to the C# example: Read the relevant reservations from the database using the readReservations
function argument, and sum over their quantities. Based on the number of already reserved seats, decide whether or not to accept the reservation. If you can accept the reservation, set IsAccepted
to true
, call the createReservation
function argument, and pipe the returned ID (integer) to Some
. If you can't accept the reservation, then return None
.
Notice that the first three arguments are 'dependencies', whereas the last argument is the 'actual input', if you will. This means that you can use partial function application to compose this function.
Application #
If you recall the definition of the previous IMaîtreD
interface, the TryAccept
method was defined like this (C# code snippet):
int? TryAccept(Reservation reservation);
You could attempt to define a similar function with the type Reservation -> int option
. Normally, you'd want to do this closer to the boundary of the application, but the following example demonstrates how to 'inject' real database operations into the function.
Imagine that you have a DB
module with these functions:
module DB = // string -> DateTimeOffset -> Reservation list let readReservations connectionString date = // .. // string -> Reservation -> int let createReservation connectionString reservation = // ..
The readReservations
function takes a connection string and a date as arguments, and returns a list of reservations for that date. The createReservation
function also takes a connection string, as well as a reservation. When invoked, it creates a new record for the reservation and returns the ID of the newly created row. (This sort of API violates CQS, so you should consider alternatives.)
If you partially apply these functions with a valid connection string, both have the type desired for their roles in tryAccept
. This means that you can create a function from these elements:
// Reservation -> int option let tryAcceptComposition = let read = DB.readReservations connectionString let create = DB.createReservation connectionString tryAccept 10 read create
Notice how tryAccept
itself is partially applied. Only the arguments corresponding to the C# dependencies are passed to it, so the return value is a function that 'waits' for the last argument: the reservation. As I've attempted to indicate by the code comment above the function, it has the desired type of Reservation -> int option
.
Equivalence #
Partial application used like this is equivalent to dependency injection. To see how, consider the generated Intermediate Language (IL).
F# is a .NET language, so it compiles to IL. You can decompile that IL to C# to get a sense of what's going on. If you do that with the above tryAcceptComposition
, you get something like this:
internal class tryAcceptComposition@17 : FSharpFunc<Reservation, FSharpOption<int>> { public int capacity; public FSharpFunc<Reservation, int> createReservation; public FSharpFunc<DateTimeOffset, FSharpList<Reservation>> readReservations; internal tryAcceptComposition@17( int capacity, FSharpFunc<DateTimeOffset, FSharpList<Reservation>> readReservations, FSharpFunc<Reservation, int> createReservation) { this.capacity = capacity; this.readReservations = readReservations; this.createReservation = createReservation; } public override FSharpOption<int> Invoke(Reservation reservation) { return MaîtreD.tryAccept<int>( this.capacity, this.readReservations, this.createReservation, reservation); } }
I've cleaned it up a bit, mostly by removing all attributes from the various elements. Notice how this is a class, with class fields, and a constructor that takes values for the fields and assigns them. It's constructor injection!
Partial application is dependency injection.
It compiles, works as expected, but is it functional?
Evaluation #
People sometimes ask me: How do I know whether my F# code is functional?
I sometimes wonder about that myself, but unfortunately, as nice a language as F# is, it doesn't offer much help in that regard. Its emphasis is on functional programming, but it allows mutation, object-oriented programming, and even procedural programming. It's a friendly and forgiving language. (This also makes it a great 'beginner' functional language, because you can learn functional concepts piecemeal.)
Haskell, on the other hand, is a strictly functional language. In Haskell, you can only write your code in the functional way.
Fortunately, F# and Haskell are similar enough that it's easy to port F# code to Haskell, as long as the F# code already is 'sufficiently functional'. In order to evaluate if my F# code is properly functional, I sometimes port it to Haskell. If I can get it to compile and run in Haskell, I take that as confirmation that my code is functional.
I've previously shown an example similar to this one, but I'll repeat the experiment here. Will porting tryAccept
and tryAcceptComposition
to Haskell work?
It's easy to port tryAccept
:
tryAccept :: Int -> (ZonedTime -> [Reservation]) -> (Reservation -> Int) -> Reservation -> Maybe Int tryAccept capacity readReservations createReservation reservation = let reservedSeats = sum $ map quantity $ readReservations $ date reservation in if reservedSeats + quantity reservation <= capacity then Just $ createReservation $ reservation { isAccepted = True } else Nothing
Clearly, there are differences, but I'm sure that you can also see the similarities. The most important feature of this function is that it's pure. All Haskell functions are pure by default, unless explicitly declared to be impure, and that's not the case here. This function is pure, and so are both readReservations
and createReservation
.
The Haskell version of tryAccept
compiles, but what about tryAcceptComposition
?
Like the F# code, the experiment is to see if it's possible to 'inject' functions that actually operate against a database. Equivalent to the F# example, imagine that you have this DB
module:
readReservations :: ConnectionString -> ZonedTime -> IO [Reservation] readReservations connectionString date = -- .. createReservation :: ConnectionString -> Reservation -> IO Int createReservation connectionString reservation = -- ..
Database operations are, by definition, impure, and Haskell admirably models that with the type system. Notice how both functions return IO
values.
If you partially apply both functions with a valid connection string, the IO
context remains. The type of DB.readReservations connectionString
is ZonedTime -> IO [Reservation]
, and the type of DB.createReservation connectionString
is Reservation -> IO Int
. You can try to pass them to tryAccept
, but the types don't match:
tryAcceptComposition :: Reservation -> IO (Maybe Int) tryAcceptComposition reservation = let read = DB.readReservations connectionString create = DB.createReservation connectionString in tryAccept 10 read create reservation
This doesn't compile.
It doesn't compile, because the database operations are impure, and tryAccept
wants pure functions.
In short, partial application used for dependency injection isn't functional.
Summary #
Partial application in F# can be used to achieve a result equivalent to dependency injection. It compiles and works as expected, but it's not functional. The reason it's not functional is that (most) dependencies are, by their very nature, impure. They're either non-deterministic, have side-effects, or both, and that's often the underlying reason that they are factored into dependencies in the first place.
Pure functions, however, can't call impure functions. If they could, they would become impure themselves. This rule is enforced by Haskell, but not by F#.
When you inject impure operations into an F# function, that function becomes impure as well. Dependency injection makes everything impure, which explains why it isn't functional.
Functional programming solves the problem of decoupling (side) effects from program logic another way. That's the topic of the next article.
Next: Dependency rejection.
Comments
A couple of questions: If you're porting your code from F# to Haskell and back into F#, why not just use Haskell in the first place?
Also, why would you want to mark a function as having side effects in the first place?
Thanks
Kurren, thank you for writing. Why not use Haskell in the first place? In some situations, if that's an option, I'd go for it. In most of my professional work, however, it's not. A bit of background is required, I think. I've spent most of my professional career working with Microsoft technologies. Most of my clients use .NET. The majority of my clients use C#, but some are interested in adopting functional programming. F# is a great bridge to functional programming, because it integrates so well with existing C# code.
Perhaps most importantly is that while these organisations learn F# as a new programming language, and a new paradigm, all other dimensions of software development remain the same. You can use familiar libraries and frameworks, you can use the same Continuous Integration and build tools as you've been used to, you can deploy like you always do, and you can operate and monitor your software like before.
If you start using Haskell, not only will developers have to learn an entire new ecosystem, but if you have a separate operations team, they would have be on board with that as well.
If you can't get the entire organisation to accept Haskell-based software, then F# is a great choice for a .NET shop. Developers will obviously know the difference, but the rest of the organisation can be oblivious to the choice of language.
When it comes to side-effects, that's one of the main motivations behind pure code in the first place. A major source of bugs is that often, programs have unintended side-effects. It's easy to introduce defects in a code base when side-effects are uncontrolled. That's the case for languages like C# and Java, and, unfortunately, F# as well. When you look at a method or function, there's no easy way to determine whether it has side-effects. In other words: all methods or functions could have side effects. (Also: all methods could return null.)
This slows you down when you work on an existing code base, because there's only one way to determine if side-effects or non-deterministic behaviour are part of the code you're about to call: you have to read it all.
On the other hand, if you can tell, just by looking at a function's type, whether or not it has side-effects, you can save yourself a lot of time. By definition, all the pure functions have no side-effect. You don't need to read the code in order to detect that.
In Haskell, this is guaranteed. In F#, you can design your system that way, but some discipline is required in order to uphold such a guarantee.
Dependency injection is passing an argument
Is dependency injection really just passing an argument? A brief review.
This is the first article in a small article series called from dependency injection to dependency rejection.
In a talk at the 2012 Northeast Scala Symposium, Rúnar Bjarnason casually remarked that dependency injection is "really just a pretentious way to say 'taking an argument'". Given that I've written a 500+ pages book about dependency injection, you might expect me to disagree with that. Yet, there's some truth to that statement, although it's not quite as simple as that.
In this article, I'll show you some simple examples and explain why, on the one hand, Rúnar Bjarnason is right, but also, on the other hand, why there's a bit more to it.
Restaurant reservation example #
Like the other articles in this series, the example scenario is on-line restaurant reservation. Imagine that you've been asked to develop an HTTP-based API that accepts JSON documents containing restaurant reservations. Furthermore, assume that you're using ASP.NET Web API with C# for the job, and that you're aspiring to use domain-driven design.
In order to handle the incoming POST request, you could write an action method like this:
public IHttpActionResult Post(ReservationRequestDto dto) { var validationMsg = validator.Validate(dto); if (validationMsg != "") return this.BadRequest(validationMsg); var r = mapper.Map(dto); var id = maîtreD.TryAccept(r); if (id == null) return this.StatusCode(HttpStatusCode.Forbidden); return this.Ok(); }
This method follows a simple and familiar path: validate input, map to a domain model, delegate to said model, examine posterior state, and return a result.
You may have noticed, though, that this method doesn't do all the work itself. It delegates some of the work to collaborators: validator
, mapper
, and maîtreD
. Where do these collaborators come from?
They are dependencies. Could you make the Post
method take them as arguments?
Unfortunately, you can't. The Post
method constitutes part of the boundary of the HTTP API. ASP NET Web API routes and dispatches incoming HTTP requests by convention, and action methods must follow that convention. You can't just make the function take any argument you'd like, so you have to find another place to pass those dependencies to the object.
The second-best option (after the Post
method itself) is via the constructor:
public ReservationsController( IValidator validator, IMapper mapper, IMaîtreD maîtreD) { this.validator = validator; this.mapper = mapper; this.maîtreD = maîtreD; }
This is the application of a design pattern called constructor injection. It captures the dependencies in class fields, making them available for members (like Post
) of the class.
This turns out to be a regular pattern.
Turtles all the way down #
You could argue that the Post
method is a special case, since it's part of the boundary of the system, and therefore must adhere to specific rules. On the other hand, these rule don't apply deeper in the implementation, so could you implement other objects by simply passing in dependencies as arguments?
Consider, as an example, the implementation of IMaîtreD.TryAccept
:
public int? TryAccept(Reservation reservation) { var reservedSeats = reservationsRepository .ReadReservations(reservation.Date) .Sum(r => r.Quantity); if (reservedSeats + reservation.Quantity <= capacity) { reservation.IsAccepted = true; return reservationsRepository.Create(reservation); } return null; }
This method has another collaborator: reservationsRepository
. It's another dependency. Where does it come from?
Could you make the TryAccept
method take reservationsRepository
as an argument?
Unfortunately, that's not possible either, because the method is defined by the IMaîtreD
interface:
public interface IMaîtreD { int? TryAccept(Reservation reservation); }
You may recall that the above Post
method is programmed against the IMaîtreD
interface, and not the concrete class. It'd be a leaky abstraction to add IReservationsRepository
as an argument to IMaîtreD.TryAccept
, because not all implementations of the interface may need that dependency. Or perhaps another implementation has another dependency. Should we add that to the parameter list of IMaîtreD.TryAccept
as well?
Surely, that's not a tenable design principle. On the other hand, by using constructor injection, you can decouple implementation details from your abstractions:
public MaîtreD(int capacity, IReservationsRepository reservationsRepository) { this.capacity = capacity; this.reservationsRepository = reservationsRepository; }
This constructor not only takes an IReservationsRepository
object, but also an integer that represents the capacity of the restaurant in question. This demonstrates that dependencies can also be primitive values.
Summary #
Dependency injection is, in a sense, only a specific way for objects to take arguments. Often, however, objects have roles defined by the interfaces they implement. Such objects may need collaborators that are not available via the APIs defined by these interfaces, so you'll have to supply dependencies via members that belong to the concrete class in question. Passing dependencies via a class' constructor is the best way to do that.
Comments
Hey Mark. You may want to update the article slightly to cover the use of the [FromServices]
attribute, which allows you to inject any service into a controller action as method-level injection. The main article on it is this one.
I'm personally not a fan of using it, but it is an option that transforms standard injection into parameter-passing for controllers.
Juliano, thank you for writing. I wasn't aware of that particular capability, so thank you for bringing it to my attention.
My article attempts to explain a general software design problem, as well as a possible solution. While it uses ASP.NET as an example context, the main point is independent of any particular framework, or language, for that matter.
From dependency injection to dependency rejection
The problem typically solved by dependency injection in object-oriented programming is solved in a completely different way in functional programming.
Several years ago, I wrote a book called Dependency Injection in .NET, which was published in 2011. The book contains examples in C#, but since then I've increasingly become interested in functional programming to the extend that I now consider F# my primary language.
With that combination, it's no wonder that people often ask me how to do dependency injection in functional programming.
I've seen more than one answer, from other people, explaining how partial function application is equivalent to dependency injection. In a small series of articles, I'll explain both why this is true, but also why it's not functional. I'll conclude by showing a functional alternative to decoupling logic and (side) effects.
(Comic courtesy of John Muellerleile and Igal Tabachnik.)
There's another school of functional programmers who believe that dependency injection in functional programming involves a Free monad.
You can often make do with less, though.
In my experience, it's usually enough to refactor a unit to take only direct input and output, and then compose an impure/pure/impure 'sandwich'. You'll see an example later.
This article series contains the following parts:
- Dependency injection is passing an argument
- Partial application is dependency injection
- Dependency rejection
- Pure interactions
The scenario is to implement an HTTP-based API that can accept incoming JSON documents that represent restaurant reservations.
The fourth article on pure interactions is a gateway to another article series on free monads.
I should point out that nowhere in this article series do I reject dependency injection as a set of object-oriented patterns. In object-oriented programming, dependency injection is a well-known and comprehensively described way to achieve decoupling and testability. In the next article, you'll see a brief review of dependency injection in C#.
Decoupling application errors from domain models
How to prevent application-specific error cases from infecting your domain models.
Functional error-handling is often done with the Either monad. If all is good, the right case is returned, but if things go wrong, you'll want to return a value that indicates the error. In an application, you'll often need to be able to distinguish between different kinds of errors.
From application errors to HTTP responses #
When an application encounters an error, it should respond appropriately. A GUI-based application should inform the user about the error, a batch job should log it, and a REST API should return the appropriate HTTP status code.
Regular readers of this blog will know that I write many RESTful APIs in F#, using ASP.NET Web API. Since I like to write functional F#, but ASP.NET Web API is an object-oriented framework, I prefer to escape the object-oriented framework as soon as possible. (In general, it makes good architectural sense to write most of your code as framework-independent as possible.)
In my Test-Driven Development with F# Pluralsight course (a free, condensed version is also available), I demonstrate how to handle various error cases in a Controller class:
type ReservationsController (imp) = inherit ApiController () member this.Post (dtr : ReservationDtr) : IHttpActionResult = match imp dtr with | Failure (ValidationError msg) -> this.BadRequest msg :> _ | Failure CapacityExceeded -> this.StatusCode HttpStatusCode.Forbidden :> _ | Success () -> this.Ok () :> _
The injected imp
function is a complete, composed, vertical feature implementation that performs both input validation, business logic, and data access. If input validation fails, it'll return Failure (ValidationError msg)
, and that value is translated to a 400 Bad Request
response. Likewise, if the business logic returns Failure CapacityExceeded
, the response becomes 403 Forbidden
, and a success is returned as 200 OK
.
Both ValidationError
and CapacityExceeded
are cases of an Error
type. This is only a simple example, so these are the only cases defined by that type:
type Error = | ValidationError of string | CapacityExceeded
This seems reasonable, but there's a problem.
Error infection #
In F#, a function can't use a type unless that type is already defined. This is a problem because the Error
type defined above mixes different concerns. If you seek to make illegal states unrepresentable, it follows that validation is not a concern in your domain model. Validation is still important at the boundary of an application, so you can't just ignore it. The ValidationError
case relates to the application boundary, while CapacityExceeded
relates to the domain model.
Still, when implementing your domain model, you may want to return a CapacityExceeded
value from time to time:
// int -> int -> Reservation -> Result<Reservation,Error> let checkCapacity capacity reservedSeats reservation = if capacity < reservation.Quantity + reservedSeats then Failure CapacityExceeded else Success reservation
Notice how the return type of this function is Result<Reservation,Error>
. In order to be able to implement your domain model, you've now pulled in the Error
type, which also defines the ValidationError
case. Your domain model is now polluted by an application boundary concern.
I think many developers would consider this trivial, but in my experience, failure to manage dependencies is the dominant reason for code rot. It makes the code less general, and less reusable, because it's now coupled to something that may not fit into a different context.
Particularly, the situation in the example looks like this:
Boundary and data access modules depend on the domain model, as they should, but everything depends on the Error
type. This is wrong. Modules or libraries should be able to define their own error types.
The Error
type belongs in the Composition Root, but it's impossible to put it there because F# prevents circular dependencies (a treasured language feature).
Fortunately, the fix is straightforward.
Mapped Either values #
A domain model should be self-contained. As Robert C. Martin puts it in APPP:
Abstractions should not depend upon details. Details should depend upon abstractions.Your domain model is an abstraction of the real world (that's why it's called a model), and is the reason you're developing a piece of software in the first place. So start with the domain model:
type BookingError = CapacityExceeded // int -> int -> Reservation -> Result<Reservation,BookingError> let checkCapacity capacity reservedSeats reservation = if capacity < reservation.Quantity + reservedSeats then Failure CapacityExceeded else Success reservation
In this example, there's only a single type of domain error (CapacityExceeded
), but that's mostly because this is an example. Real production code could define a domain error union with several cases. The crux of the matter is that BookingError
isn't infected with irrelevant implementation details like validation error types.
You're still going to need an exhaustive discriminated union to model all possible error cases for your particular application, but that type belongs in the Composition Root. Accordingly, you also need a way to return validation errors in your validation module. Often, a string
is all you need:
// ReservationDtr -> Result<Reservation,string> let validateReservation (dtr : ReservationDtr) = match dtr.Date |> DateTimeOffset.TryParse with | (true, date) -> Success { Reservation.Date = date Name = dtr.Name Email = dtr.Email Quantity = dtr.Quantity } | _ -> Failure "Invalid date."
The validateReservation
function returns a Reservation
value when validation succeeds, and a simple string
with an error message if it fails.
You could, conceivably, return string
values for errors from many different places in your code, so you're going to map them into an appropriate error case that makes sense in your application.
In this particular example, the Controller shown above should still look like this:
type Error = | ValidationError of string | DomainError type ReservationsController (imp) = inherit ApiController () member this.Post (dtr : ReservationDtr) : IHttpActionResult = match imp dtr with | Failure (ValidationError msg) -> this.BadRequest msg :> _ | Failure DomainError -> this.StatusCode HttpStatusCode.Forbidden :> _ | Success () -> this.Ok () :> _
Notice how similar this is to the initial example. The important difference, however, is that Error
is defined in the same module that also implements ReservationsController
. This is part of the composition of the specific application.
In order to make that work, you're going to need to map from one failure type to another. This is trivial to do with an extra function belonging to your Result (or Either) module:
// ('a -> 'b) -> Result<'c,'a> -> Result<'c,'b> let mapFailure f x = match x with | Success succ -> Success succ | Failure fail -> Failure (f fail)
This function takes any Result
value and maps the failure case instead of the success case. It enables you to transform e.g. a BookingError
into a DomainError
:
let imp candidate = either { let! r = validateReservation candidate |> mapFailure ValidationError let i = SqlGateway.getReservedSeats connectionString r.Date let! r = checkCapacity 10 i r |> mapFailure (fun _ -> DomainError) return SqlGateway.saveReservation connectionString r }
This composition is a variation of the composition I've previously published. The only difference is that the error cases are now mapped into the application-specific Error
type.
Conclusion #
Errors can occur in diverse places in your code base: when validating input, when making business decisions, when writing to, or reading from, databases, and so on.
When you use the Either monad for error handling, in a strongly typed language like F#, you'll need to define a discriminated union that models all the error cases you care about in the specific application. You can map module-specific error types into such a comprehensive error type using a function like mapFailure
. In Haskell, it would be the first
function of the Bifunctor
typeclass, so this is a well-known function.
Comments
Mark,
Why is it a problem to use HttpStatusCode
in the domain model. They appear to be a standard way of categorizing errors.
David, thank you for writing. The answer depends on your goals and definition of domain model.
I usually think of domain models in terms of separation of concerns. The purpose of a domain model is to model the business logic, and as Martin Fowler writes in PoEAA about the Domain Model pattern, "you'll want the minimum of coupling from the Domain Model to other layers in the system. You'll notice that a guiding force of many layering patterns is to keep as few dependencies as possible between the domain model and the other parts of the system."
In other words, you're separating the concern of implementing the business rules from the concerns of being able to save data in a database, render it on a screen, send emails, and so on. While also important, these are separate concerns, and I want to be able to vary those independently.
People often hear statements like that as though I want to reserve myself the right to replace my SQL Server database with Neo4J (more on that later, though!). That's actually not my main goal, but I find that if concerns are mixed, all change becomes harder. It becomes more difficult to change how data is saved in a database, and it becomes harder to change business rules.
The Dependency Inversion Principle tries to address such problems by advising that abstractions shouldn't depend on implementation details, but instead, implementation details should depend on abstractions.
This is where the goals come in. I find Robert C. Martin's definition of software architecture helpful. Paraphrased from memory, he defines a software architect's role as enabling change; not predicting change, but making sure that when change has to happen, it's as economical as possible.
As an architect, one of the heuristics I use is that I try to imagine how easily I can replace one component with another. It's not that I really believe that I may have to replace the SQL Server database with Neo4J, but thinking about how hard it would be gives me some insights about how to structure a software solution.
I also imagine what it'd be like to port an application to another environment. Can I port my web site's business rules to a batch job? Can I port my desktop client to a smart phone app? Again, it's not that I necessarily predict that I'll have to do this, but it tells me something about the degrees of freedom offered by the architecture.
If not explicitly addressed, the opposite of freedom tends to happen. In APPP, Robert C. Martin describes a number of design smells, one of them Immobility: "A design is immobile when it contains parts that could be useful in other systems, but the effort and risk involved with separating those parts from the original system are too great. This is an unfortunate, but very common occurrence."
Almost as side-effect, an immobile system is difficult to test. A unit test is a different environment than the intended environment. Well-architected systems are easy to unit test.
HTTP is a communications protocol. Its purpose is to enable exchange of information over networks. While it does that well, it's specifically concerned with that purpose. This includes HTTP status code.
If you use the heuristic of imagining that you'd have to move the heart of your application to a batch job, status codes like 301 Moved Permanently
, 404 Not Found
, or 405 Method Not Allowed
make little sense.
Using HTTP status codes in a domain model couples the model to a particular environment, at least conceptually. It has little to do with the ubiquitous language that Eric Evans discusses in DDD.
From REST to algebraic data
Mapping RESTful HTTP requests to values of algebraic data types is easy.
In previous articles, you've seen how to easily model a simple domain model with algebraic data types, and how to use RESTful API design to surface such a model at the boundary of an application. In this article, you'll see how trivial it is to map incoming HTTP requests back to values of algebraic data types.
The advantage of REST is that you can make illegal states unrepresentable. Clients follow links, and while clients are supposed to treat links as opaque values, URLs still contain information your API can use.
Routing and dispatching #
Continuing where the previous article left off, clients can issue POST requests against a URL like https://example.com/credit-card
. On the server, a well-known piece of code handles such requests. (In the example code base I've used so far, I've been using ASP.NET Web API, so the code that handles such a request is a Controller.) Since you know that URLs like that are always routed to that particular piece of code, you can create a new PaymentType
value that specifically represents an individual payment with a credit card:
let paymentType = Individual { Name = "credit-card"; Action = "Pay" }
If, on the other hand, the client is using a provided link to POST a representation against the URL https://example.com/recurrent/start/credit-card
, your server-side dispatcher will route the request to a different handler (Controller), in which case you can create a PaymentType
value like this:
let paymentType = Parent { Name = "credit-card"; Action = "Pay" }
Finally, if the client has already created a parent payment and is now using the resulting link to create child payments, it may be POSTing to a URL like https://example.com/recurrent/42
. Your server-side dispatcher will route that request to a third handler. Most web frameworks, including ASP.NET Web API, will be able to pull values out of URLs. In this case, you can configure it so that it pulls the value 42
out of the URL and binds it to a value called transactionKey
. With this, again it's trivial to create a PaymentType
value:
let paymentType = Child (transactionKey, { Name = "credit-card"; Action = "PayRecurrent" })
Notice that, despite containing different data, and being created three different places in the code base, they all have the same type: PaymentType
. This means that you can pass these values to a common pay
function, which handles the actual communication with the third-party payment service.
Code reuse #
Independent of the route the data arrived at, a central, reusable function named pay
handles all such payments. This is still an impure boundary function that takes various other input apart from PaymentType
. Without going into too much detail, it has a type like Config -> PaymentType -> Result<PaymentDtr,BoundaryFailure>
. Don't worry if some of the details look obscure; the important point is that pay
is a function that takes a PaymentType
value as input. You can visualise the transition from HTTP requests to a function call like this:
The pay
function is composed from various smaller functions, some pure and some impure. Ultimately, it transforms all the input data to the format required by the third-party payment service, and forwards the transaction information. Inside that function you'll find the pattern match that you saw in my previous article.
Summary #
By making good use of routing and dispatching, you can easily map incoming HTTP requests to values of algebraic data types. This enables you to close the loop on exposing your domain model at the boundary of your system. Not only can clients request data from your API in terms of your model, but when clients send data to your API, you can translate that data back to your model.
Comments
There are two minor points I want to address: * I think instead of the recursive search for a nested exception you can use AggregateException.Flatten() |> Seq.exists ...
* And I know that you know that a major difference between
Async
andTask
is that aTask
is typically started whereas anAsync
is not. So it might be irritating that callingexecute
already starts the execution. If you wrapped the body ofexecute
inside anotherasync
block it would be lazy as usual, I think.