29 March 2026

Deck of Cards

In my last post, I used the analogy of creating a deck of cards to illustrate the relationship between discriminated unions and pattern matching. That example was taken from Chris Smith’s “Programming in F# 3.0”. I know that the approach taken there was more to serve as a teaching example but a few things about the way we constructed the PlayingCard type bothered me:

> type PlayingCard =
-     | Ace of Suit
-     | King of Suit
-     | Queen of Suit
-     | Jack of Suit
-     | ValueCard of int * Suit;;
type PlayingCard =
  | Ace of Suit
  | King of Suit
  | Queen of Suit
  | Jack of Suit
  | ValueCard of int * Suit

First off, I didn’t like how the union types for Ace and the face cards differed in type from the numeral cards. The former consisted of just the Suit while the latter was constructed using a tuple of int and Suit. Second, there was nothing preventing someone from creating a card such as:

> let card = ValueCard (723, Spade);;
val card: PlayingCard = ValueCard (723, Spade)

I think if I were to do this again, I would do it slightly differently. I would keep the original type to define the Suit:

type Suit =
  | Heart
  | Diamond
  | Spade
  | Club

But I would create a second discriminated union to define the Rank:

> type Rank =
-     | Ace
-     | King
-     | Queen
-     | Jack
-     | Ten
-     | Nine
-     | Eight
-     | Seven
-     | Six
-     | Five
-     | Four
-     | Three
-     | Two;;
type Rank =
  | Ace
  | King
  | Queen
  | Jack
  | Ten
  | Nine
  | Eight
  | Seven
  | Six
  | Five
  | Four
  | Three
  | Two

This enforces correctness at the type level obivating the need for any runtime checks.

Now we can create our PlayingCard record:

> type PlayingCard = { Rank: Rank; Suit: Suit };;
type PlayingCard =
  {
    Rank: Rank
    Suit: Suit
  }

With the assistance of some “helper lists”, we can create the list for our deckOfCards:

> let ranks =              
-     [
-         Ace; King; Queen; Jack;
-         Ten; Nine; Eight; Seven;
-         Six; Five; Four; Three; Two
-     ];;
val ranks: Rank list =
  [Ace; King; Queen; Jack; Ten; Nine; Eight; Seven; Six; Five; Four; Three;
   Two]

> let suits = [ Heart; Diamond; Spade; Club ];;
val suits: Suit list = [Heart; Diamond; Spade; Club]

