Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Tuesday, September 10, 2019

How to solve it? Crafting web apps using finite state machines - Part I

Code: https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps
Presentation: http://www.danbunea.ro/presentations/crafting%20web%20apps.html

PROBLEM


How should you start crafting a web app?


The requirements come, you have some UI mockups and now you need to start developing a web app. What is the first step you need to do?

Let's go back a bit. If this was not a specific web app, but rather generic:

  • What would our strategy be like?
  • What would be our goals?
  • Which steps would we have to do?
  • Which patterns could help us?
  • What tactics could we use?

In the following lines I will try to address exactly this questions. First we'll discuss the theory and then we'll develop a web app.

we'll start with a little bit of theory, then we'll put it in practice:



THEORY 

Strategy


We'll have to think a bit strategically and a little bit tactically. For the strategy, what would be our goals:

Goals

  • Break the complexity down (state machines, separation of concerns)
  • Build robustness into the system (atomicity with rollback)
Ok, but how can this be done?

Well, we'll do it in 4 Steps
  1. Finite state machine diagram (state/transitions starting from the UI)
  2. States (domain modelling for the states)
  3. Transitions (TDD the controller)
  4. Presentation/UI for the states (TDD the view)

Steps don't need to be sequential, in fact it is recommended to do the last two in parallel.

Tactics


We will be using a few Patterns:
  • finite state machines
  • separation of concerns using MVC
  • atomicity = all or nothing
and a few Techniques:
  • TDD - for the non UI
  • TDD - for the UI
  • Design by contract

PRACTICE


TodoMVC web application, because everyone loves to prove their framework with it, but we'll focus on what we said before.




Step 1 Draw the diagram of the UI state machine


So let's start with Step 1 Finite state machine

Our purpose is to create a state diagram with all the states and transitions we think we'll need. This is not an easy step, but it can clarify the entire development process further on. Let's see what can be done

Actions:
  • list
  • filter
  • add
  • check/unckeck
  • edit
  • delete
From the screens it also look like we'll have the following States:
  • list (filtered or not)
  • add
  • edit
and now let's put it in a diagram:




or:



Now using theis diagram we'll move to step 2 and we'll do the code according to it.


Step 2: Domain modelling



We will separate the different concerns using the MVC pattern. All the logic will be in the controller, the data in the model and we'll add the presentation later or someone else could do it in parallel.

Our data will have to be able to represent all the states in the diagram above: list/filter, add and edit

;What is a todo?

{:text "todo", :done? true}


;How do we know the status?
;When do we filter?


{:context {:status "list"
          :filter true}
:todos [{:text "todo 1" :done? true}
        {:text "todo 2" :done? false}]}


;What about edit?
;How do we know which do we edit?
;How do we order the todos?

{:context {:status "edit"
          :filter true
          :selected-id 1},
:todos {
        :1 {:id 1, :text "todo 1", :done? true, :time 1}
        :2 {:id 2, :text "todo 2", :done? false, :time 2}
        }}

;Let's carve it in stone:

