F#'s List

Introduction

This article will explore F# lists, which developers use extensively.

Generally, .NET developers typically mean the generic List when they speak about List.

Don't be confused with the generic List with the .NET System.Collections.Generic.List.

F# language lists syntax compiles to the FSharpLIst<' T> class instances found in Microsoft.Fsharp.Collections namespace, and that's the kind of List where going to explore in this article.

OK, let's get started.

Creating a F# List

Create an Empty List

It is easy to create a list; the square brackets [] denote an empty List. 

Let's try to see an example below. 

[<Fact>]
let ``Empty List`` () = 
	let emptyList = []
	Assert.True(List.isEmpty emptyList)

To check if the List is empty, we must use the function List.isEmpty and pass the list's name. 

But if you love to be more static or specific with your declaration. 

See the example below. 

[<Fact>]
let``Another Empty List`` () = 
	let emptyList = List.empty<int>
	Assert.True(List.isEmpty emptyList)

Different Ways to Create a List

Now that we know how to create at least an empty list. Why not create a simple list this time? 

[<Fact>]
let ``Create A Simple List`` () =
	let shoes = ["New Balance" ; "Nike"; "Puma"]
	Assert.True(List.isEmpty shoes |> not)

Inside the symbol square brackets [], we have put some items inside, and after each item, it is followed by a semicolon(;)

We must check if the list isn't empty on the last line. 

But if you're interested in knowing another way to create a list.

See the example below. 

[<Fact>]
let ``Other Ways to Create a Simple List`` () = 
	let shoes = [
				  "New Balance"
				  "Nike"
				  "Puma"
			    ]
	Assert.True(List.isEmpty shoes |> not)

As you can see the difference, the first example is enclosed with a semicolon, but this time is a new line. 

There's another way.

Let's try range expression this time. 

[<Fact>]
let ``Other Ways to Create a Simple List Using Range Operator`` () = 
	let number10to50 = [10..50]
	Assert.True(number10to50.Head = 10)
	Assert.True((number10to50 |> List.last) = 50)

This time we have used a range expression that starts from 10 and ends at 50. 

Seems easy right? But the interesting part is the Head property and the last method of a List instance. 

The Head is the first element, while the last method returns the last item on the list.

There are many ways to create a List, and we have many options as developers. 

Now for our last example, let's see how we can use the built-in operator that consists of two colons (::), pronounced as “cons.”

[<Fact>]
let ``Using the Cons Operator`` () =
	let shoes = "New Balance" :: "Nike" :: "Puma" :: []

	Assert.True(List.isEmpty shoes |> not)

Length of a List

[<Fact>]
let ``Length of a List`` () = 
	let cars = ["Toyota"; "Isuzu"; "Ford"; "Tata Safari"]
	Assert.Equal(4, cars.Length)

It seems easy to call the Length property.

It will return to the total number of items on a List. 

Checking If Type is a List

We have learned from the previous examples how to create a List and get its length of it.

This time we'll try to show how to check a type is a List. 

[<Fact>]
let ``Check if type is a List`` () = 
	let emptyList = []
	Assert.True(emptyList.GetType().GetGenericTypeDefinition() = typedefof<List<_>>)

Note:

As a .NET developer, typically, what we do after invoking the GeType() method, we'll check the type using the typedefof method.  

So don't forget to include the GetGenericTypeDefinition.

Accessing Elements on a List

Let's see an example first. 

[<Fact>]
let ``Accessing Elements`` () = 
	
	let cars = ["Toyota"; "Mazda"; "Mitsubishi"]
	
	Assert.True(List.nth cars 0 = "Toyota")
	Assert.True(cars.[0] = "Toyota")
    Assert.True(cars.Item(0) = "Toyota")

I am assuming that you have a familiarity with an Array. Typically in other languages, we can access an element via an array using the indexer syntax. 

In F#, we have the indexer syntax but with a dot before the square brackets [] and the number/index element. 

Moreover, we can also use List.nth or the Item to get the same result.

Head and Tails 

This time, let's explore a List's Head and Tail property. 

The Head is the first element, while the Tail property is the list without the first element. 

Let's try to see an example below. 

[<Fact>]
let ``Head and Tails List`` () = 
	let mobilePhones = ["Samsung"; "IPhone"; "Huwaei"]
	
	let head = mobilePhones.Head
	let tails = mobilePhones.Tail

	let rec contains fn l = 
		if l  = [] then false
		else fn(List.head l) || contains fn (List.tail l)

	Assert.Equal("Samsung", head)
	
	let containsIPhone = (tails |> contains (fun element-> element = "IPhone" ))
	Assert.True(containsIPhone)
	
	let containsHuwaei = (tails |> contains  (fun element-> element = "Huwaei"))
	Assert.True(containsHuwaei)

From the example above, we have declared a List named mobilePhones.

If it is obvious that the Head will be equivalent to Samsung. That's easy. 

But the Tail property is different. It returns the List without the Head for us to check that our List contains the IPhone or the Huwaei. 

We created a recursive function named contains. That will check if the value passed exists on the List. 

Concat a F# List

We can concatenate two lists using the symbol @. 

[<Fact>]
let ``Simple Way to Concat a List`` () = 
	let twoListOfCars = ["Toyota"; "Isuzu"] @ ["Ford"; "Tata Safari"]

	Assert.True(twoListOfCars.Head = "Toyota")
	Assert.True((twoListOfCars |> List.last) = "Tata Safari")

Just make sure the lists are the same type. 

From the example above, we declare the twoListOfCars to join or concatenate two Lists.

Another equivalent of the example above is the List.concat method.

[<Fact>]
let ``Concat List`` () = 
	let inputDevice = ["keyboard"; "mouse"; "microphone"]

	let outputDevice = ["printer"; "monitor";"speaker"]

	let desktop = [inputDevice; ["CPU"; "Motherboard"; "RAM"; "HDD"]; outputDevice] |> List.concat 

	let len:int = (desktop.Length) - 1

	for i = 0 to len do
		let message = $"{i}={desktop.[i]}"  
		outputs.WriteLine(message)

	Assert.Equal(10,desktop.Length)

Append a F# List

For our last topic, we'll explore how to append List. 

To append a list, the F# language has a method List.append, which returns a new list that contains the elements of the first list followed by the second list.

Let's see an example below.

[<Fact>]
let ``Append List`` () = 
	let cars1 = ["Toyota"; "Mazda"; "Mitsubishi"]
	let cars2 = ["Ford"]

	let carResult = cars2 |> List.append  cars1

	Assert.True(carResult.Head = "Toyota")
	Assert.True((carResult |> List.last)  = "Ford")

Summary

In this article, we've discussed F# records type concepts and the following:

  • Creating a List
  • Length of a List
  • Checking If Type is a List
  • Accessing Elements on a List
  • Head and Tails
  • Concat a List
  • Append a List

I hope you have enjoyed this article, as I enjoyed it while writing.

You can also find the sample code here on GitHub.

Till next time, happy programming!


Similar Articles