tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Head- and Tail-like methods in C# (and F# and Python and Haskell)

27 Jun 2017 4 mins C#, F#, Functional programming, Haskell, Python

While writing previous post I realized the deconstruction to tuple can be added to any type. As far as compiler is concerned the Deconstruct method needs to exist, doesn’t matter whether it’s an extension method or instance one. That means I can write a Head- and Tail-like methods in C# with nice syntax. I also dusted off my Haskell knowledge and wrote the same logic there for comparison. And then I did it in Python and F# as well. Brace yourself for a true polyglot post.

Deconstruction in C#

To support deconstruction to tuple, C# compiler is looking for a void Deconstruct(out T1 out1, out T2 out2, ...) method. As I said it doesn’t need to be instance method, extension method will do.

For example, imagine classic Point class with X and Y properties. Then the method could be void Deconstruct(out int x, out int y).

Head and Tail for List<T> in C#

The Head method returns first element in the list, which is easy to do thanks to FirstOrDefault LINQ method. The Tail method returns the remaining. Although you can modify the list itself and return it back, I consider it bad practice and rather decided to construct a new one.

static void Deconstruct<T>(this List<T> list, out T head, out List<T> tail)
{
	head = list.FirstOrDefault();
	tail = new List<T>(list.Skip(1));
}

Fairly easy. With that in place I can write an infamous Sum function to sum the items in the list.

int Sum(List<int> list)
{
	switch (list.Count)
	{
		case 0:
			return 0;
		default:
			var (head, tail) = list;
			return head + Sum(tail);
	}
}

Bit too much noise around, but what you can do…

Just to make sure it works.

static void Main(string[] args)
{
	var list = new List<int>() { 10, 20, 100 };
	Console.WriteLine(Sum(list));
}

What about F#

F#, being functional language, has a great support for pattern matching and hence I expected fairly succinct code.

open System

let rec sum list =
    match list with
    | head :: tail -> head + sum tail
    | [] -> 0

[<EntryPoint>]
let main argv =
    let list = [10; 20; 100]
    Console.WriteLine(sum list)
    0

Not bad.

What about Python

Python is not a first-class functional language, although some syntax/features, especially for tuples, I like.

def sum(list):
	if len(list) == 0:
		return 0;
	else:
		head, *tail = list
		return head + sum(tail)

list = [10, 20, 100]
print(sum(list))

I’d say, about as good as in C#. Notable piece is the head, *tail = list where I’m using the “Extended Iterable Unpacking” (PEP 3132). Also, if you don’t like the if len(list) == 0, you can use if not list, which some argue is more Pythonic.

What about Haskell

Last time I used Haskell for anything real was when I was at university. Thus, when I saw the Prelude> it brought back some memories.

module Main(main) where

list = [10,20,100]

sum :: [Int] -> Int
sum [] = 0
sum xs = head xs + Main.sum (tail xs)

main =
	print (Main.sum list)

Umm. Although it’s using the head and tail functions it doesn’t feel like idiomatic Haskell. After some research, I remembered there is a pattern matching on lists directly.

sum :: [Int] -> Int
sum (x:xs) = x + Main.sum xs
sum _ = 0

This feels more like Haskell I remember. It’s also very pleasant to read, don’t you think?

I want to spend more time in this language. And quick googling shows it might be possible to even interact with .NET.

The end

I was trying to recall where I’ve got the idea for Head- and Tail-like methods in C#, but my memory failed me. Never mind. It was fun anyway. Especially checking other languages. And especially Haskell. I’m definitely going to explore hs-dotnet more one day.

Related post.

Profile Picture Jiří Činčura is .NET, C# and Firebird expert. He focuses on data and business layers, language constructs, parallelism, databases and performance. For almost two decades he contributes to open-source, i.e. FirebirdClient. He works as a senior software engineer for Microsoft. Frequent speaker and blogger at www.tabsoverspaces.com.