> let deckOfCards =
-     let toCard (rank, suit) = { Rank = rank; Suit = suit }
-     List.allPairs ranks suits
-     |> List.map toCard;;
val deckOfCards: PlayingCard list =
  [{ Rank = Ace
     Suit = Heart }; { Rank = Ace
                       Suit = Diamond }; { Rank = Ace
                                           Suit = Spade }; { Rank = Ace
                                                             Suit = Club };
   { Rank = King
     Suit = Heart }; { Rank = King
                       Suit = Diamond }; { Rank = King
                                           Suit = Spade }; { Rank = King
                                                             Suit = Club };
   { Rank = Queen
     Suit = Heart }; { Rank = Queen
                       Suit = Diamond }; { Rank = Queen
                                           Suit = Spade }; { Rank = Queen
                                                             Suit = Club };
   { Rank = Jack
     Suit = Heart }; { Rank = Jack
                       Suit = Diamond }; { Rank = Jack
                                           Suit = Spade }; { Rank = Jack
                                                             Suit = Club };
...

Not only is this approach more strict, there really is no need for pattern matching to “consume” a card:

> let describe card = sprintf $"{card.Rank} of {card.Suit}";;
val describe: card: PlayingCard -> string

> describe deckOfCards[22];;
val it: string = "Nine of Spade"

And if you look closely, we also eliminated the nested for loops in the deckOfCards function with some List methods. I think I like this way much better!

28 March 2026

Discriminated Unions and Pattern Matching in F#

I’m starting to kick the tires on discriminated unions and pattern matching as I continue to learn about F#. While not exactly the same thing, they are kind of like C#’s enumeration types where a type can have only one possible value. That value is called a union case. Using playing cards as an example, we can define the type Suit:

> type Suit =
-     | Heart
-     | Diamond
-     | Spade
-     | Club;;
type Suit =
  | Heart
  | Diamond
  | Spade
  | Club

> Diamond;;
val it: Suit = Diamond

Here, we can see that Diamond is of type Suit.

Discriminated unions are used to define what is sometimes known as an “or” relationship which, in our example, means that the suit of a playing card can be a Heart or a Diamond or a Spade or a Club. This is in contrast to F# Records which help define an “and” relationship.

We can then define the type PlayingCard as being either an Ace, a face/court card (King, Queen or Jack) or a numeral card (2 through 10, inclusive) using another discriminated union:

> type PlayingCard =
-     | Ace of Suit
-     | King of Suit
-     | Queen of Suit
-     | Jack of Suit
-     | ValueCard of int * Suit;;
type PlayingCard =
  | Ace of Suit
  | King of Suit
  | Queen of Suit
  | Jack of Suit
  | ValueCard of int * Suit

> Ace;;
val it: Item: Suit -> PlayingCard

> ValueCard;;
val it: Item1: int * Item2: Suit -> PlayingCard

In defining the type PlayingCard we have chosen to associate data with each union case. This is optional. The data we associate with the union case can be a simple type (e.g. int, string), a tuple, a record or even another discriminated union. In our case, we’ve associated the discriminated union Suit to the union cases Ace, King, Queen and Jack. For the union case ValueCard, we’ve associcated a tuple consisting of an int and a Suit.

Note how F# treats these union cases as a function when we examine their type signatures. Ace, when provided a parameter of type Suit, will return a PlayingCard. ValueCard is also a function that returns a PlayingCard but it takes a tuple made up of an int and Suit for its parameter. This is very important because understanding the type signature not only helps us in constructing a card but, as we’ll see later, also guides us in defining the correct patterns when it comes to matching a card. Knowing this, we can define a few individual cards:

> let card0 = Ace Spade;;
val card0: PlayingCard = Ace Spade

> let card1 = King Spade;;
val card1: PlayingCard = King Spade

> let card4 = ValueCard (2, Spade);;
val card4: PlayingCard = ValueCard (2, Spade)

> let card18 = ValueCard (3, Club);; 
val card18: PlayingCard = ValueCard (3, Club)

(Note that when defining a card that belongs to the ValueCard union case, parentheses are required because we have to pass a tuple. The parentheses are optional if we are defining a card with the union case Ace, King, Queen or Jack. We can include parenetheses for readability of we desire like in the example below.)

Armed with these discriminated unions, we can now create a deckOfCards:

> let deckOfCards =
-     [
-         for suit in [ Spade; Club; Heart; Diamond ] do
-             yield Ace(suit)
-             yield King(suit)
-             yield Queen(suit)
-             yield Jack(suit)
-             for value in 2 .. 10 do
-                 yield ValueCard(value, suit)
-     ];;
val deckOfCards: PlayingCard list =
  [Ace Spade; King Spade; Queen Spade; Jack Spade; ValueCard (2, Spade);
   ValueCard (3, Spade); ValueCard (4, Spade); ValueCard (5, Spade);
   ValueCard (6, Spade); ValueCard (7, Spade); ValueCard (8, Spade);
   ValueCard (9, Spade); ValueCard (10, Spade); Ace Club; King Club;
   Queen Club; Jack Club; ValueCard (2, Club); ValueCard (3, Club);
   ValueCard (4, Club); ValueCard (5, Club); ValueCard (6, Club);
   ValueCard (7, Club); ValueCard (8, Club); ValueCard (9, Club);
   ValueCard (10, Club); Ace Heart; King Heart; Queen Heart; Jack Heart;
   ValueCard (2, Heart); ValueCard (3, Heart); ValueCard (4, Heart);
   ValueCard (5, Heart); ValueCard (6, Heart); ValueCard (7, Heart);
   ValueCard (8, Heart); ValueCard (9, Heart); ValueCard (10, Heart);
   Ace Diamond; King Diamond; Queen Diamond; Jack Diamond;
   ValueCard (2, Diamond); ValueCard (3, Diamond); ValueCard (4, Diamond);
   ValueCard (5, Diamond); ValueCard (6, Diamond); ValueCard (7, Diamond);
   ValueCard (8, Diamond); ValueCard (9, Diamond); ValueCard (10, Diamond)]

Now that we have our deckOfCards, we can see how discriminated unions and pattern matching can work together by creating a describe function which will take a parameter card of type PlayingCard and return a string that tells us what type of card we have. Let’s take a minute to quickly revisit the type signatures of the union cases that make up the PlayingCard type:

> Ace;;
val it: Item: Suit -> PlayingCard

> ValueCard;;
val it: Item1: int * Item2: Suit -> PlayingCard

Using Ace as an example, we can mentally think of how we used its signature to create individual cards above:

Ace : Suit -> PlayingCard

Using the same logic, we can “deconstruct” the card in a way to be used in pattern matching. Let’s start with just one specific rule and one wildcard (to silence distracting compiler warnings):

> let describe card =
-     match card with
-     | Ace suit -> $"Ace of %A{suit}"  // note the "Ace suit" pattern
-     | _ -> "another card";;
val describe: card: PlayingCard -> string

> describe deckOfCards[0];;           
val it: string = "Ace of Spade"

> describe deckOfCards[1];;
val it: string = "another card"

Using the same format, we can build up our describe function to include the face cards:

> let describe card =
-     match card with
-     | Ace suit -> $"Ace of %A{suit}"
-     | King suit -> $"King of %A{suit}"
-     | Queen suit -> $"Queen of %A{suit}"
-     | Jack suit -> $"Jack of %A{suit}"
-     | _ -> "another card";;
val describe: card: PlayingCard -> string

> describe deckOfCards[3];;
val it: string = "Jack of Spade"

We can now match for ValueCards but with a slightly different pattern. Again, going back to the ValueCard type signature, we see that it takes a tuple of an int and Suit. Another way to think about this is:

ValueCard : (int * Suit) -> PlayingCard

Therefore, we can augment our describe function with a pattern to map to ValueCards:

> let describe card =
-     match card with
-     | Ace suit -> $"Ace of %A{suit}"
-     | King suit -> $"King of %A{suit}"
-     | Queen suit -> $"Queen of %A{suit}"
-     | Jack suit -> $"Jack of %A{suit}"
-     | ValueCard (value, suit) -> $"{value} of %A{suit}";; // note the tuple
val describe: card: PlayingCard -> string

> describe deckOfCards[4];;
val it: string = "2 of Spade"

> describe deckOfCards[18];;
val it: string = "3 of Club"

Note that because we have now accounted for all possible union cases, we no longer require the wildcard pattern.

Like all of my prior posts on F#, this is in now way meant to be an exhaustive or comprehensive treatise on discriminated unions or pattern matching. Rather, it’s a brief summary of my limited understanding of how they work to hopefully help improve my comprehension. As usual, comments, suggestions or feedback are greatly appreciated!

21 March 2026

Learning to Read the F# Documentation

I’ve come across a lot of good resources in the form of books and blog posts in my journey to learn F# but one resource I’m spending more time with is the official Microsoft .Net Documentation and more specifically, the F# Language Reference. The language reference material in particular has a lot of good information including code examples and links to the F# Core Library Documentation.

However, as I spend more time scouring and reading through all of this, I’m finding it challenging navigating all of the various sites and understanding it. Much like reading code is an acquired skill, so too is learning to read documentation. There are conventions, idioms and syntax you need to be familiar with to get the most out of the documentation. It can be a bit like “Catch-22”: To better understand F#, you need to read the documentation but to understand the documentation, you need to be familiar with the language. To be clear, this isn’t an F#-only paradox - it’s common in almost every programming language.

But I’ve yet to find any resource that helps walk you through the documentation, how it’s organized or how to read it. That’s why I thought it would be good for me to document what I’m learning as I read through the F# documentation, not only about the language itself, but also on how to read the docs.

One recent instance of this came when I was learning more about the DateTime struct to access various time and date properties and methods. Let’s assume for a moment that we don’t have access to any search engines, LLMs, coding agents or AI. The first question is how do I even find the documentation so I can read it?

A good first place to start is the official Microsoft F# Documentation page - I have this one bookmarked in my browser! From here, you can find lots of good links to the language guide and library reference I mentioned above. Just as important, you can also find a link to the .Net library reference and API browser which is what we want. From there, we can browse what is contained in the System namespace which is where we will find our DateTime struct. (I know there was a lot of hand-waving in that paragraph, especially for anyone who is brand new to .NET and/or F# but unfortunately, I’m no expert either!)

On the documenation page for DateTime, we can see it’s type definition along with what interfaces it implements. (Note: The concept and application of interfaces are completely over my head right now and on my list to eventually learn so I’ll refrain from commenting on them here.) More importantly for me, this page also lists the various contructors, properties and methods available for this type.

On the DateTime Constructors page, we can see all of the Overloads available to construct a DateTime object. Taking the simpler example of DateTime(Int32, Int32, Int32), we can see the type signature of this function:

new DateTime : int * int * int -> DateTime

Just below this, the documentation helpfully indicates what each parameter is:

Parameters

year Int32

The year (1 through 9999).

month Int32

The month (1 through 12).

day Int32

The day (1 through the number of days in month).

This allows us to create an instance of DateTime as follows:

> open System;;
> let dt = DateTime(2026, 3, 21);;
val dt: DateTime = 3/21/2026 12:00:00 AM

Going back to the definition of DateTime, we can also examine the type’s various properties. Looking at the page for the DateTime.DayOfWeek Property, we can see that this is an instance property represented by the type signature:

member this.DayOfWeek : DayOfWeek

where DayOfWeek is an enumerated constant. Instance properties return the property for a specific instance of the type. To use it, replace this with our object:

> dt.DayOfWeek;;
val it: DayOfWeek = Saturday

There are also static properties, such as DateTime.Today and DateTime.Now that return DateTime objects based on the current date and time. Note the static keyword in their signatures:

static member Today : DateTime
static member Now : DateTime

Static properties apply to the type in general, DateTime in this case. They can be used as follows:

> let today = DateTime.Today;;
val today: DateTime = 3/20/2026 12:00:00 AM

> let now = DateTime.Now;;    
val now: DateTime = 3/20/2026 5:48:14 PM

Returning again to the DateTime definition page, we can also see the long list of methods that are available to us. Like the properties we reviewed, methods come in two flavors: instance methods, such as DateTime.AddDays and static methods such as DateTime.IsLeapYear. Let’s examine both:

Instance methods, such as DateTime.AddDays, will have some reference of returning a “value of this instance” in the method definition. In addition, we can again look at the method’s type signature:

member this.AddDays : double -> DateTime

Note the this. Like the instance property, instance methods apply to a specific instance of the type. Reading the signature, we know that we have to pass a double to the method to obtain another DateTime object like so:

> dt.AddDays 2.0;;
val it: DateTime = 3/23/2026 12:00:00 AM {Date = 3/23/2026 12:00:00 AM;
                                          Day = 23;
                                          DayOfWeek = Monday;
                                          DayOfYear = 82;
                                          Hour = 0;
                                          Kind = Unspecified;
                                          Microsecond = 0;
                                          Millisecond = 0;
                                          Minute = 0;
                                          Month = 3;
                                          Nanosecond = 0;
                                          Second = 0;
                                          Ticks = 639098208000000000L;
                                          TimeOfDay = 00:00:00;
                                          Year = 2026;}

We can also use the pipe operator (2.0 |> dt.AddDays) to achieve the same result.

For static methods, such as DateTime.IsLeapYear, the method works on the type in general. Looking at the type signature, we see:

static member IsLeapYear : int -> bool

which means that we can use it like:

> DateTime.IsLeapYear 2026;;
val it: bool = false

or via the pipe operator (2026 |> DateTime.IsLeapYear).

This is by no means an exhaustive review of how to use all of the Microsofot .NET or F# documentation but just a reminder to myself that while complicated, there is a ton of good information on the language and associated APIs. It takes effort to properly understand how it is all organized and how to use it but I think the the investment is worth it.

16 March 2026

Do What Makes You Happy

I came across this post by Amit Gawande about losing interest in blogging. As someone who has been an on again, off again blogger across all sorts of platforms, I can certainly sympathize. Thank god my livelihood does not depend on it! For me, maintaining a blog is supposed to afford me an outlet to crystalize my ideas, share what I’m learning or working on and hopefully make me a better communicator. But by no means do I expect to build an audience or a following because when I write, it’s for an audience of one: me.

Currently, I am going through another cycle of trying to write more but who knows how long it will last. I may give up in three weeks, three months or three years but since I’m under no obligation to publish anything, I don’t really care. I’ll do it as long as I enjoy it and for Amit, I think, that is key.

Going through some of Amit’s prior posts, it also looks like he’s lost interest in reading blogs which is again something I can relate to. I was an avid user of Google Reader who switched to Feedly upon its demise and, until recently, was a user of Inoreader. But it was getting stressful logging in and seeing the hundreds of unread posts. I just couldn’t keep up. Like Amit, I declared RSS bankruptcy and now only subscribe to one blog: mine.1

I’m sure at some point, I’ll start to fill up my OPML file again but I can see why most of today’s discourse has moved to social media sites. It’s much easier to digest a stream of bite size chunks than clicking through to someone’s blog and reading hundreds of lines instead of characters. But I appreciate those that soldier on so I’ll have something to ponder when my interest inevitably returns.

Hobbies are allowed to come and go. For proof, look no further than my YouTube subscriptions and playlists. It’s like a graveyard of unrealized aspirations: microcontrollers, smoking meat, video games, flash photography, Indian cooking. The list goes on and on!

Life is too short to spend your valuable but limited free time on things that don’t interest you. Do what makes you happy.


  1. Just to ensure that my RSS feed works in case anyone decides to subscribe!↩︎

15 March 2026

Using F# Sequences to Count Instances of the 13th of the Month

Shortly after publishing yesterday’s post on the lazy evaluation of F# sequences, I came across this post by Dr. Drang about calculating the frequency by day of week of occurences of the 13th day of the month. His post was inspired by a Scientfic American article about why the 13th of the month falls on a Friday more than any other day of the week (like two days ago from when this post is published). Spoiler: while true, it’s not by much.

In his post, Dr. Drang offered up a short Python script that calculates the number of times the 13th falls on each day of the week across a 400-year span. He uses a simple imperative script consisting of nested for loops to iterate through the years and months and counting up the number of occurences by day in an array:

f13s = [0]*7
for y in range(1800, 2200):
  for m in range(1, 13):
    wd = date(y, m, 13).weekday()
    f13s[wd] += 1

Given the timeliness of his post as well as my recent forays into functional programming and the use of collections in F#, I thought it would be fun to see what I could come up with:

open System

let allDates (startDate: DateTime) (endDate: DateTime) =
    seq {
        let mutable theDate = startDate.Date
        while theDate <= endDate.Date do
            yield theDate
            theDate <- theDate.AddDays 1
    }

let startDate = DateTime(2000, 1, 1)
let endDate = DateTime(2399, 12, 31)

let myDates = allDates startDate endDate

myDates
|> Seq.filter (fun date -> date.Day = 13)
|> Seq.countBy (fun date -> date.DayOfWeek)
|> Seq.sort
|> Seq.iter (fun (dayOfWeek, count) -> printfn $"{dayOfWeek}: {count}")

While not as concise as the Python implementation, I thought I did ok.

I started with the now familiar allDates function from yesterday’s post to define a sequence consisting of all dates within the range we are interested in. Like the Scientific American article, I went for years 2000 through 2399. Once we have our dates, I apply a filter to find all occurrences of the 13th of the month and then count them by day of week. After sorting them by day of week, we print our results:

Sunday: 687
Monday: 685
Tuesday: 685
Wednesday: 687
Thursday: 684
Friday: 688
Saturday: 684

which match both the authors of the article and Dr. Drang!1

While this past Friday was the second Friday the 13th of year, we’ll have one more in November. To learn more about why, look no further than the good Doctor!


  1. The only difference is in the order of the days. Whereas Python date.weekday() uses 0 for Monday and 6 for Sunday, .NET DayOfWeek uses 0 for Sunday and 6 for Saturday.↩︎

14 March 2026

Lazy Evaluation of Sequences in F#

I’ve been coming back to something I read in “F# in Action” about sequences and how they’re lazily evaluated. Lazy evaluation is not something I ever encountered or thought about previously as a hobbyist programmer so I had to spend more time looking into this. To see this in action, it helps to run through some examples in FSI, the interactive REPL.

If we create a list, the REPL immediately reports back what was created:

> let myList = [ 1 .. 10 ];;
val myList: int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

The same is true if we created an array:

> let myArray = [| 1 .. 10 |];;
val myArray: int array = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]