(require '[clojure.spec.alpha :as s])

;SPEC
(s/def ::status #{"list" "edit" "add"})
(s/def ::filter #(or (false? %) (true? %)))
(s/def ::filter-with-nil #(or (nil? %) (false? %) (true? %)))
(s/def ::selected-id nat-int?)
(s/def ::context (s/keys :req-un [::status] :opt-un [::filter-id ::selected-id]))


(s/def ::id nat-int?)
(s/def ::text string?)
(s/def ::done? boolean?)
(s/def ::time number?)
(s/def ::todo (s/keys :req-un [::id ::text ::done? ::time]))
(s/def ::todos (s/map-of keyword? ::todo))

(defn edit-mode-has-a-valid-selected-id? [state]
  (if (= "edit" (get-in state [:context :status]))
    (some (into #{} (map :id (vals (:todos state)))) [(get-in state [:context :selected-id])])
    true
    ))

(s/def ::model (s/and
                 (s/keys :req-un [::context] :opt-un [::todos])
                 edit-mode-has-a-valid-selected-id?
                 ))

(comment 
(s/valid? ::model {:context {:status "edit"
          :filter true
          :selected-id 1}
:todos {
        :1 {:id 1, :text "todo 1", :done? true, :time 1}
        :2 {:id 2, :text "todo 2", :done? false, :time 2}
        }}))

Step 3: Solve the data problem


Now we know how we could represent our states as data in the model, let's test drive the different transitions which will al be functions in the controller. Normally I start with a plan, where I will know what I want to test.

The plan of the tests

 [ ] controller-should
 [ ] initialize-in-list-mode
 [ ] check&uncheck
 [ ] delete
 [ ] toggle-filters
 [ ] set a filter
 [ ] remove all filters
 [ ] set-add-mode
 [ ] save-a-new-todo
 [ ] set-list-mode
 [ ] set-one-for-edit
 [ ] save-a-changed-todo

Then we'll write the first one:

(deftest initialize-in-list-mode
         (is (= {:context {:status "list"} :todos   {}}
                (init!))))

which will obviously

Test fails! Good... now we write the code to make it pass:

;THE MODEL
(def model (atom {:context {:status "list"}}))



;THE CONTROLLER
(defn commit! [value atom-to-change]
  (reset! atom-to-change value))

(defn init! []
  (-> @model
    (assoc :todos {})
    (assoc-in [:context :status] "list")
    (commit! model)))



;THE TEST
(deftest initialize-in-list-mode
         (is (= {:context {:status "list"} :todos {}}
                (init!))))


Or:


Any refactorings? No, let's move to the second test and so on until we solve the entire data problem, one test at a time, making sure it fails, then making it pass, then refactoring the code. We end up with this:

The code is merged here:
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/merge_requests/1/diffs

The final code:
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/blob/master/test/todomvc/controller_should.cljs
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/blob/master/src/todomvc/controller.cljs

You'll probably be able to see that the names of the functions in our controller closely follow our transitions from the diagram.



Solve the data problem (TDD the model/controller) Done!

Now let's have another look again to see the correspondence between the transitions and the controller functions:





while the data in the model corresponds to:



Step 4: Build the UI


We will need now to make sure that our states can be represented on the screen.

For instance when we're in list mode, filtered:



How about edit state:



Now that we know how the data corresponds to the UI,
we can move on to doing the actual UI. We'll use react/reagent to break the UI into components:


Then we'll test drive the entire UI starting by planning the tests:

 [ ] views_should
 [ ] ender-the-main-screen
 [ ] render all sections
 [ ] not render main section when no todos
 [ ] render-input-text-component
 [ ] use-keys-on-input-text-component
 [ ] write something and hit enter
 [ ] hitting enter with no text
 [ ] hitting esc
 [ ] render todo input component
 [ ] add-a-new-todo-when-clicking-enter-or-go-to-list-on-escape
 [ ] render-todos-list-component
 [ ] no filter
 [ ] filter active
 [ ] filter completed
 [ ] render-todo-item-component
 [ ] normal mode
 [ ] completed mode
 [ ] editing mode
 [ ] toggle a todo item component
 [ ] render-todos-count-component
 [ ] no item left
 [ ] 1 item left
 [ ] 2 items left
 [ ] render-todos-filters-component
 [ ] render no filter
 [ ] render filter active
 [ ] render filter completed
 [ ] invoke-controller-filter-when-clicking-filters-in-todos-filters-component

It is worth mentioning that TDD-ing the UI can be split into two types of tests:
- render tests and
- interaction tests

For the render tests you send some data to your components, render it then check how it's rendered.
We start with a render test (we'll use enzyme):

(deftest render-the-main-screen
  (testing "render all sections"
    (let [component [views/screen-component data/one-todo-list-mode-no-filter]
          mounted (->> (r/as-element component)
                       (.mount js/enzyme))]
      (is (= 1 (-> mounted (.find ".header") .-length)))
      (is (= 1 (-> mounted (.find ".main") .-length)))
      (is (= 1 (-> mounted (.find ".footer") .-length)))
      (is (= 1 (-> mounted (.find "li.todo") .-length)))
      (is (= "0 items left" (-> mounted (.find "strong") .getDOMNode .-innerText)))
      )))

the test fails, so then we start writing our first component, making it pass, refactoring etc.

For the interaction tests, you render some data, then click a button and make sure the mocked function that should be invoked is actually invoked:

(deftest invoke-controller-filter-when-clicking-filters-in-todos-filters-component
  (let [invocations (atom [])
        component [views/todos-filters-component nil]
        mounted (->> (r/as-element component)
                     (.mount js/enzyme))]
    (with-redefs [controller/filter! #(swap! invocations conj [%])]
                 (testing "unfilter"
                   (reset! invocations [])
                   (-> mounted
                       (.find "#all")
                       (.simulate "click"))
                   (is (= [[nil]] @invocations)))
                ))

The code is merged here:
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/merge_requests/2/diffs

The final code:
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/blob/master/test/todomvc/views_should.cljs
https://gitlab.com/danbunea/how-to-solve-it-crafting-web-apps/blob/master/src/todomvc/views.cljs




Goals revised


Complexity problem -solved by making a state machine and using separation of concerns.
Robustness problem - TDD is a clear step forward but we can go further:

  • add atomicity into the transitions/operations (all or nothing)
  • design by contract, always making sure the states are valid states
But we'll see that in part II


Wednesday, August 29, 2018

Outside-In TDD the bank kata in Clojure/ClojureScript


Code: https://github.com/DanBunea/katas/tree/master/outside-in%20tdd%20bank%20kata 


In "London school" TDD or Outside-In we start from an acceptance test, then write unit tests. This is better explained as the double loop of TDD:




Some time ago, I found this great step by step explanation of Outside-In TDD, by Sandro Mancuso from Codurance:



However it was all Java and obviously OOP, so I wanted do the same thing but in Clojure, and fully functional: functions and not classes. So here we go:





Restrictions (adapted for functional):

1. Start with a module account that has the following functions:

(defn deposit [account amount] ...)
(defn withdraw [account amount] ...)
(defn print-statement [account] ...)

Threat the methods, as if they cannot return any values!

2. You're not allowed to add any new functions to the module
3. Strings and integeres are to be used for dates and amounts for simplicity
4. Don't worry about spacing in the statement printed at the console


Step 1: The acceptance test (the big loop)





we need to take this test, unit it fails for the right reason: the results of the console-print are not what was expected, so we write two modules:



and account.cljs:



and now the test:



Step 2: The first unit test - account module - the business layer (the small loop)


The very first module we should test is account, so we make account_tests. For account, we could test the deposit function. Considering we might have a module which will actually do the depositing/withdrawals in a database or something, that module will be separate and we'll call it transaction-repository. So we could test the interactions between modules like:



We expect that when we invoke deposit from the account module, the add-deposit from the transaction-repository will be invoked.



we make it pass,



and continue with withdraw and later with print-statement for which we'll do a separate module: statement-printer. The final test will be:



and:



and:




Step 3: the unit tests for the "data layer" (the small loop)

Like for the account, we start with a simple test, where we make a deposit and check if all-transactions will return it. We also need to make sure it's on a certain date. We make the test pass, and we move on to withdraw, ending up with:



and:



and:




Step 4: Unit testing the statement-printer (the small loop)


Now, we'll go straight to the results, even though I used TDD to get to it:

The tests:




The code obtained:



The tests result:



Let's not forget that our console-print function is still mocked, and the implementation is:



and we can now implement it:



Tests still pass.

Step 5: Going back to the acceptance test (the big loop)


We will modify the test, mocking the dates delivered, as well as console-print:




And when we run all the tests:




Conclusion


Starting from the outside, we could test-drive the design and the code.

acceptance-test -> 
      account-tests->account->
      transaction-respository-tests->transaction-repository->
      statement-printer-tests->statement-printer->console


Friday, February 06, 2015

Fighting complexity through functional composition, part 1: how to implement functional composition


The javascript code online:
http://jsfiddle.net/danbunea1/gz87dt5a/

Complexity


The absolute enemy in software (and other things as well) is complexity. Considering complexity as the opposite of simple, it makes our systems hard to understand, hard to debug and hard to extend or adapt. There is one very good talk of the great Rich Hickey, about this called: Simplicity Matters. The video: https://www.youtube.com/watch?v=rI8tNMsozo0. So let;s see how we can fight complexity in a practical example by using functional composition

The problem 



Considering that these days most of the integration is done through web services and JSON, we'll try to illustrate the complexity problem using an example from this area.

Requirement: in the json, we need to have a key “measurement”, that is mandatory, cannot be null, needs to be a string and cannot be empty string.

We’ll do a little bit of TDD here, starting with a test:

Python:
 class TestValidations(unittest.TestCase):  
   
   def validate_pair(self,json, is_valid, number_of_errors):  
     errors = validate_simplest_json(json)  
     print "",is_valid, errors, json  
     self.assertEquals(len(errors)==0,is_valid)  
     self.assertEquals(len(errors),number_of_errors)  
   
   def test_json_validation(self):  
     self.validate_pair({},False,1)  
   
   
 def validate_simplest_json(json):  
   errors = []  
   return errors  



All fail, which is great. Now let’s write the code, to check if the key is there:

 def validate_simplest_json(json):  
   errors = []  
   if not json.has_key("measurement"):  
     errors.append("measurement cannot be missing”)  
   return errors    

Pass. Now what about null? The test extends:

   def test_json_validation(self):  
     self.validate_pair({},False,1)  
     self.validate_pair({"measurement":None},False,1)  

the code to pass:

 def validate_simplest_json(json):  
   errors = []  
   if not json.has_key("measurement"):  
     errors.append("measurement cannot be missing")  
   else:  
     if json["measurement"]==None:  
       errors.append("measurement cannot be null”)  
   return errors  

Pass. Now let’s check if it is a string (or unicode):

   def test_json_validation(self):  
     self.validate_pair({},False,1)  
     self.validate_pair({"measurement":None},False,1)  
     self.validate_pair({"measurement":-1},False,1)  
     self.validate_pair({"measurement":{}},False,1)  
     self.validate_pair({"measurement":False},False,1)  
     self.validate_pair({"measurement":"abc"},True,0)  
     self.validate_pair({"measurement":u"Citroën"},True,0)  
   
 def validate_simplest_json(json):  
   errors = []  
   if not json.has_key("measurement"):  
     errors.append("measurement cannot be missing")  
   else:  
     if json["measurement"]==None:  
       errors.append("measurement cannot be null")  
     else:  
       if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):  
         errors.append("measurement needs to string or unicode")  
   return errors  

Now we also need to check if it is not emty string:

   def test_json_validation(self):  
     self.validate_pair({},False,1)  
     self.validate_pair({"measurement":None},False,1)  
     self.validate_pair({"measurement":-1},False,1)  
     self.validate_pair({"measurement":{}},False,1)  
     self.validate_pair({"measurement":False},False,1)  
     self.validate_pair({"measurement":"abc"},True,0)  
     self.validate_pair({"measurement":u"Citroën"},True,0)  
     self.validate_pair({"measurement":""},False,1)  
   
 def validate_simplest_json(json):  
   errors = []  
   if not json.has_key("measurement"):  
     errors.append("measurement cannot be missing")  
   else:  
     if json["measurement"]==None:  
       errors.append("measurement cannot be null")  
     else:  
       if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):  
         errors.append("measurement needs to string or unicode")  
       else:  
         if len(json["measurement"].strip())==0:  
           errors.append("measurement cannot be an empty string")  
   return errors  

All of the sudden, we hear we also need to make sure the length of the string is between 3 and 8 characters, and cannot be some reserved words like “password” or “archived"

Ok, so let’s code, expanding our tests:

   def test_json_validation(self):  
     self.validate_pair({},False,1)  
     self.validate_pair({"measurement":None},False,1)  
     self.validate_pair({"measurement":-1},False,1)  
     self.validate_pair({"measurement":{}},False,1)  
     self.validate_pair({"measurement":False},False,1)  
     self.validate_pair({"measurement":"abc"},True,0)  
     self.validate_pair({"measurement":u"Citroën"},True,0)  
     self.validate_pair({"measurement":""},False,1)  
     self.validate_pair({"measurement":"a"},False,1)  
     self.validate_pair({"measurement":"abcdefghijklmnefghij"},False,1)  
     self.validate_pair({"measurement":"password"},False,1)  
     self.validate_pair({"measurement":"archived"},False,1)  
     self.validate_pair({"measurement":"arCHived"},False,1)  

Then gradually we start coding the validation, arriving to:

 def validate_simplest_json(json):  
   errors = []  
   if not json.has_key("measurement"):  
     errors.append("measurement cannot be missing")  
   else:  
     if json["measurement"]==None:  
       errors.append("measurement cannot be null")  
     else:  
       if not isinstance(json["measurement"], str) and not isinstance(json["measurement"], unicode):  
         errors.append("measurement needs to string or unicode")  
       else:  
         lenm=len(json["measurement"].strip())  
         if lenm==0:  
           errors.append("measurement cannot be an empty string")  
         else:  
           if lenm<3: data-blogger-escaped-div="">  
             errors.append("measurement needs at least 3 characters")  
           elif lenm&gt;10:  
             errors.append("measurement needs at most 10 characters")  
           elif json["measurement"].strip().lower() in ["archived","password"]:  
             errors.append("measurement has a value which is not allowed")  
   return errors  

As requirements are added complexity grows. Now of course this code could be refactored, but eliminating the essential problem of complexity is very hard. Just imagine what will happen if at version 1.2 the customer will change the API and only allow the values to be a measurement unit like “0.12mm” or “13.2mg”. It will grow again and become more complex. Not pretty!

And having json with only one key is kind of rare… Usually the number of keys is a lot higher and of course the code a lot bigger. Bigger and more complex = disaster. In terms of code quality it will fail at being able to extend it easily and it will fail at being able to debug it easily.


The solution: implementing functional composition



Removing complexity can mean, more linear code, so let’s refactor it to be more linear:

 def validate_simplest_json_imperative_linear(json):  
   errors = []  
   should_exit=False  
   key = "measurement"  
   if not key_exists(json,key):  
    errors.append("{0} cannot be missing".format(key))  
    should_exit=True  
   
   if not should_exit:  
     if value_null(json, key):  
       errors.append("{0} cannot be null".format(key))  
       should_exit=True  
   
   if not should_exit:  
     if not is_string_or_unicode(json, key):  
       errors.append("{0} needs to string or unicode".format(key))  
       should_exit=True  
   
   if not should_exit:  
     if is_empty_string(json, key):  
       errors.append("{0} cannot be an empty string".format(key))  
       should_exit=True  
   
   
   if not should_exit:  
     lenm=len(json[key].strip())  
     if lenm<3: data-blogger-escaped-div="">  
       errors.append("{0} needs at least 3 characters".format(key))  
       should_exit=True  
     elif lenm&gt;10:  
       errors.append("{0} needs at most 10 characters".format(key))  
       should_exit=True  
   
   if not should_exit:  
     if json[key].strip().lower() in ["archived","password"]:  
       errors.append("{0} has a value which is not allowed".format(key))  
       should_exit=True  
   
   return errors  

And yes, all the tests still pass. But we’re far from over, although we do see a pattern by which each method is executed after the other… Hmm, now I’ll move all variables like json, key, errors and exit into a single object (a tuple) so that we don’t pass 4 parameters back and forth:

 ValidationState = namedtuple("ValidationState","json key errors exit”)  

then I will extract the actual validations in simple functions, like:

 def validate_simplest_json_imperative_linear_with_state(json):  
   initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)  
   
   state = validate_key_exists(initial_state)  
   
   if not state.exit:  
     state = validate_not_null(state)  
   
   if not state.exit:  
     state = validate_string_or_unicode(state)  
   
   if not state.exit:  
     state = validate_not_empty_string(state)  
   
   if not state.exit:  
     state = validate_length(state, 3,10)  
   
   if not state.exit:  
     state = validate_not_in(state, ["archived","password"])  
   
   return state.errors  

And the functions:

 def validate_key_exists(state):  
   print validate_key_exists.__name__,state  
   if not key_exists(state.json,state.key):  
     return state._replace(errors = state.errors+["{0} cannot be missing".format(state.key)])._replace(exit=True)  
   return state  
   
 def validate_not_null(state):  
   print validate_not_null.__name__,state  
   if value_null(state.json,state.key):  
     return state._replace(errors = state.errors+["{0} cannot be null".format(state.key)])._replace(exit=True)  
   return state  
   
 def validate_string_or_unicode(state):  
   print validate_string_or_unicode.__name__,state  
   if not is_string_or_unicode(state.json,state.key):  
     return state._replace(errors = state.errors+["{0} needs to string or unicode".format(state.key)])._replace(exit=True)  
   return state  
   
 def validate_not_empty_string(state):  
   print validate_not_empty_string.__name__,state  
   if is_empty_string(state.json,state.key):  
     return state._replace(errors = state.errors+["{0} cannot be an empty string".format(state.key)])._replace(exit=True)  
   return state  
   
 def validate_length(state, min, max):  
   print validate_length.__name__,state  
   lenm=len(state.json[state.key].strip())  
   if lenm  
     return state._replace(errors = state.errors+["{0} needs at least 3 characters".format(state.key)])._replace(exit=True)  
   elif lenm&gt;max:  
     return state._replace(errors = state.errors+["{0} needs at most 10 characters".format(state.key)])._replace(exit=True)  
   return state  
   
 def validate_not_in(state,vals):  
   print validate_not_in.__name__,state  
   if state.json[state.key].strip().lower() in vals:  
     return state._replace(errors = state.errors+["{0} has a value which is not allowed".format(state.key)])._replace(exit=True)  
   return state  
   

The code looks is now a series of functions that run with the result of the previous function if the exit parameter is not set to True. So basically having 2 functions f,g they’ll be composed like:

initial_state = …
state = f(initial_state)
if not state.exit:
    return g(state)

And putting this in a function:

 def compose2(f, g):  
   def run(x):  
     result_f = f(x)  
     if not result_f.exit:  
       return g(result_f)  
     else:  
       return result_f  
   return run  

Using this we can now compose 2 functions into one:

 def validate_simplest_json_imperative_linear_with_state(json):  
   initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)  
   
   # state = validate_key_exists(initial_state)  
   #  
   # if not state.exit:  
   #   state = validate_not_null(state)  
   
   composed_function = compose2(validate_key_exists, validate_not_null)  
   state = composed_function(initial_state)  


But we don’t have only 2 function, we have more, so we write a reduce:

 #compose n functions  
 def compose(*functions):  
   return reduce(compose2, functions)  

and out function becomes:

 def validate_simplest_json_imperative_linear_with_state(json):  
   initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)  
   
   composed_function = compose(validate_key_exists, validate_not_null,validate_string_or_unicode,validate_not_empty_string)  
   state = composed_function(initial_state)  
   
   if not state.exit:  
     state = validate_length(state, 3,10)  
   
   if not state.exit:  
     state = validate_not_in(state, ["archived","password"])  
   
   return state.errors  

but we just hit a problem. He have some functions that have more parameters and we need to pass them. We’ll use closures:

 def create_validate_length(min, max):  
   def validate_length(state):  
     print validate_length.__name__,state  
     lenm=len(state.json[state.key].strip())  
     if lenm  
       return state._replace(errors = state.errors+["{0} needs at least 3 characters".format(state.key)])._replace(exit=True)  
     elif lenm&gt;max:  
       return state._replace(errors = state.errors+["{0} needs at most 10 characters".format(state.key)])._replace(exit=True)  
     return state  
   return validate_length  
   
 def create_validate_not_in(vals):  
   def validate_not_in(state):  
     print validate_not_in.__name__,state  
     if state.json[state.key].strip().lower() in vals:  
       return state._replace(errors = state.errors+["{0} has a value which is not allowed".format(state.key)])._replace(exit=True)  
     return state  
   return validate_not_in  

And now the final validation code:

 def validate_simplest_functional_composition(json):  
   initial_state = ValidationState(json=json, key="measurement",errors=[], exit=False)  
   
   composed_function = compose(validate_key_exists, validate_not_null,validate_string_or_unicode,validate_not_empty_string, create_validate_length(3, 10), create_validate_not_in(["archived","password"]))  
   final_state = composed_function(initial_state)  
   
   return final_state.errors  

It is much better. It basically says: having an initial start of the system, run all these functions (validators) and at the end get a final state.


a preview:


Or in Javascript: http://jsfiddle.net/danbunea1/gz87dt5a/





Conclusion: Why is this better?



Now you would think, how can a solution with ... lines of code be better then one with just 21. In part 2 of the article, called "Why is functional composition better" I will illustrate why, and how functional composition makes our code simpler, easier to understand, debug and change.