Let's look at a function to reserve a book in a library. This function takes a book and returns a BookPlacedOnHold event if reserve is successful.
public BookPlacedOnHoldEvent? PlaceOnHold(Book aBook, HoldDuration duration)
if (PatronCanHold(aBook, duration))
var bookPlacedOnHoldEvent = BookPlacedOnHoldNow(aBook.BookId, aBook.Type, aBook.LibraryBranch, _patron.PatronId, duration);
// or there could be an exception
// return BookHoldFailedException("Book Hold Failed")
Let's reason about it
PlaceOnHold: (Book, HoldDuration) -> BookPlacedOnHold | Null
This function takes a Book and a HoldDuration and returns a BookPlacedOnHold event if successful otherwise returns a null. I don't like returning null from functions. Let's improve this.
We can introduce a event for failure scenario. Let's call it BookHoldFailedEvent. This means, now PlaceOnHoldFunction return two different types.
We can create a base/common class and make both of these events inherit from it. Suppose this baseclass is called BookHoldEvent. With this change our method will look as below.
PlaceOnHold: (Book, HoldDuration) -> BookHoldEvent
BookPlacedOnHold inherit from BookHoldEvent
BookHoldCancelled inherit from BookHoldEvent
Let's try to do this with a functional paradigm. We'll use "either" to model this. With this change our function looks like as below:
PlaceOnHold: (Book, HoldDuration) ->
You can easily see functional representation makes it way more explicit than using inheritance.
can we refactor it further ?
Can any Book be placed on Hold ? No, only book which are available can be placed on hold. So is there any functional pattern that allow us to make things more explicit ?
Yes, we can represent states of types as the type in our system. For instance, a book can be either available or not.
Book = AvailableBook | UnavailableBook
Important thing to note here is that, we should only try to model states which are relevant in our domain.
So, with this change our function would look like below
PlaceOnHold: (AvailableBook, HoldDuration) ->
So, we are now getting compile type safety because PlaceOnHold function only takes AvailableBook as parameter. During the construction of AvailableBook we make sure that only valid object is constructed.