Introduction to types – how to program safely from one checkpoint to another

This post is part of the F# Advent Calendar 2019. Check out other posts in this series, under the #fsadvent hashtag, and… happy holidays everybody 🙂

In this post, I would like to share my thoughts about types, more specifically algebraic data types. There can become a powerful weapon in your toolkit if they aren’t already now. They can make a difference in your modelling and make your life easier. From C# 9.0, C# programmers will also be able to use them – finger crossed.

Beginnings…

At the beginning of my F# adventure, I didn’t recognize the importance of types immediately. I thought that functional programming is just about functions, and the functional approach is about composability.

Functions compose because you can simply combine them. You can pass the function; you can return the function, you can wrap a function with another function and so. The simplicity of this approach is you don’t need any other artefact than function to do all these operations. And the same analogy can be applied to algebraic data types.

At a very glance, if you come from the object-oriented world, you probably can think that types are just classes, and nothing is exciting about them. Sometimes you can even feel the burden of creating them.

A functional and object-oriented approach to types

First of all, the meaning of types is different. Types in functional languages imply immutability, and they represent just data. They are means of transport, and with types, you can shape data to your needs. Usually, in the object-oriented paradigm, you are focusing on behaviour – which in functional programming is the equivalent of functions. So in some sense, you don’t think about data at all. We are told that object-oriented world models things as they are. But it mixes two concepts and pushes data to the background. When you try to apply some generalization, you feel a lot of pain.

This reminds me of my 3rd year on university at control theory course, where a professor said something like that:

You learnt all these things which you can apply to linear environments. In real life, there’s no such thing as a linear environment. Some things in a controlled manner behave linearly, and in that context, you can apply your knowledge about linear systems.

That context is a key here. Most of us do not control that context. The requirements of your clients manage the context.

So how exactly can types be your blessing?

