Tuesday, August 11, 2020

Moving to Functional Thinking

I've been working on an educational-based site to help teachers evaluate students. I'm using F# for the backend API, and the regular use of a functional programming language has me thinking differently about how I code.

I recently needed to test a value to see if it is a GUID. If the value is a GUID, I want to use it. If the value isn't a valid GUID, I want to create a new GUID. In C#, this would be done like this:

if(Guid.TryParse(maybeGuidValue, out validGuidValue)
{
 myObject.ID = validGuidValue;
}
else
{
 myObject.ID = Guid.NewGuid();
}

This code is fairly straightforward, but a little problematic. First, the TryParse() method takes a string to test, maybeGuidValue, and an out parameter. If the method succeeds, meaning, the value is a valid GUID, the method returns true and the value validGuidValue is populated with the value in a GUID data type. If the value passed in is not a GUID, the method returns false and the out parameter should be ignored.

This code is tried and true in the C# world, but it isn't great. First, I'm not a fan of out parameters. This code construct is not pure or immutable since the method is returning one value as part of the method call and mutating another value by sending back an out parameter. Code like this requires special attention and is difficult to wrap in good unit tests.

In contrast, F# handles the same concept in a more intuitive manner. The code to TryParse a GUID looks like this:

let didParse, validGuidValue = Guid.TryParse maybeGuidValue
let ID = if didParse then validGuidValue else Guid.NewGuid()

This code calls the same TryParse method that is used in the C# code (F# shares the .NET libraries). Instead of having a single return value and an out parameter, this function returns a tuple with the boolean result of the parse operation along with the possibly populated GUID value. This construct is easier to see and much easier to unit test! The other obvious difference in the F# code is that it is only two lines! One line parses the value and the other line makes the assignment.

The F# code seems easier and much more intuitive, but it requires a change in thinking. The first change in thinking is embracing the power of tuples. Since tuples are relatively new in C#, it takes a mental effort to remember they are critical to F# and truly powerful. The second change in thinking is that if statements are expressions in F# as opposed to blocks in C#. An expression results in a value, allowing the assignment of ID, no matter how it is generated, to be made in a single line of code. The block construct in C# allows for branching, but the assignment code needs to be implemented in each logic branch. The block approach results in more code that can lead to ambiguity in how a value is assigned.

As I get more practice using F#, I am really enjoying the simplicity of code and the power the language has for me.

No comments:

Post a Comment