TL;DR My F# version of the image processing experiments may not be as short as the bash version but it is by far the most beautiful to look at. Find the source code in this repository: spitzfaust/image-processing-experiments
The introduction to the image processing experiments can be found in my first article of the series.
Just to recall the program should do the following:
- Read in an image as the first argument passed to it on the command line.
- Print the file path, width and height of the image to the console.
- Display the original image and a blurred version of the image.
- Convert the image to grayscale and save it besides the original image as
After implementing a solution in Rust and in Bash I decided to build the final implementation with a functional programming language. The first language I wanted to use was Haskell but I was not familiar with the development processes and tooling of the language and I had a deadline to hold which repelled me from using it. Just recently I had looked into F# and because my professional job has provided me with some expertise with the .NET ecosystem I chose F# as the language for the third version of the program.
Programming in F# or other functional languages requires a different thinking process opposed to non functional languages. Combined with the syntax of the language this leads to more concise and readable code. While the syntax of the language is very concise and abstains from the myriad of brackets that other languages require it initially is a bit tricky and the indent based delimitation of code blocks can lead to some funny frustrating errors. A great page to learn the basics of the F# syntax is F# syntax in 60 seconds from F# for fun and profit by Scott Wlaschin.
let showImage path =
let imgProcessInfo = ProcessStartInfo()
imgProcessInfo.UseShellExecute <- true
imgProcessInfo.FileName <- path
Diagnostics.Process.Start(imgProcessInfo) |> ignore
As an example for a function I wrote you can see the
showImage function that accepts a
path to an image as parameter. Some interesting operators are the assignment operator
<- which can assign values to variables and the forward pipe operator
|> which can be used to pass the result of a function on the left side to the function on the right side. The
ignore function that gets the result of the
Diagnostics.Process.Start(imgProcessInfo) call can be used to explicitly ignore the result of a function. If the ignore function would not be used in this case a compile time warning would occur that the result of the function is not used. This prevents implicit side effects and unhandled results of function calls.
Because F# is part of the .NET ecosystem all .NET libraries can be used by it. This made finding an image library fairly easy. I chose the great SixLabors.ImageSharp library which provided all the functionalities I needed and more. Using this .NET library which is written in C# is a bit different than using native F# libraries. Functions need brackets around them and parameters are savagely separated by commas instead of spaces only. Apart from those small annoyances using the library was really easy and enabled me to quickly implement a solution for the problem at hand.
In this article I wrote a little bit about F# and using it to implement a simple program. This was the last article of the image processing experiments series. The source code for all the implementations can be found in this repository: spitzfaust/image-processing-experiments.