First of all, think about types as a set of values, in the end, it’s about just data. We can differentiate two types: 

  • product type (called in F# record type) 
  • sum type (called in F# discrimination type) 

AlgebraicDataTypes

Record type represents values which are demanded and labelled. You can’t miss value – you need to provide all fields, and you need to pass them in a defined order.

Discrimination union type represents variants of models which are labelled. For example: think of shape type. You can have a rectangle, circle, square and so on. Some of these subtypes will not have anything in common, but all of them in real-world we will call a shape.

If product and sum names start to associate to math, point for you. There is a reason why these types are called algebraic. Product type defines AND logic (all fields required), and sum type defines OR logic (alternate between cases). Types create an algebra of your domain.

You can infinitely mix types to produce very complex data. You don’t need to use any special thing to combine them. They are created in a manner to be glued together; demand values or alternate them.

Ok, maybe I understood that but how to apply it to real life?

So the last thing which might not be evident at the beginning is that these types allow us to defined a state machine. We can model states very easily and model input. And with powerful pattern-matching, we will get errors when we don’t handle all cases. This approach makes things easier and safer because, with defined types, you need to define your transformations between states which are your functions. In some sense, you’ve made checkpoints for yourself.

Probably you might think, now I heard about state machine at university a long time ago. I’ve never seen a need for creating one, so why it’s so important.

It’s important, but probably you didn’t get used to data focus approach.

By defining an FSM (Finite State Machine) you make things explicit. And encoding it with algebraic data types is so simple. You probably didn’t do that because you don’t have such a tool in your toolkit to make define finite state machine for free.

State machine makes things more explicit

It’s hard to pick relevant example for everyone which would not simplify too much, but at least I will pick checkout situation, so everyone can have a thought process how would do it in another paradigm.

So let’s see the above situation with the eyes of the consumer. Usually, you go to some website, authenticate yourself to the system, pick items to a basket. When you are happy you go through the checkout process – give any details about your credit card. And when everything is alright – you will get confirmation about your order.

Let’s focus only on shopping and model that situation:

type CartItem = string
type Card = string

type CheckoutState =
    | NoItems
    | HasItems of CartItem list
    | ProvideCard of CartItem list
    | CardSelected of CartItem list * Card
    | CardConfirmed of CartItem list * Card
    | OrderPlaced

So now we can ask ourselves how we will move between states? We need some trigger which will evaluate transition. And this it is what FSM is about.

Let’s consider that definition from Erlang documentation:

A FSM can be described as a set of relations of the form:

State(S) x Event(E) -> Actions (A), State(S’) 

These relations are interpreted as meaning:

If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S’.

From the definition, we can notice that we need to introduce another type, which will be invoked from any other place to make a change into our state. So let’s do it:

type CheckoutEvent = 
    | Select of CartItem
    | Checkout
    | SelectCard of Card
    | Confirm
    | PlaceOrder
    | Cancel

As you can see, our events are not a dummy; they might transport information with them to make a change.

So, in the end, to make it work, we need to write our evaluation function to change the state:

let checkoutFsm state event = 
    match (state,event) with
    | (NoItems, Select item) -> HasItems [item]
    | (HasItems items, Select item) -> HasItems (item::items)
    | (HasItems items, Checkout) -> ProvideCard items
    | (ProvideCard items, SelectCard card) -> CardSelected (items,card)
    | (CardSelected (items, card), Confirm) -> CardConfirmed (items, card)
    | (CardConfirmed _, PlaceOrder) -> OrderPlaced
    | (ProvideCard items, Cancel) -> HasItems items
    | (CardSelected (items,_), Cancel) -> HasItems items
    | (CardConfirmed (items,_), Cancel) -> HasItems items
    | _ -> failwith "Something went wrong"

Please notice how happy path is implemented first. State machine gives us an excellent way of understanding what interactions are taking place. To test our code, you can use these snippets:

let replay events =
    (NoItems, events) ||> List.fold checkoutFsm

let happyPathEvents = [
    Select "product1"
    Select "product2"
    Select "product3"
    Checkout
    SelectCard "4572494102173xxx"
    Confirm
    PlaceOrder]

let dataWithCancel = [
    Select "product1"
    Select "product2"
    Select "product3"
    Checkout
    SelectCard "4572494102173xxx"
    Cancel]
printfn "%A" <| replay dataWithCancel //result: `HasItems ["product3"; "product2"; "product1"]`
printfn "%A" <| replay happyPathEvents //result: `OrderPlaced`

It’s just a simple example, where I don’t handle all edge cases. I don’t build a bus to communicate with events and so on. But please notice how fast it is to produce proof of concept. Types had created a nice domain model, which I can show to my business analyst to discuss it. They might notice missing a case, or maybe everything is wrong. But you know what? It’s good because you didn’t need to create abstract proxy adapter or any boilercode to discover that it didn’t have sense. So, in the end, algebraic data types give us a possibility to express our domain in the language of the user.

Summary

I am hoping that I, at least, aroused your curiosity and convinced you to give a go with algebraic data types.

You will be able to find a lot of examples and see the same pattern of FSM to evaluate your code. Interested in logo language? Give a go to fsharp dojo here.

In the end, we can discover that algebraic data types are perfect for making states explicit. They give you more clarity between transition. Otherwise, the scattered state across many mutable variables makes things harder to follow. Moreover, it’s hard to ensure the integrity of state transitions.

Resources to read more about types

uConf 2019 – summary

TL;DR Takeaways

  • Focusing on the business domain can give you a lot of value – even applying one small thing from DDD. You don’t need to understand everything at once, to begin with.
  • Context is the king! Learn Wardley maps to make your discussions more efficient and help you make decisions.
  • Autonomous needs an alignment. Otherwise, it is just chaos.

Context

I was at the conference called uConf, which was about domain-driven design, microservices and software architecture. It was a 3-day conference, so there’s a lot of content which probably I am not able to summarise. So my summarise will not be talk-by-talk, but instead, I will try to present the DDD topic as a holistic view from all talks.

1 What is the domain?

The domain represents part of the process in the system. The whole system can have multiple domains like order-taking, shipping, billing and so on. Usually, the DDD approach is used to express complex problem space. The whole domain should be described with the consistent language used by everyone. Language is specific to the domain. Such a language is called a ubiquitous language.
We can distinguish different subdomain types:

  • generic domain – not valuable from the business perspective. It is not unique to the provided business. It can be easily bought from 3rd parties.
  • supportive domain – important, required from the core perspective, but doesn’t offer a competitive advantage.
  • core domain – critical to the business, because this is what business is trying to sell and solve. This is what a specific company should be good at.

So the core domain is the heart of the thing which you are trying to solve.

2 How to model your domain?

As said above, you tried to discover your domain analysing the process, not entities. The relationships are crucial to an understanding domain, so staring modelling with entities can lead you to the rabbit hole if you start with them prematurely. The most common way to discover the domain and describe the processes is event storming. The event is something that happened in the domain. They are records of something happened, so usually we write them in the past tense. Events become a building block of scenarios, use-cases, business process and workflow.
There exist another approach called temporal modelling, where you try not to model entities, but compose them from events itself. In that case, your entities are a projection of events. You can understand such an entity as an interpretation of events which creates it.
Another important thing while modelling your domain is to discover bounded contexts. DDD deal with large models by splitting it do different bounded contexts. Bounded contexts delimit the applicability of domain models. You can understand the bounded context as an analogy to the animal’s cell. One context/cell doesn’t know about another. It’s called context because each context represents some specialised knowledge in the solution. Within the context, we share the common language. It’s bounded because we want to make it evolving independently from other subsystems.
The helpful hint to detect the “Bounded context.” is to try to find boundaries of the domain. You should make the context boundary explicit because it makes it easier to manage. Bounded context is not equal to a unit of deployment. You can have multiple microservice which will still produce one bounded context.

3 How to find the most important thing?

To identify what is the essential thing in your business Wardley map approach can help you make that decision. First of all, due to Wardley, it is a map, because the place where you put a thing has a meaning. This map is 2D coordinate:

  • The vertical axis shows you how your components are visible to a user (direct need of user).
  • The horizontal one shows the evolution of the component (phase/stage: genesis, custom-built, product + rental, commodity).

So after drawing coordinates you start from the top because you would like to define user needs. You bring components/activities/dependencies, which are the most visible to the user and valuable for the user. Then you draw subcomponents which are required to provide the former functionality, with deciding where to put in the evolution phase.
Such a map gives you an overview of your business, and you can optimise things, by finding a path in the graph to change. You can delegate some components that don’t need to be done by yourself. Wardley claims that there are two `whys` in the business:

  1. why of purpose,
  2. why of movement.

And you can optimise in both ways.

The map doesn’t give you a solution. It just reflects your assumptions about the business. The most valuable thing is to show it to someone else, where the discussion begins that some element maybe is wrongly localised.

Here is visualized introduction to Wardley maps:
https://learnwardleymapping.com/?gclid=EAIaIQobChMIsrDtybmM4wIVFM13Ch0wOQ7KEAAYASAAEgKtjvD_BwE

F# eXchange — 2019 summary

F# eXchange is a 2-day conference in London. I spent there a great time and I can recommend it. Subscriptions for 2020 are already opened!

Where you can use F#?

F# is used in the .NET world and in JavaScript using Fable transcompiler.
F# has its own application development stack called SAFE (Saturn-Azure-Fable-Elmish). It allows the F# developer to write programs for full-stack development. You can use F# across cross-platform in the cloud and make beautiful UI.
F# can improve the work of research engineers. The strong type system helps them with proper modelling and computations.

About writing code

Alfonso (@alfonsogcnunez) had a great talk about communication. He starts from the statement “I am a writer” not an engineer. He referred it to the communication process between the writer and reader of the code.
This communication can happen through 3 main points: semantics, readability and structure.
We tend to go to the extremes which reveal in the tension between economy and expressiveness. We want to express our intention in less code and as much correct as possible. At the same time, making our intention as clear as possible, we tend to produce too much verbose code. Good balance is a key success.
To make the code more readable — we can try to express meaning through some convention or relationship. In his mind formatting is not so much important as some of us think it is. He gave jumbled letters as an example of the human mind to deal with some not well-formatted code. It’s essential to think about code according to the relationships. For example, missing information or redundant information. Both cases are wrong and we should bear them in mind during writing the code.
It’s also important to have a structured code. It makes our communication much clearer. In object-oriented design, the structure can happen through classification (hierarchy of inheritance). In functional programming, it can be harder to achieve. But still possible with proper use of modules and patterns. For example MVU for UI design.
Read more how to organizing modules in F#.

Elmish architecture

Elmish architecture is MVU pattern which stands from Model-View-Update. It solves problems of other MV approaches like MVC, MVVM, MVP. The main advantage of this approach is that it has the ultimate golden source of truth where is the state. The state is in the model and nowhere else!
Other letters stand for 2 functions:

  • view function — to generate virtual UI
  • update function — to make changes to the current model.

Because these functions are pure (they depend only on what was passed in and don’t have side effect) we can notice these benefits:

  • it’s easy to test your view because you test your view function
  • you can forget about UI cross-threads. The update function will change the state of the model and cause a change in UI to depend on it.
  • it’s easy to reproduce a problem if you get your model. Model is enough to reproduce everything.

Elmish owes its name from language ELM language. MVU pattern can be applied anywhere. In the web using Fable. In desktop and mobile apps using the Fabulous framework. The fabulous framework is built on top of Xamarin.

Why use F# in research

Evelina Gabasova (@evelgab) had presented how F# can be useful in research. She defined the skills of a research software engineer as interleaving domains: computer science, math & statistics, business knowledge.
The most important thing for researches is reproducibility. If you can’t reproduce your result your studies your work is pointless.
She presented the projects on which she worked and showed how F# helped her:

  1. “The (mis)informed citizen” — web page to provide human annotations with marking source references in articles. The project has the aim to recognise fake news.
    Because F# can be used to write web it helped her in a short period of time write a robust web page for providing annotations.
  2. “Air traffic simulation” — visualisation of an aircraft simulation (the part written in F#). The main idea to navigate control to avoid loss of separation. Because in aeroplane domain there are a lot of measures like longitude and latitude, meters, and so on. The F# feature “unit of measure” helped her a lot. Unit of measure is an annotation on number type. It corresponds exactly to measures used in the real world. You can’t add different units of measure, because it will give you a compile-time error, for example:
    1<m>+2<ft> //it will not compile
    But you can multiply and divide numbers with measures. As a result, you will get a new measure, for example:
    1<m>/1<s> = 1<m/s>

Why use F# in the cloud?

It’s important to mark that whole Azure is adapted to use F#. There were mentioned two F# features which help in daily work with Azure:
1) Type-provider for Azure storage SDK
All challenges in C# world were opposed to provided type-provider.

You can’t easily explore data. -> You can explore data with type provider. It will give you all relevant suggestions by simply putting a dot near a type. Using it in F# REPL you can invoke queries to check more interesting facts.

Mismatched types. -> It also detects data in a table column to provide you with a proper type. For example optionality of some column.

”String” typed data. -> You can notice failure after 3 steps from the issue origin -> everything is checked in compile time.

”Unusual” API design — not supported exceptions in runtime. -> All relevant suggestions are provided.

Read more about Azure Storage Type Provider
2) Computation-expressions “Orchestrator”
It’s a tool which organizes invocation of stateless independent functions into high-level workflows. It invokes under the hood Azure activities and gathers results and triggers other functions.
Read more here

Summary

I hope that I aroused your curiosity and proved the utility of F#. All videos from the conference are available online on skillsmatters website.
It’s true that it’s not a mainstream language, but currently, none of the functional languages is mainstream. It’s worth learning because it can provide a lot of unique features and can be mixed with C#. It can help in your daily work more often than you would expect.

Presentations used in the article

You need to sign in to skillsmatters website (you can do it for free 🙂 )

DDD Scotland summary

My takeaways from the conference

BigCode transforming

  • The code is a liability, not an asset.
  • Refactoring is expensive.
  • Everybody in a team should be able to draw an architecture of project!
  • Have an architect in a team. Without the architect, it is hard to be consistent with design or you will end up with lack of any design.
  • Don’t afraid to kill code. It’s easy to do when you have decoupled code.
  • We are heading to the services.

Teaching an old dog new tricks – presentation

Beyond C# 7

 

Developing for privacy and data protection

  • https://webdevlaw.uk/data-protection-gdpr/
  • GDPR  applies to all data collected processed and retained about people within the European Union. It concerns personal data and sensitive personal data.
  • This law is extraterritorial – it means that GDPR will apply not only in Europe!
  • In brief: in EU data belongs to the subject.
  • The seven principles of privacy by design:
    • Proactive
    • Default
    • Built into design
    • Positive sum
    • End-to-end
    • Open
    • User-centric
  • You should document your approach to protect the user data
  • If data leaks you will need to prove that you did what you can to protect the user data.

Adding a layer of chocolate(y) – presentation

  • Under the hood, there is PowerShell and NuGet.
  • You can improve default installer by adding in chocolatey for example path to the program in the system variable
  • Chocolatey has the default implementation of uninstalling feature, so even if your installer doesn’t provide uninstall feature, choco will know how to do it.
  • Chocolatey cannot detect reboot requirement
  • ‘choco list -lo’
    • Lists all apps installed by choco
    • Synchronise removed apps from “programs&features”.

DDD Scotland

Hi all, tomorrow is my next DDD conference in the UK (the third one). I arrived today (09/02/2018) around noon and I had an opportunity to visit city :). I hope that I will enjoy it and I create some article from one of the course.

Here are some photos of the city 🙂

Topic which I would like to attend to

9:30 BigCode transforming
10:30 My Maker Journey
11:30 teaching an old dog a new tricks
—- LUNCH
13:30 Beyond C# 7
14:30 How to get hired as a Skyscanner engineer/Levelling up to become a Technical Lead
15:30 Developing for privacy and data protection OR what makes graduate software developer employable?
(I will probably decide the topic on the conference itself)
16:30 Adding a layer in chocolatey

References