First draft
This commit is contained in:
parent
1c9352bb2b
commit
7799d5f43f
|
@ -0,0 +1,118 @@
|
||||||
|
#+title: Elegant Functional Programming Application Architecture
|
||||||
|
#+description: An elegant and working code architecture
|
||||||
|
#+keywords: blog static
|
||||||
|
#+author: Yann Esposito
|
||||||
|
#+email: yann@esposito.host
|
||||||
|
#+date: [2021-09-26 Sun]
|
||||||
|
#+lang: en
|
||||||
|
#+options: auto-id:t
|
||||||
|
#+startup: showeverything
|
||||||
|
|
||||||
|
In this article I will expose you about how to architect your application
|
||||||
|
using function programming paradigm.
|
||||||
|
|
||||||
|
It is not tied to any programming language.
|
||||||
|
The principles in this article could probably be used by most programming
|
||||||
|
languages.
|
||||||
|
* Pre
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: pre
|
||||||
|
:END:
|
||||||
|
|
||||||
|
First note that many /functional programming languages/ are not functional.
|
||||||
|
They all provide holes in order to trick the system to be faster.
|
||||||
|
Also for internal use, some advanced feature might be provided.
|
||||||
|
|
||||||
|
1. Type-classes ; this is not something I consider belongs to functional programming.
|
||||||
|
2. Clojure Protocols, ~defmulti~ ; are not something I would consider belongs
|
||||||
|
to functional programming.
|
||||||
|
|
||||||
|
And many other languages features.
|
||||||
|
Let be radical functional programmer.
|
||||||
|
|
||||||
|
Radical, means going back to the roots.
|
||||||
|
|
||||||
|
So here we go.
|
||||||
|
Functional programming is about manipulating functions with the meaning of
|
||||||
|
*mathematical functions* not what most programming language call functions.
|
||||||
|
A "function" in C, Clojure, Python, Java, Javascript is *not* a function in
|
||||||
|
the functional programming sense.
|
||||||
|
OTOH in Haskell it is /often/ a real function, but not always.
|
||||||
|
|
||||||
|
So a mathematical function is something that takes a bunch of parameters
|
||||||
|
and return a value (or not, we should allow partial functions unfortunately).
|
||||||
|
And if you provide the same parameter twice the result will always be the same.
|
||||||
|
Also, it should not have any side effect, for example, it should log, emit
|
||||||
|
sounds, ... nothing.
|
||||||
|
|
||||||
|
Whoa whoa... Give me a minute here. Why would we want to ... do nothing at
|
||||||
|
all?
|
||||||
|
Isn't writing an application about doing things?
|
||||||
|
Yes, it is.
|
||||||
|
|
||||||
|
Let just say, that, you generally, don't need more than plunging a
|
||||||
|
pure function graph into a context where an interpreter will be able to use
|
||||||
|
and this big pile of pure functions will be used to have side effect.
|
||||||
|
A big ball of pure functions are reproducible, easy to test, easy to
|
||||||
|
analyze and reason about.
|
||||||
|
Easy to expose business logic out of technical detail to access data,
|
||||||
|
etc...
|
||||||
|
|
||||||
|
So I will provide a system as radical, and simple as possible that will
|
||||||
|
have a lot of good properties that advanced functional programming methods
|
||||||
|
(like Monad, Monads transformers, Free Monads, Effect systems) are trying
|
||||||
|
to achieve.
|
||||||
|
|
||||||
|
* The Architecture
|
||||||
|
:PROPERTIES:
|
||||||
|
:CUSTOM_ID: the-architecture
|
||||||
|
:END:
|
||||||
|
|
||||||
|
The main architecture is based on the /Service/ paradigm.
|
||||||
|
A service will be a sub-application living at runtime, it will depends on
|
||||||
|
other services, and have an internal state.
|
||||||
|
|
||||||
|
So in the end your application should look like an acyclic graph of services.
|
||||||
|
|
||||||
|
Every service has an init phase, then a living phase, then a stop phase.
|
||||||
|
Every service declare a set of methods to be used and every service will
|
||||||
|
keep an internal implicit state.
|
||||||
|
|
||||||
|
Even if this look a lot like Object oriented programming.
|
||||||
|
It is in fact a quite radical functional programming architecture.
|
||||||
|
|
||||||
|
#+begin_src clojure
|
||||||
|
(def Interface
|
||||||
|
(function-1 [arg-1 arg-2] "a function"))
|
||||||
|
|
||||||
|
;; Main implementation of /my-service/
|
||||||
|
(defservice my-service
|
||||||
|
Interface
|
||||||
|
[sub-service-1
|
||||||
|
sub-service-2
|
||||||
|
,,,
|
||||||
|
sub-service-n]
|
||||||
|
(init [initial-ctx]
|
||||||
|
(let [service-state
|
||||||
|
(core/init {:sub-service-1 sub-service-1 ,,,})]
|
||||||
|
(into initial-ctx service-state)))
|
||||||
|
|
||||||
|
(function-1 [ctx arg-1 arg-2]
|
||||||
|
(core/function-1 ctx arg-1 arg-2)))
|
||||||
|
|
||||||
|
;; A test implementation of /my-service/
|
||||||
|
(defservice my-test-service
|
||||||
|
Interface
|
||||||
|
[sub-service-1
|
||||||
|
sub-service-2
|
||||||
|
,,,
|
||||||
|
sub-service-n]
|
||||||
|
(init [initial-ctx]
|
||||||
|
(let [service-state
|
||||||
|
(test/init {:sub-service-1 sub-service-1 ,,,})]
|
||||||
|
(into initial-ctx service-state)))
|
||||||
|
|
||||||
|
(function-1 [ctx arg-1 arg-2]
|
||||||
|
(test/function-1 ctx arg-1 arg-2)))
|
||||||
|
|
||||||
|
#+end_src
|
Loading…
Reference in a new issue