But look what happens if we define a sequence:

> let mySequence = seq { 1 .. 10 };;
val mySequence: int seq

Unlike lists and arrays, which are evaluated upon creation (eager evaluation), sequences are evaluated lazily, or on demand. Where this comes in handy is illustrated by an example in the book asking us to find the dates of all Mondays between February and May 2020, inclusive. In the example, we start by creating a sequence to contain all representable dates(!):

let allDates = seq {
    let mutable theDate = DateTime.MinValue
    while theDate <= DateTime.MaxValue do
        theDate
        theDate <- theDate.AddDays 1.
}

Using standard library functions, we can then filter this sequence to obtain only the dates we are interested in1:

let mondays =
    allDates

    // skip all dates while function evaluates to true
    |> Seq.skipWhile (fun d -> d.Year < 2020 || d.Month < 2)

    // return only those dates where function evaluates to true
    |> Seq.filter (fun d -> d.DayOfWeek = DayOfWeek.Monday)

    // take all dates while function evaluates as true
    |> Seq.takeWhile (fun d -> d.Year = 2020 && d.Month < 6)

    // return result as array
    |> Seq.toArray

F# returns the result of this query almost instantaenously despite the fact that there could be over 3.6 million possible dates represented by allDates:

> DateTime.MinValue;;
val it: DateTime = 1/1/0001 12:00:00 AM ...

