I, like a lot of newer developers, learnt how to code using Javascript. I loved how quickly I could get a project up and running. However, this often came at a cost of bugs and surprises for the Q.A. team. Since JS has no type safety it's up to us developers to run through the possibilities and ensure weird bugs dont pop up - and thats hard.
What about Typescript?! Even though I got to use the awesome features that JS/TS had to offer it always felt like extra work, like fighting an uphill battle - especially with the any
type in play.
The Pit Of Success
How do you win the uphill battle? Flip it on its head and make it hard to do the wrong thing. Thats why I fell in love with F#. It's a functional first language that is type safe and has all the features you love about JS, plus a lot more! The biggest difficulty I had when trying to learn a statically typed langauage was the verbose syntax, having to specify everything, coming from a world where I could just focus on the code. But with F# that isn't the case.
Here's a few direct comparisons:
1. Looping through a list and applying a function
JS
const oneToFive = [1,2,3,4,5]
const double = input => input.map(x => x * 2)
// doubleInput(oneToFive)
// [2,4,6,8,10]
F#
let oneToFive = [1..5]
let double input = input |> List.map (fun x -> x * 2) // int list -> int list
// The F# compiler has inferred that input is an int list
oneToFive |> doubleInput
val it : int list = [2; 4; 6; 8; 10]
F# works like maths - if something is on both sides of the =
it can often be ommited, giving an even terser syntax
let double = List.map ((*) 2)
2. Updating a field in an object/record
JS
const person1 = { firstName: "Akash", age: 24 }
const updatePerson1 = { ...person1, age: 25 }
// {firstName: "Akash", age: 25}
F#
let person1 = {| FirstName = "Akash"; Age = 24 |} // Anonymous record
let updatePerson1 = {| person1 with Age = 25 |}
// {| FirstName = "Akash"; Age = 25 |}
// Here we define an anonymous type of Person but F# has picked up that both person1 and updatedPerson1 are both of the same type
3. Control Flow - Ternary Operator
JS
const isTrue = true ? 'This is the value' : 'Never gets reached'
F#
let isTrue = if true then "This is the value" else "Never gets reached" // string
4. Control Flow - Conditionals
JS
const findDiscount = customer => {
if (customer === "Student") {
return 10
} else if (customer === "Senior") {
return 15
} else if (customer === "VIP") {
return 35
} else {
return 0
}
}
F#
let findDiscount customer =
match customer with
| "Student" -> 10
| "Senior" -> 15
| "VIP" -> 35
| _ -> 0
// string -> int
We can take this further in F# and ensure not just any string is passed in using a Choice Type or Discriminated Union
type Customer =
| Student
| Senior
| VIP
| Guest
let findDiscount customer =
match customer with
| Student -> 10
| Senior -> 15
| VIP -> 35
| Guest -> 0
// Customer -> int
Now we have a function that takes a limited range of values and is more accurate to the domain.
From the examples above we can see that F# can be as clean, if not cleaner than JS, with full type safety. This is just a small preview of what F# is capable of, and it was enough for me to start looking into the language and eventually make the switch from JS.