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.

110 lines
3.8 KiB

11 months ago
#+TITLE: Are Services superior to Free Monads?
#+AUTHOR: Yann Esposito
#+EMAIL: yann@esposito.host
#+DATE: [2021-01-10 Sun]
#+KEYWORDS: haskell, clojure, architecture, programming
#+DESCRIPTION: Here is a simple description on how to architect a big functional programming application.
#+OPTIONS: auto-id:t toc:nil
#+begin_abstract
TODO
#+end_abstract
A recurring hot topic in the functional programming world is how to make
your code scale while keeping professionnal level of code quality.
Quite often in the functional programming we communities and talk people
are focusing on enhancing specifics...
To organise your code in a functional paradigm there are many concurrent proposals.
And structuring a code application is challenging.
The way you need to structure the code generally need to reach a few
properties.
1. You should make it easy to test your code
2. You need to support modern features any modern application is expected
to provide. Typically ability to write logs, if possible send structured
logs events.
3. The code should try to help people focalise on the business logic and
put aside irrelevant technical details.
4. Split your applications into smaller (ideal composable) components
5. Control accesses between different components of your applications
The design space is quite open.
In Haskell for example, there are different proposed solutions.
One of my preferred one to start with is the Handler
Pattern[fn:handler_pattern].
Because it doesn't need any advanced Haskell knowledge to understand.
And also it prevents a classical overabstraction haskell curse I often see
within Haskellers.
No premature abstraction here.
No typeclass.
The main principle behing it is that you create /handlers/.
Handlers are /component/ focused that each provide a set of methods and
functions already initialized.
[fn:handler_pattern]: https://jaspervdj.be/posts/2018-03-08-handle-pattern.html
* Monads, MTL, RIO, Handler Pattern, Free Monad
:PROPERTIES:
:CUSTOM_ID: monads--mtl--rio--handler-pattern--free-monad
:END:
There are a lot of solutions to architecture a program while keeping all
the best properties of functional programming as well as best professional
practices.
Here too, there are different level of looking at the problem of code
organisation.
On the very high level, an application is often understood as a set of
features.
But for all of thoses features to work together it is generally a lot of
work to organise them.
So we can descend the level to look at code organisation.
Files organisation, how to group them.
Structure of the code organisation.
How to put test, etc...
If you strive for composability you generally try to understand how to
group "components" and ask yourselve what a componentn should contain.
Here is a solution.
* Free Monads/Effect System
:PROPERTIES:
:CUSTOM_ID: free-monads-effect-system
:END:
Foreword, semantic vs syntax.
The kind of best way to talk about semantic and forget about the syntax is
to deal directly with a simplified representation of the AST.
Overall API:
#+begin_src clojure
(interpret-with
[effect-1 effect-2 ... effect-n]
(let [admin-user (get-in-config [:user :admin :user-id])
admin (get-user admin-user-id)
admin-email (get admin :email)]
(log "Admin email" admin-email)
admin-email))
#+end_src
It will be up to the actual instanciation of all =effect-*= to change the
interpretation of the body.
So some effect could have different interpreation of specific symbols.
So here we can imagine that =get-in-config=, =get-user= and =log= are
handlers specified in the effects.
One advantage is that to test your code you can simply use stubbed effects.
One can use a list users
Real effects and free monads are in fact more powerful than this example
is showing.
For example, within a free monad, even =let= semantic would be changed.
But let's not take this rabbit hole in this article right now.