> DateTime.MaxValue;;
val it: DateTime = 12/31/9999 11:59:59 PM ...

I say “could be” because F# only evalutes sequences when a result is needed, improving the performance of our code.

To be fair, if we used lists or arrays on modern hardware, we would likely get the same result in a reasonable amount of time. But the point is that sequences allow us to work with potentially infinite data structures without running into memory issues. This is a powerful concept that can be applied in many different contexts, such as working with streams of data or generating large datasets on the fly. (In “F# in Action”, the author does demonstrate an example of generating an array from a sequence of 100 million elements which does take a noticeable amount of time to execute; however, when we skip the Seq.toArray call, the results are returned almost immediately.)

A more practical example might be reading in lines from a large file in a memory-constrained environment. Using a sequence, we can read the file line-by-line without loading the entire file into memory at once. This allows us to process large files efficiently and avoid out-of-memory errors:

> let lines = System.IO.File.ReadLines("journalctl.log")
- 
- lines
- |> Seq.filter (fun line -> line.Contains("Network Time"))
- |> Seq.take 10
- |> Seq.iter (printfn "%s" )
- ;;
Dec 24 10:34:33 nixos systemd[1]: Starting Network Time Synchronization...
Dec 24 10:34:33 nixos systemd[1]: Started Network Time Synchronization.
Dec 24 20:53:37 nixos systemd[1]: Starting Network Time Synchronization...
Dec 24 20:53:37 nixos systemd[1]: Started Network Time Synchronization.
Dec 26 16:42:34 nixos systemd[1]: Starting Network Time Synchronization...
Dec 26 16:42:34 nixos systemd[1]: Started Network Time Synchronization.
Dec 26 16:58:35 nixos systemd[1]: Stopping Network Time Synchronization...
Dec 26 16:58:35 nixos systemd[1]: Stopped Network Time Synchronization.
Dec 26 16:59:27 nixos systemd[1]: Starting Network Time Synchronization...
Dec 26 16:59:27 nixos systemd[1]: Started Network Time Synchronization.
val lines: string seq
val it: unit = ()

  1. The Seq.skipWhile and Seq.takeWhile functions are used to limit the range of dates we are working with, while Seq.filter is used to select only the dates that fall on a Monday. If we try to just run a filter on allDates without skipWhile and takeWhile, we will run into System.ArgumentOutOfRangeException errors, at least on my machine:

    > let mondays =                                                                              
    -     allDates                                                                               
    -     |> Seq.filter (fun d ->                                                                
    -         (d.DayOfWeek = DayOfWeek.Monday) && (d.Year = 2020) && (d.Month > 1 && d.Month < 6)
    -     );;                                                                                    
    val mondays: DateTime seq
    
    > mondays |> Seq.toArray;;                                                                   
    System.ArgumentOutOfRangeException: The added or subtracted value results in an un-representable DateTime. (Parameter 'value')
       at System.DateTime.ThrowDateArithmetic(Int32 param)
       at FSI_0005.allDates@16.GenerateNext(IEnumerable`1& next) in /home/samir/Documents/programming/fsharp/stdin:line 19
       at Microsoft.FSharp.Core.CompilerServices.GeneratedSequenceBase`1.MoveNextImpl() in /build/dotnet-10.0.103/src/fsharp/src/FSharp.Core/seqcore.fs:line 488
       at Microsoft.FSharp.Collections.Internal.IEnumerator.next@277[T](FSharpFunc`2 f, IEnumerator`1 e, FSharpRef`1 started, Unit unitVar0) in /build/dotnet-10.0.103/src/fsharp/src/FSharp.Core/seq.fs:line 278
       at Microsoft.FSharp.Collections.Internal.IEnumerator.filter@267.System.Collections.IEnumerator.MoveNext() in /build/dotnet-10.0.103/src/fsharp/src/FSharp.Core/seq.fs:line 283
       at Microsoft.FSharp.Collections.SeqModule.toArray$cont@1028[T](IEnumerator`1 e, Unit unitVar) in /build/dotnet-10.0.103/src/fsharp/src/FSharp.Core/seq.fs:line 1032
       at Microsoft.FSharp.Collections.SeqModule.ToArray[T](IEnumerable`1 source) in /build/dotnet-10.0.103/src/fsharp/src/FSharp.Core/seq.fs:line 1027
       at <StartupCode$FSI_0012>.$FSI_0012.main@() in /home/samir/Documents/programming/fsharp/stdin:line 43
       at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
       at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
       at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
    Stopped due to error
    ↩︎

09 March 2026

Higher Order Functions in F#

One of the things about F# that I am still trying to better understand is the concept of treating functions as “first class citizens”, especially when it comes to passing functions to other functions or having functions return another function. Functions that can take another function as an argument or return a function as a result are called higher-order functions.

Coming from more mainstream programming languages, this idea really makes my head explode!

Here’s a simple example. Suppose we define a function applyFunction as follows:

> let applyFunction funct a b = funct a b;;
val applyFunction: funct: ('a -> 'b -> 'c) -> a: 'a -> b: 'b -> 'c

In this example, applyFunction takes three arguments:

  1. A function funct that takes two generic arguments and returns a generic value.

  2. A generic argument 'a.

  3. A generic argument 'b.

It then applies the function funct to 'a and 'b, returning the generic value 'c.

Note: Another way to define applyFunction, which makes the type signature more explicit, is:

> let applyFunction (funct: 'a -> 'b -> 'c) a b = funct a b;;
val applyFunction: funct: ('a -> 'b -> 'c) -> a: 'a -> b: 'b -> 'c

Let’s see how we can use this in practice. First, let’s define a function that takes two arguments and returns a value:

> let add x y = x + y;;
val add: x: int -> y: int -> int

The type signature of add matches that of funct above which means we can now pass add, along with two other arguments, to applyFunction:

> applyFunction add 4 5;;    
val it: int = 9

applyFunction accepts the function add and the two integers 4 and 5 and applies add to them returning 9.

Like we did in the previous post, we can also use partial application on applyFunction to define a new function:

> let newAdd = applyFunction add;;
val newAdd: (int -> int -> int)

> newAdd 4 5;;
val it: int = 9

newAdd now takes two integers and returns another. In practice, is this how we would define newAdd? Probably not since we could have more easily done:

> let newAdd = add;;
val newAdd: (int -> int -> int)

However, I think this line of application does make sense when we’re working with anonymous functions, or functions that have no name (also referred to as lamdas). For example:

> let newAdd = applyFunction (fun x y -> x + y);;
val newAdd: (int -> int -> int)

> newAdd 3 4;;
val it: int = 7

Here, we’re combining the concepts of anonymous functions with partial applcation. Instead of passing a defined function, such as add to applyFunction, we’re passing anonymous function fun x y -> x + y. This works because the anonymous function has the same type signature expected by applyFunction: 'a -> 'b -> 'c.

For a more practical example of how all of this comes together, let’s take a look at Exercise 7.1 in “F# in Action” which has us build a simple calculator.

Let’s define a new function, calculate, similar to applyFunction:

> let calculate operation x y = operation x y;;
val calculate: operation: ('a -> 'b -> 'c) -> x: 'a -> y: 'b -> 'c

Because calculate is a higher-order function, we can pass to it various other anonymous functions that represent basic mathematical operations:

> calculate (fun a b -> a + b) 2 3;;
val it: int = 5

> calculate (fun a b -> a - b) 10 8;;
val it: int = 2

We can get a little bit fancier by passing both an operation as well as a description of the action we are taking as a tuple so that we can return a string that describes what we are doing:

> let calculate (operation, action) x y =                   
-     let answer = operation x y                            
-     sprintf $"%i{x} %s{action} %i{y} equals %i{answer}";; 
val calculate:
  operation: (int -> int -> int) * action: string ->
    x: int -> y: int -> string

We can then call calculate as follows:

> let result = calculate (( fun x y -> x / y ), "divided by") 10 2;;
val result: string = "10 divided by 2 equals 5"

Putting this all together, we can create a simple calculate.fsx script:

let calculate (operation, action) x y =
    let answer = operation x y
    sprintf $"%i{x} {action} %i{y} equals %i{answer}"

printfn "Enter the first number: "
let x = System.Console.ReadLine() |> int
printfn "Enter the second number: "
let y = System.Console.ReadLine() |> int

printfn "Enter an operation:"
printfn "    1. Add"
printfn "    2. Multiply"
printfn "    3. Subtract"
printfn "    4. Divide"
printfn "Enter your choice: "
let choice = System.Console.ReadLine()

let operation =
    match choice with
    | "1" -> ((fun a b -> a + b), "plus")
    | "2" -> ((fun a b -> a * b), "multiplied by")
    | "3" -> ((fun a b -> a - b), "minus")
    | "4" -> ((fun a b -> a / b), "divided by")
    | _ -> failwith "Invalid choice"

let result = calculate operation x y
printfn "%s" result

which we can run via dotnet fsi calculate.fsx:

Enter the first number: 
10
Enter the second number: 
5
Enter an operation:
    1. Add
    2. Multiply
    3. Subtract
    4. Divide
Enter your choice: 
2
10 multiplied by 5 equals 50

Hopefully this example helps explain a bit more about what higher-order and anonymous functions are in F# and how they can be used in a simple example.