WRITING: Started writing the functional article
This commit is contained in:
parent
3212a5ccfb
commit
867585c7cc
@ -0,0 +1,257 @@
|
|||||||
|
Date: 2025/04/28
|
||||||
|
Desc: I have decided to begin the journey of learning functional programming. Here is my experience.
|
||||||
|
|
||||||
|
# What is a Functional Programming Language: Featuring Elixir.md
|
||||||
|
|
||||||
|
<img src="/journal/elixir-logo.png" alt="Jet Brains Logo" style="background-color: white; border-radius: 15px; padding: 10px; margin-inline: 10px; margin-block: 2.5%;" width="300">
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
###### Author: Hayden Hargreaves
|
||||||
|
|
||||||
|
###### Published: 05/??/2025
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Many programmers tend to avoid **functional programming** due to its perceived complexity, myself included.
|
||||||
|
But is functional programming really that much more complex? My goal is to break my fear of functional
|
||||||
|
programming, [monads](https://en.wikipedia.org/wiki/Monad_(functional_programming)), [functors](https://en.wikipedia.org/wiki/Functor), and all those *scary* terms frequently tossed around in the functional
|
||||||
|
space.
|
||||||
|
|
||||||
|
How am I going to do this? Well, I am definitely not going to start with [Haskell](https://www.haskell.org). For those who have
|
||||||
|
never seen or heard of Haskell, this decision might be hard to understand. I will not explain myself too
|
||||||
|
much. But I will provide a code snippet from the Haskell docs and let you decide if it a worthy language
|
||||||
|
for someone who has never written a line of functional code in their life.
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
primes = filterPrime [2..] where
|
||||||
|
filterPrime (p:xs) =
|
||||||
|
p : filterPrime [x | x <- xs, x `mod` p /= 0]
|
||||||
|
```
|
||||||
|
|
||||||
|
Personally, the Haskell language is far too esoteric for my liking. Eventually, I would love to be able
|
||||||
|
to, at the very least, **read** Haskell code, but not yet. So this put me a spot to select from a large
|
||||||
|
selection of languages. I did not want to use a language like **Python** or **JavaScript** which can be
|
||||||
|
written to *seem* functional. No, I wanted to write a **real** functional language. A common list of as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
- [Haskell](https://www.haskell.org)
|
||||||
|
- [Erlang](https://www.erlang.org)
|
||||||
|
- **[Elixir](https://elixir-lang.org)**
|
||||||
|
- [Scala](https://www.scala-lang.org)
|
||||||
|
- [Clojure](https://clojure.org)
|
||||||
|
- [OCaml](https://ocaml.org)
|
||||||
|
- [Common Lisp](https://lisp-lang.org)
|
||||||
|
|
||||||
|
To avoid offending all of the "functional bros" I will not explain my thought process much. However, I
|
||||||
|
will mention that I tried to learn **OCaml** a years ago and could not enjoy it. Maybe that was because I
|
||||||
|
was not as advanced as I am today, or maybe I did not have the proper mindset. Regardless, that ruled
|
||||||
|
out OCaml. I have no experience in any of the other languages, so I did what most would do. I let Reddit
|
||||||
|
decide! The [Elixir](https://www.reddit.com/r/elixir/) Reddit page was very informative as well as providing me with a link to the
|
||||||
|
[2024 Stack Overflow Survey](https://survey.stackoverflow.co/2024/technology#top-paying-technologies) which described Erlang and Elixir as the top two paying languages.
|
||||||
|
|
||||||
|
With that out of the way, lets talk more about what functional programming is.
|
||||||
|
|
||||||
|
## What is Functional Programming
|
||||||
|
|
||||||
|
At the most basic level, functional programming is defined as a ["programming paradigm where programs are
|
||||||
|
constructed by applying and composing functions"](https://en.wikipedia.org/wiki/Functional_programming). In laymen terms, a majority of the code written in these
|
||||||
|
languages is just functions. Everything can be expressed as some composition of functions. For this reason,
|
||||||
|
a strong understanding of mathematics can be hugely beneficial to functional programmers.
|
||||||
|
|
||||||
|
>
|
||||||
|
> "Functional programming evolved from lambda calculus"
|
||||||
|
>
|
||||||
|
|
||||||
|
#### Pure Functions
|
||||||
|
|
||||||
|
Another key difference between other paradigms is the strict immutability. Functional programming introduces
|
||||||
|
the term "[pure function](https://en.wikipedia.org/wiki/Pure_function)" which is any function that can be run (any amount of times) and will always produce
|
||||||
|
the same output, **and cannot be affected by (or affect) any mutable state.** The intention of writing pure
|
||||||
|
functions is to prevent [side effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), "any observable effect other than its primary purpose."
|
||||||
|
|
||||||
|
To simplify, the two properties of a **pure** function are:
|
||||||
|
|
||||||
|
1) The function will return an identical result for identical arguments. With ***no*** variation for any reason,
|
||||||
|
including reference arguments.
|
||||||
|
2) The function has no *side effects*, no mutation of local static variables, non-local variables, etc.)
|
||||||
|
|
||||||
|
An example of a pure function in C++ may look as follows:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void f() {
|
||||||
|
static std::atomic<unsigned int> x = 0;
|
||||||
|
++x;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The function `f()` is pure because it follows the above properties.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
int f_i() {
|
||||||
|
static int x = 0;
|
||||||
|
++x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, the function `f_i()` is impure because it returns a variation of a static variable.
|
||||||
|
|
||||||
|
Countless more examples of impure functions can be found [here](https://en.wikipedia.org/wiki/Pure_function#Impure_functions).
|
||||||
|
|
||||||
|
**NOTE:** I wanted to write these examples in Elixir, but due to its functional nature, it would be very hard.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Functions are First Class Citizens
|
||||||
|
|
||||||
|
Another key property of functional programming is that functions are known as [first class citizens](https://en.wikipedia.org/wiki/First-class_citizen) which
|
||||||
|
means they can be assigned to variables, passed into other functions as arguments ([higher order functions](https://en.wikipedia.org/wiki/Higher-order_function))
|
||||||
|
and returned from functions. This is not uncommon in modern programming languages so I will not provide an
|
||||||
|
in-depth explanation.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Recursion
|
||||||
|
|
||||||
|
The death of many modern programming languages, recursion, is one of the many strengths of functional languages.
|
||||||
|
[Recursion](https://en.wikipedia.org/wiki/Recursion_(computer_science)) occurs when a functions calls itself. This is the most common way to implement [iteration](https://en.wikipedia.org/wiki/Iteration) in
|
||||||
|
functional programming. For example a simple loop in any common procedural language:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::vector<int> numbers = {1, 2, 4, 8, 16};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numbers.size(); i++) {
|
||||||
|
std::cout << numbers[i] << " ";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Can be written in a similar way using recursion with Elixir:
|
||||||
|
|
||||||
|
```Elixir
|
||||||
|
def print_list([]), do: IO.puts("") # Base case: when the list is empty, this function is called
|
||||||
|
|
||||||
|
def print_list([head | tail]) do
|
||||||
|
IO.puts(head) # Print the first element in the list (head)
|
||||||
|
print_list(tail) # Call the function again with the remaining elements
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Programmers coming from other languages might freak when they see so much recursion, and it is not wrong to
|
||||||
|
worry. In procedural languages, recursion requires the stack to keep a record of each function call, and when
|
||||||
|
recursion is used to extreme levels, the stack becomes very large and uses lots of memory. Not very efficient!
|
||||||
|
However, functional programming languages found a solution, [tail recursion](https://en.wikipedia.org/wiki/Tail_call). Tail recursion allows the compiler
|
||||||
|
to creation a subroutine which represents the final action (final function call) and place it onto the stack,
|
||||||
|
allowing the program to jump right to the end without having to store the entire recursive loop in stack memory.
|
||||||
|
This reduces the memory stack space from linear or O(N) to constant or O(1). A huge performance increases in
|
||||||
|
recursive programs.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Strict vs. Non-Strict Evaluation
|
||||||
|
|
||||||
|
Functional languages can be categorized by *strict (eager)* or *non-strict (lazy)* evaluation. The different
|
||||||
|
evaluation styles refer to how arguments are processed inside an expression under evaluation. This concept
|
||||||
|
is quite complex and this overview does not warrant the necessity for such details. However, a simple example
|
||||||
|
can explain (on a higher level) the difference. **"Under strict evaluation, the evaluation of any term containing
|
||||||
|
a failing subterm fails.** An example makes this a bit easier to understand. We will use the same example to described
|
||||||
|
both evaluation types.
|
||||||
|
|
||||||
|
```
|
||||||
|
print length([2+1, 3*2, 1/0, 5-4])
|
||||||
|
```
|
||||||
|
|
||||||
|
In a language categorized as *strict*, the expression above will fail, due to the term `1/0` failing. However, in a language
|
||||||
|
with *non-strict* evaluation, the length expression will return 4 because it does attempt to evaluate the subterms, which
|
||||||
|
result in failure.
|
||||||
|
|
||||||
|
To summarize, *lazy* evaluation does not attempt to evaluate function arguments unless their values are required for the
|
||||||
|
function call itself.
|
||||||
|
|
||||||
|
***NOTE: Elixir is a strictly evaluated language.***
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Referential Transparency
|
||||||
|
|
||||||
|
Another very detailed differentiation from imperative programming languages. [Referential transparency](https://en.wikipedia.org/wiki/Referential_transparency) stems from linguistic
|
||||||
|
roots and language by extension. Applications in this context (computer science) states that "a language is *referentially
|
||||||
|
transparent* when an expression built from another, replaces the expression with a subexpression which represents the same value
|
||||||
|
and does not change the value of the expression." That sounds very complicated, and it is, but to be simple, expressions cannot
|
||||||
|
be modified, only replaced.
|
||||||
|
|
||||||
|
Functional programming languages do not have **assignment statements**, because a value can never be changed once it is defined.
|
||||||
|
This is a result of the language allowing variables to be swapped with their value at any time. Therefore, functional languages
|
||||||
|
are referentially transparent.
|
||||||
|
|
||||||
|
Another example will make this easier to understand. Consider the following assignment statement from [C](https://en.wikipedia.org/wiki/C_(programming_language)). This assignment changes
|
||||||
|
the value assigned to the variable `x`. If the initial value of `x` was to be `1`, the result would be `10`. But when called again, the
|
||||||
|
result would be `100`. Since replacing `x` with `10` or `100` gives the program different meaning, it is not *referentially transparent.*
|
||||||
|
|
||||||
|
```c
|
||||||
|
x = x * 10
|
||||||
|
```
|
||||||
|
|
||||||
|
However, consider the following elixir function, `f(x)`. This implementation is *transparent*, as it does not modify the value of x,
|
||||||
|
which results in the absence of **side effects**. Instead, it returns a value which can be used to replace an existing value. This
|
||||||
|
is the standard in functional languages which results in their referential nature.
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def f(x) do
|
||||||
|
x + 1
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
#### Data Structures
|
||||||
|
|
||||||
|
The last key difference between imperative and function languages is their representation of data structures. Functional languages
|
||||||
|
admit a [purely functional](https://en.wikipedia.org/wiki/Purely_functional_data_structure) data structure, where the biggest difference is immutability. Purely functional data structures are
|
||||||
|
strongly immutable, which allows for many advantages, such as [persistence](https://en.wikipedia.org/wiki/Persistent_data_structure), quick copy and [thread safety](https://en.wikipedia.org/wiki/Thread_safety).
|
||||||
|
Of the advantages listed, I will only go in detail about one: **persistence.** Purely functional data structures are persistent which
|
||||||
|
means that when modifications occur, the previous state will be kept unmodified. A common comparison is to the non-persistent array
|
||||||
|
which admits a **destructive update** which cannot be undone, since no previous versions are kept.
|
||||||
|
|
||||||
|
***NOTE: Elixir is not a purely functional language and as a result, does not implement a purely functional data structure.***
|
||||||
|
|
||||||
|
|
||||||
|
## Why Functional?
|
||||||
|
|
||||||
|
That was quite a long explanation, but it covered almost everything you would need to know to *begin* learning a functional language.
|
||||||
|
However, I did not explain why **I** decided to learn. Functional languages are rarely faster (or even as fast) as imperative languages
|
||||||
|
like C, so the choice was not made for performance. The most obvious choice: ***I want to.*** So many software developers spend too much
|
||||||
|
time worrying about what is "the best" or "the fastest." Maybe they should just be learning what they **want.**
|
||||||
|
|
||||||
|
Like I stated in the background, I, like many others, run at the sight of functional languages, but its about time I break that fear!
|
||||||
|
The next sections of this article will highlight my experience learning the **Elixir Programming Language,** so if you were only here for
|
||||||
|
the functional definition, this is a good stopping point.
|
||||||
|
|
||||||
|
## Why Elixir?
|
||||||
|
|
||||||
|
I have talked a lot about Elixir language but what exactly is it? Obviously its a functional programming language, but that's very vague.
|
||||||
|
"[Elixir](https://elixir-lang.org) is a dynamic, functional language for building scalable and maintainable applications." Elixir runs on the [Erlang](https://www.erlang.org) VM which is
|
||||||
|
known for creating fault tolerant, low-latency, distributed systems. The language can be installed and testing in the interactive elixir shell
|
||||||
|
`iex`, similar to pythons interactive shell. The interactive shell was a great tool for my own learning while reading through the documentation.
|
||||||
|
|
||||||
|
Elixir was first released in **2012** which makes it a newer language, but not as new as some (Odin, or Zig). With this comes a large user base
|
||||||
|
as well as a large developer ecosystem. But, what makes elixir stand out in that aspect is its compatibility with Erlang. Erlang, which appeared
|
||||||
|
in **1986** has a huge ecosystem of libraries and tools which work seamlessly in Elixir. Elixir comes packaged with a build tool, `mix` which allows
|
||||||
|
for compilation and interpretation of elixir code. Elixir *also* has a dedicated package manager, `hex`, which is comparable to `npm` in terms of
|
||||||
|
use. However, Elixir's ecosystem does not compare to the JavaScript ecosystem (what language does?).
|
||||||
|
|
||||||
|
One of the most popular uses of Elixir is with the [Phoenix Web Framework](https://www.phoenixframework.org). Phoenix, a full stack web framework that boasts its countless features
|
||||||
|
which include **LiveView**, a tool for building real-time web applications. Another popular library, **[Ecto](https://hexdocs.pm/ecto/Ecto.html)** is a SQL ORM that is built into the Phoenix
|
||||||
|
framework and allows for seamless database connections from your web application back end. (The Phoenix framework will come up more later.)
|
||||||
|
|
||||||
|
|
||||||
|
But who cares? Well, many large companies you have heard of use Elixir in their software. Some of which include WhatsApp, Discord, Heroku, Pepsico
|
||||||
|
and [more](https://elixir-lang.org/cases.html).
|
||||||
|
|
||||||
|
## The Beginning
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The repo:
|
||||||
|
https://github.com/haydenhargreaves/ElixirTodo
|
||||||
|
|
||||||
BIN
static/journal/elixir-logo.png
Normal file
BIN
static/journal/elixir-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
Loading…
x
Reference in New Issue
Block a user