Skip to main content
  1. posts/

Shades of F#

·1140 words·6 mins

I am writing after a long time for F#. And this would be a little bit controversial post. Take it with a pinch of salt.

Recently I got a call from some company who wants me to do some job in F#, for its client. The client took my interview and told that its client wants to do some work in F#. Things were going smoothly until the interviewer asked me that I should be writing F# code following standard and design patterns.

I just went to pause, as my brain trying to search Functional Programming Design Patterns. It gave me Scott Wlaschin’s famous answer. In functional programming every design pattern is function. But can’t give that answer to the client’s operator for sure. So, I gave the other best possible answer.

You are asking about F# code like Python or F# code like Haskell?

The contract didn’t get converted but this post does.

Let’s start with first shade. F# like typed Python. #

If you are old enough to know and write code with Visual Studio 2012 then go back to that time, and try to remember good talks you have seen to learn F#.

Here are few of my favorites

There are common things you can abstract away from it

  • Functions (obviously)
  • Forward Pipe (Best thing in language)
  • Type Providers

There were no Result Type, Elevated world, not even Railways. Even I have to ask on Stack Overflow, just to do multiple validations on an entity.

The language was so simple and beautiful. Tooling was good enough, at least it works most of the time. Don’t crash on my lap just because I have opened any big project. People are way more friendly then compare to now for sure. Good old days.

Python gets traction from the Data Science community because of its simplicity. It is too good to write algorithms, especially when you have definite input and output.

Here, F# just adds more power to this. You have types and type providers, you can write simple functions to process data, join them using forward pipes. It was too much fun then, I tend to convert every damn thing to forward pipe. Overdoing it sometimes but it was fun. This is the F# I loved the most. I still like this kind of programming, no specific types, few record types to define input and output and all the functions. Files and modules are always there to help in case of separation. So, nice and so Python-like.

Let’s see some code. I am demoing famous world bank data example (from their docs only).

#r "../../../bin/lib/net45/FSharp.Data.dll"
#load "../../../packages/test/FSharp.Charting/FSharp.Charting.fsx"
open FSharp.Data
open FSharp.Charting
type WorldBank = WorldBankDataProvider<"World Development Indicators", Asynchronous=true>
let wb = WorldBank.GetDataContext()
let countries =
[| wb.Countries.``Arab World``
wb.Countries.``European Union``
wb.Countries.Australia
wb.Countries.Brazil
wb.Countries.Canada
wb.Countries.Chile
wb.Countries.``Czech Republic``
wb.Countries.Denmark
wb.Countries.France
wb.Countries.Greece
wb.Countries.``Low income``
wb.Countries.``High income``
wb.Countries.``United Kingdom``
wb.Countries.``United States`` |]
[ for c in countries ->
c.Indicators.``Gross capital formation (% of GDP)`` ]
|> Async.Parallel
|> Async.RunSynchronously
|> Array.map Chart.Line
|> Chart.Combine

So, what’s wrong with this approach. Practically nothing, but real-world problems (especially in web, distributed system, mobile, etc) kind of need something more. It needs some more design and architecture to hold big applications together.

But don't we already have design patterns for ages? Can't it be applied to F#.

F# is functional first language running on .Net. And it does support object-oriented programming. Here comes second shade of F#.

F# as better C#. #

If someone likes me coming from C#, this might be a natural transaction. Try to map whatever you learn in C# to F#. I also wrote a couple of articles long back explaining how you can use traditional object-oriented design patterns in F#.

Here is how a typical C# class looks like.

