You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
12 KiB
247 lines
12 KiB
% -*- mode: latex; coding: utf-8; TeX-master: ../thesis -*-
|
|
% !TEX TS-program = pdflatexmk
|
|
% !TEX encoding = UTF-8 Unicode
|
|
% !TEX root = ../thesis.tex
|
|
|
|
\section{Learning Functional Programming}
|
|
|
|
Within the last decade, concepts from functional programming have been brought into the daily life
|
|
of almost every programmer. There are many events that contributed to this gain in popularity:
|
|
|
|
In 2007, C\# 3.0 was released, which introduced lambda expressions and laid the foundations for
|
|
turning C\# into a hybrid Object-Oriented / Functional language\autocite{csharp-functional}.
|
|
Two years later, Ryan Dhal published the initial version of Node.js, eliminating JavaScript's
|
|
ties to the browser and introducing it as a server-side programming language, increasing the
|
|
adoption of JavaScript further.
|
|
In 2013, Java 8 was released and brought support for lambda expressions and streams.
|
|
Within the same time frame, Python has been rapidly growing in popularity\autocite{python-popularity}.
|
|
|
|
Further, many new multi-paradigm programming languages have been introduced,
|
|
including Rust, Kotlin, Go and Dart. They all have functions as first-class citizens in
|
|
the language since their initial release.
|
|
|
|
With these developments, it can be said that functional programming has emerged
|
|
from niche use-cases and academia to truly arrive in the wider programming community.
|
|
For example Rust, the `most popular programming language' for 5 years in a row (2016--2020)
|
|
according to the Stack Overflow Developer survey\autocite{rust-loved}, has been significantly
|
|
influenced by functional programming languages\autocite{rust-functional}. Further, in idiomatic
|
|
Rust code, a functional style can be clearly observed\footnote{A simple example for this may be
|
|
that variables are immutable by default.}.
|
|
|
|
Learning a purely functional programming language increases fluency with these concepts and
|
|
teaches a different way to think and approach problems when programming. Due to this, many
|
|
people recommend learning a functional programming
|
|
language\autocite{blog1-funcprog}\autocite{blog2-funcprog}\autocite{blog3-funcprog}\autocite{blog4-funcprog},
|
|
even if one may not end up using that language at all\autocite{quora-funcprog}.
|
|
|
|
Most literature about functional programming,
|
|
including academia and online resources like blogs, contain code examples written in Haskell.
|
|
Further, according to the Tiobe Index\autocite{tiobe-index}, Haskell is also the most popular
|
|
purely functional programming language\autocite{comparison-functional-languages}.
|
|
|
|
\section{Haskell}
|
|
|
|
Haskell, the \textit{lingua franca} amongst functional programmers, is a lazily-evaluated, purely functional programming
|
|
language. While Haskell's strengths stem from all it's features like its advanced type system, pattern matching and more,
|
|
these features are also what makes Haskell famously hard to learn\autocite{haskell-hard-one}\autocite{haskell-hard-two}\autocite{haskell-hard-three}\autocite{haskell-hard-four}.
|
|
|
|
Beginner Haskell programmers face a very distinctive challenge in contrast to learning a new, non-functional programming language:
|
|
Not only do they need to learn a new language with an unusual syntax (compared to imperative or object-oriented languages), they
|
|
also need to change their way of thinking and reasoning about problems.
|
|
For example, the renowned quicksort-implementation from the Haskell Introduction Page\autocite{haskell-quicksort}:
|
|
|
|
\begin{listing}
|
|
\begin{haskellcode}
|
|
quicksort :: Ord a => [a] -> [a]
|
|
quicksort [] = []
|
|
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
|
|
where
|
|
lesser = filter (< p) xs
|
|
greater = filter (>= p) xs
|
|
\end{haskellcode}
|
|
\caption{Quicksort implementation in Haskell}\label{code:haskell-quicksort}
|
|
\end{listing}
|
|
|
|
While this is only a very short and clean piece of code, these 6 lines already pose many challenges to non-experienced Haskellers:
|
|
|
|
\begin{itemize}
|
|
\item The function's signature with no `fn' or `func' statement as they often appear in imperative languages
|
|
\item The pattern matching, which would be a `switch' statement or a chain of `if / else' conditions
|
|
\item The deconstruction of the list within the pattern matching
|
|
\item The functional nature of the program, passing `(< p)' (a function returning a function) to another function
|
|
\item The function call to `filter' without parenthesised arguments and no clear indicator at which arguments
|
|
it takes and which types are returned
|
|
\end{itemize}
|
|
|
|
Although some of these constructs also exist in imperative or object-oriented languages, the cumulative difference
|
|
is not to underestimate and adds to Haskell's steep learning curve.
|
|
|
|
\section{Goals}
|
|
|
|
As demonstrated in the example above, learning a new paradigm and syntax at the same time
|
|
can be daunting and discouraging for novices.
|
|
The entry barrier for functional programming should be lowered by
|
|
using a modern, multi-paradigm language with a clear and familiar syntax. The functional
|
|
programming beginner should be able to focus on the paradigm first, and then change to a language
|
|
like Haskell to fully get into functional programming.
|
|
|
|
To achieve this goal, this thesis will consist of two parts.
|
|
In the first part, writing functional code will be made as easy as possible. This means that
|
|
a programming language with an easy and familiar syntax should be chosen. Optimally, this language
|
|
should already support functions as first-class citizens. Additionally, it should be statically
|
|
typed, as a static type system makes it easier to reason about a program and can support the
|
|
programmer while writing code.
|
|
In the second part, a linter will be created to check code for non-functional statements. To achieve
|
|
this, a definition of what functional purity means has to be selected and a ruleset has to be
|
|
worked out and implemented into a static analysis tool.
|
|
|
|
\section{Why Go}\label{sec:why-go}
|
|
|
|
The language of choice for this task is Go, a statically typed, garbage-collected programming language
|
|
designed at Google in 2009\autocite{golang-publish}. With its strong syntactic similarity to C, it should
|
|
be familiar to most programmers.
|
|
|
|
Go strives for simplicity and its syntax is extremely small and easy to learn. For example, the
|
|
language consists of only 25 keywords and purposefully omits constructs like the ternary operator
|
|
(<bool> ? <then> : <else>) as a replacement for the longer `if <bool> \{ <then> \} else \{ <else> \}'
|
|
for clarity. `A language needs only one conditional control flow construct'\autocite{go-ternary},
|
|
and this also holds true for many other constructs. In Go, there is usually only one way
|
|
to express something, improving the clarity of code.
|
|
|
|
Due to this clarity and unambiguity, the language is a perfect fit to grasp the concepts and trace
|
|
the inner workings of functional programming. It should be easy to read code and understand what
|
|
it does without a lot of experience with the language.
|
|
|
|
There are however a few downsides of using Go. Currently, Go does not have polymorphism, which means
|
|
that functions always have to be written with specific types. Due to this, Go also does not include
|
|
common list processing functions like `map', `filter', `reduce' and more\footnote{Although Go does
|
|
have some polymorphic functions like `append', these are specified as built-in functions in the
|
|
language and not user-defined}. Further, Go does not have a built-in `list' datatype. However, Go's
|
|
`slices' cover a lot of use cases for lists already. Section~\ref{sec:go-slices} covers this topic
|
|
in more detail.
|
|
|
|
\section{Existing Work}
|
|
|
|
With Go's support of some functional aspects, patterns and best practices have emerged that make
|
|
us of functional programming constructs.
|
|
For example, in the \textit{net/http} package of the standard library, the function
|
|
\begin{gocode}
|
|
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
|
|
\end{gocode}
|
|
is used to register functions for http server handling\autocite{go-http-doc}:
|
|
|
|
\begin{code}
|
|
\begin{gocode}
|
|
func myHandler(w http.ResponseWriter, r *http.Request) {
|
|
// Handle the given HTTP request
|
|
}
|
|
|
|
func main() {
|
|
// register myHandler in the default ServeMux
|
|
http.HandleFunc("/", myHandler)
|
|
http.ListenAndServe(":8080", nil)
|
|
}
|
|
\end{gocode}
|
|
\caption{Go web server handler function}
|
|
\end{code}
|
|
|
|
Using functions as function parameters or return types is a commonly used feature in Go, not just
|
|
within the standard library. Furthermore, design patterns have emerged within the community
|
|
that use functional concepts. An example of this are `functional options'.
|
|
|
|
\subsection{Functional Options}
|
|
|
|
The `functional options' pattern has been outlined in Dave Cheney's blog post `Functional options
|
|
for friendly APIs'\autocite{functional-options} and is a great example on how to use the support for multiple paradigms.
|
|
The basic idea with functional options is that a type constructor receives an unknown (0-n) amount
|
|
of options:
|
|
\begin{code}
|
|
\begin{gocode}
|
|
func New(requiredSetting string, opts ...option) *MyType {
|
|
t := &MyType{
|
|
setting: requiredSetting,
|
|
featureX: false,
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(t)
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
type option func(t *MyType)
|
|
\end{gocode}
|
|
\caption{Constructor with functional options}
|
|
\end{code}
|
|
|
|
These options can then access the instance of \mintinline{go}|MyType| to modify it accordingly,
|
|
for example:
|
|
|
|
\begin{code}
|
|
\begin{gocode}
|
|
func EnableFeatureX() option {
|
|
return func(t *MyType) {
|
|
t.featureX = true
|
|
}
|
|
}
|
|
\end{gocode}
|
|
\caption{Example for a functional option}
|
|
\end{code}
|
|
|
|
To enable feature X, `New' can be called with that option:
|
|
\begin{gocode}
|
|
t := New("required", EnableFeatureX())
|
|
\end{gocode}
|
|
|
|
With this pattern, it is easy to introduce new options without breaking old usages of the API.
|
|
Furthermore, the typical `config struct' pattern can be avoided and meaningful zero values
|
|
can be set.
|
|
|
|
A more extensive example on how functional options are implemented and used can be found in
|
|
Appendix~\ref{appendix:funcopts}.
|
|
|
|
\begin{quote}
|
|
In summary
|
|
\begin{itemize}
|
|
\item Functional options let you write APIs that can grow over time.
|
|
\item They enable the default use case to be the simplest.
|
|
\item They provide meaningful configuration parameters.
|
|
\item Finally they give you access to the entire power of the language to initialize complex values.
|
|
\end{itemize}\autocite{functional-options}
|
|
\end{quote}
|
|
|
|
While this is a great example of what can be done with support for functional concepts, a purely functional approach to
|
|
Go has so far been discouraged by the core Go team, which is understandable for a multi-paradigm programming language.
|
|
However, multiple developers have already researched and tested Go's ability to do functional programming.
|
|
|
|
\subsection{Functional Go?}
|
|
|
|
In his talk `Functional Go'\autocite{func-go-talk}, Francesc Campoy Flores analysed some commonly used functional
|
|
language features in Haskell and how they can be copied to Go. Ignoring speed and stack overflows due to non-existent
|
|
tail call optimisation\autocite{go-tco}, the main issue is with the type system and the missing polymorphism.
|
|
|
|
\subsection{go-functional}
|
|
|
|
In July 2017, Aaron Schlesinger, a Go programmer for Microsoft Azure, gave a talk on functional programming with Go.
|
|
He released a repository\autocite{go-functional} that contains `core utilities for functional Programming in Go'.
|
|
The project is currently unmaintained, but showcases functional programming concepts like currying, functors and
|
|
monoids in Go. In the `README' file of the repository, he also states that:
|
|
\begin{quote}
|
|
Note that the types herein are hard-coded for specific types, but you could
|
|
use code generation to produce these FP constructs for any type you please!
|
|
\autocite{go-functional-readme}
|
|
\end{quote}
|
|
|
|
\section{Verdict}
|
|
|
|
The aforementioned projects showcase the main issue with functional programming in Go: the missing
|
|
helper functions that are prevalent in functional languages and that they currently cannot be implemented
|
|
in a generic way.
|
|
|
|
To make functional programming more accessible in Go, this thesis will research what the most used
|
|
higher-order functions are and implement them with a focus on usability.
|
|
Furthermore, to learn purely functional programming, a list of rules for pure functional code should
|
|
be curated and implemented in a static code analysis tool. This tool can then be used to check
|
|
existing code and report constructs that are not functional.
|