class Person {
private readonly string firstName;
private readonly string lastName;
public Person(string firstName, string lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public string FirstName { get {return this.firstName; } }
public string LastName { get {return this.lastName; } }
private string calculateFullName() {
return this.firstName + " " + this.lastName;
}
public string FullName { get {return this.calculateFullName(); } }
}

and here equivalent beautiful F# code

type Person(firstName:string, lastName: string) =
let firstName = firstName
let lastName = lastName
let calculateFullName() =
firstName + " " + lastName
member x.FirstName with get() = firstName
member x.LastName with get() = lastName
member x.FullName with get() = calculateFullName()

You can make a little bit terser without going more functional

type Person = {
FirstName : string
LastName : string
}
with
member x.FullName() =
x.FirstName + " " + x.LastName

Look how beautiful F# code looks. I did write immutable C# code so I don’t have to write mutable F# code.

Let’s push F# code to the little more functional end. Here we have a small example from Railway Oriented Programming.

We are defining kind of domain with two different styles.

type Email = private | Email of string
module Email =
let create(email : string ) =
// Some validation on email
Email email
let value (Email email) = email

let’s do the same thing with class

type Email private (email : string) =
member __.Email = email
static member create(email : string ) =
// Some validation on email
Email (email)

Pick your choice, both give the almost same result on the consumer side.

But with this, we are moving more towards Haskell. Languages like Haskell, Scala have something called HKT or Higher Kind Types backed into language.

F# as a language by design lacks features like HKT.

For a long time I didn’t know HKT means Higher Kind Types. And the even longer amount of time I took to understand it.

There is a discussion going on how useful it is? So, it depends on choice of elders but F# at the time of writing this blog doesn’t have HKT.

It still doesn’t hold anyone to push your F# code more towards Haskell.

Here is the final shade. F# more like Haskell. #

If you like to skip all the IO and monad stuff, just pause this blog here and watch this video by Scott W.

You can gradually grow your system to push towards Haskell. A Little help to LIFT your code will surely be useful. Read Functional Programming for Fun and Profit’s Railway oriented programming post. Written by Scott Wlaschin’s

Or you can use a wonderful library like F# plus

Let’s check out the validation example, I have taken from docs of F# plus. So, you can build upon that.

open System
open FSharpPlus
open FSharpPlus.Data
module Person=
type Name = { unName : String }
with static member create s={unName=s}
type Email = { unEmail : String }
with static member create s={unEmail=s}
type Age = { unAge : int }
with static member create i={unAge=i}
type Person = { name : Name
email : Email
age : Age }
with static member create name email age={name=name;email=email;age=age }
type Error =
| NameBetween1And50
| EmailMustContainAtChar
| AgeBetween0and120
// Smart constructors
let mkName s =
let l = length s
if (l >= 1 && l <= 50)
then Success <| Name.create s
else Failure [ NameBetween1And50 ]
let mkEmail s =
if String.contains '@' s
then Success <| Email.create s
else Failure [ EmailMustContainAtChar ]
let mkAge a =
if (a >= 0 && a <= 120)
then Success <| Age.create a
else Failure [ AgeBetween0and120 ]
let mkPerson pName pEmail pAge =
Person.create
<!> mkName pName
<*> mkEmail pEmail
<*> mkAge pAge
// Examples
let validPerson = mkPerson "Bob" "bob@gmail.com" 25
// Success ({name = {unName = "Bob"}; email = {unEmail = "bob@gmail.com"}; age = {unAge = 25}})
let badName = mkPerson "" "bob@gmail.com" 25
// Failure [NameBetween1And50]
let badEmail = mkPerson "Bob" "bademail" 25
// Failure [EmailMustContainAtChar]
let badAge = mkPerson "Bob" "bob@gmail.com" 150
// Failure [AgeBetween0and120]
let badEverything = mkPerson "" "bademail" 150
// Failure [NameBetween1And50;EmailMustContainAtChar;AgeBetween0and120]
open FSharpPlus.Lens
let asMaybeGood = validPerson ^? Validation._Success
// Some ({name = {unName = "Bob"}; email = {unEmail = "bob@gmail.com"}; age = {unAge = 25}})
let asMaybeBad = badEverything ^? Validation._Success
// None
let asResultGood = validPerson ^. Validation.isoValidationResult
// Ok ({name = {unName = "Bob"}; email = {unEmail = "bob@gmail.com"}; age = {unAge = 25}})
let asResultBad = badEverything ^. Validation.isoValidationResult
// Error [NameBetween1And50;EmailMustContainAtChar;AgeBetween0and120]
module Email =
// ***** Types *****
type AtString = AtString of string
type PeriodString = PeriodString of string
type NonEmptyString = NonEmptyString of string
type Email = Email of string
type VError = | MustNotBeEmpty
| MustContainAt
| MustContainPeriod
// ***** Base smart constructors *****
// String must contain an '@' character
let atString (x:string) : Validation<VError list,AtString> =
if String.contains '@' x then Success <| AtString x
else Failure [MustContainAt]
// String must contain an '.' character
let periodString (x:string) : Validation<VError list,PeriodString> =
if String.contains '.' x
then Success <| PeriodString x
else Failure [MustContainPeriod]
// String must not be empty
let nonEmptyString (x:string) : Validation<VError list,NonEmptyString> =
if not <| String.IsNullOrEmpty x
then Success <| NonEmptyString x
else Failure [MustNotBeEmpty]
// ***** Combining smart constructors *****
let email (x:string) : Validation<VError list, Email> =
result (Email x) <*
nonEmptyString x <*
atString x <*
periodString x
// ***** Example usage *****
let success = email "bob@gmail.com"
// Success (Email "bob@gmail.com")
let failureAt = email "bobgmail.com"
// Failure [MustContainAt]
let failurePeriod = email "bob@gmailcom"
// Failure [MustContainPeriod]
let failureAll = email ""
// Failure [MustNotBeEmpty;MustContainAt;MustContainPeriod]
module MovieValidations=
type VError= | MustNotBeEmpty
| MustBeAtLessThanChars of int
| MustBeADate
| MustBeOlderThan of int
| MustBeWithingRange of decimal*decimal
module String=
let nonEmpty (x:string) : Validation<VError list,string> =
if String.IsNullOrEmpty x
then Failure [MustNotBeEmpty]
else Success x
let mustBeLessThan (i:int) (x:string) : Validation<VError list,string> =
if isNull x || x.Length > i
then Failure [MustBeAtLessThanChars i]
else Success x
module Number=
let mustBeWithin (from,to') (x)=
if from<= x && x <= to'
then Success x
else Failure [MustBeWithingRange (from,to')]
module DateTime=
let classicMovie year (d:DateTime)=
if d.Year < year
then Success d
else Failure [MustBeOlderThan year]
let date (d:DateTime)=
if d.Date = d
then Success d
else Failure [MustBeADate]
type Genre=
|Classic
|PostClassic
|Modern
|PostModern
|Contemporary
type Movie = {
Id: int
Title: String
ReleaseDate: DateTime
Description: String
Price: decimal
Genre: Genre
}
with static member Create(id,title,releaseDate,description,price,genre): Validation<VError list,Movie> =
fun title releaseDate description price->{ Id=id;Title=title;ReleaseDate=releaseDate;Description=description;Price=price;Genre=genre }
<!> String.nonEmpty title <* String.mustBeLessThan 100 title
<*> DateTime.classicMovie 1960 releaseDate <* DateTime.date releaseDate
<*> String.nonEmpty description <* String.mustBeLessThan 1000 description
<*> Number.mustBeWithin (0.0m, 999.99m) price
let newRelease = Movie.Create(1,"Midsommar",DateTime(2019,6,24),"Midsommar is a 2019 folk horror film written...",1m,Classic) //Failure [MustBeOlderThan 1960]
let oldie = Movie.Create(2,"Modern Times",DateTime(1936,2,5),"Modern Times is a 1936 American comedy film...",1m,Classic) // Success..
let titleToLong = Movie.Create(3, String.Concat (seq{ 1..110 }), DateTime(1950,1,1),"11",1m,Classic) //Failure [MustBeAtLessThanChars 100]

This style of coding is very helpful when you are designing a big IO based system. This way you easily separate logic from execution.

I did purposefully skip heavy functional concepts here. Concepts and patterns based on Algebra, Category Theory, Monads, etc. It’s good and important to know them. But that is totally out the scope for this blog. So, maybe next time.

F# is a very flexible language, and one can bend it as one like. Write down in comment what is your shade of F#?