User Tools

Site Tools


blog:enterprise_clojure_is_not_a_bad_phrase

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
blog:enterprise_clojure_is_not_a_bad_phrase [2017/05/24 22:17]
djo [Is Clojure too hard to use at enterprise scale?]
blog:enterprise_clojure_is_not_a_bad_phrase [2017/05/25 16:16]
djo removed
Line 8: Line 8:
  
 Naturally, feedback is welcome. Naturally, feedback is welcome.
- 
-===== The data expected in a function'​s parameters quickly becomes non-obvious ===== 
  
 Awhile back I needed to parse a string into words--except that single or double quoted substrings must function as a single word.  Nested quotes are not supported.  ​ Awhile back I needed to parse a string into words--except that single or double quoted substrings must function as a single word.  Nested quotes are not supported.  ​
Line 19: Line 17:
 </​code>​ </​code>​
  
-Here is the Clojure code I initially wrote to parse this way:+Here is the Clojure code I initially wrote to parse this.  When I recently revisited this code, I found it more challenging than I expected to deeply understand again. ​ (I'll get into why in a bit.)
  
 <code clojure> <code clojure>
Line 59: Line 57:
 At the time I wrote the code, it made perfect sense to me.  But I had a need to revisit it recently in order to write/​update tests and needed to understand it again. At the time I wrote the code, it made perfect sense to me.  But I had a need to revisit it recently in order to write/​update tests and needed to understand it again.
  
-And I found myself staring at the parameter list and code of the <​code>​merge-strings</​code> ​function, trying to understand what values each parameter could take.  I was surprised at how non-obvious it was to me a few short months later.+And I found myself staring at the parameter list and code of the ''​%%merge-strings%%'' ​function, trying to understand what values each parameter could take.  I was surprised at how non-obvious it was to me a few short months later.
  
 To my thinking, this illustrated a common pain point my colleagues have expressed about Clojure, namely... To my thinking, this illustrated a common pain point my colleagues have expressed about Clojure, namely...
  
-===== Functions accepting (possibly-nested) structures are not self-documenting ​=====+===== As complexity increases, the data expected in a function'​s parameters can quickly become non-obvious ​=====
  
-Even though I had written a docstring for the function, notice that because this function is a reducer, and is not API, I had not rigorously described each parameter'​s possible values and usage within the function.+Even though I had written a docstring for the ''​%%merge-strings%%'' ​function, notice that because this function is a reducer, is used as internal implementation detail, and is not API, I had not rigorously described each parameter'​s possible values and usage within the function.
  
 This week, I decided to use the upcoming [[https://​clojure.org/​guides/​spec|Specs]] library from Clojure 1.9 to document each parameter'​s possible values and see if this helped with the readability and maintainability of this particular example. This week, I decided to use the upcoming [[https://​clojure.org/​guides/​spec|Specs]] library from Clojure 1.9 to document each parameter'​s possible values and see if this helped with the readability and maintainability of this particular example.
Line 85: Line 83:
 </​code>​ </​code>​
  
-After trying this in a few places, I became dissatisfied with the repetitiveness of manually calling ​<​code>​s/valid?</​code> ​for each (destructured) parameter value, so I wrote a macro to DRY this pattern up.  (The code is in the <​code>​clj-foundation</​code> ​project) ​ With the macro, the above defn can be rewritten in either of the following two ways:+After trying this in a few places, I became dissatisfied with the repetitiveness of manually calling ​''​%%s/valid?%%'' ​for each (destructured) parameter value, so I wrote a macro to DRY this pattern up.  (The code is in the ''​%%clj-foundation%%'' ​project.)  With the macro, the above defn can be rewritten in either of the following two ways:
  
 <code clojure> <code clojure>
 (=> person-name [::person] string? (=> person-name [::person] string?
-  "​person->​String"​ 
   [person]   [person]
   (str (::​first-name person) " " (::​last-name person)))   (str (::​first-name person) " " (::​last-name person)))
Line 96: Line 93:
  
 (defn person-name (defn person-name
-  "​person->​String"​ 
   [person]   [person]
   (str (::​first-name person) " " (::​last-name person)))   (str (::​first-name person) " " (::​last-name person)))
Line 103: Line 99:
 </​code>​ </​code>​
  
-To my eyes, this significantly enhanced the readability of the spec information added to the <​code>​person-name</​code> ​function, so I applied the macro to my string parsing functions. ​ That code now reads as follows:+To my eyes, this significantly enhanced the readability of the spec information added to the ''​%%person-name%%'' ​function, so I applied the macro to my string parsing functions. ​ That code now reads as follows:
  
 <code clojure> <code clojure>
Line 147: Line 143:
 </​code>​ </​code>​
  
-With this code, it becomes easy to see that the <​code>​result</​code> ​(destructured) parameter contains a word-vector,​ which must be a collection of strings. ​ Similarly, without reading the function body, one can immediately note that the <​code>​delimiter</​code> ​parameter is a character from the delimiter-set or nil.+With this code, it becomes easy to see that the ''​%%result%%'' ​(destructured) parameter contains a word-vector,​ which must be a collection of strings. ​ Similarly, without reading the function body, one can immediately note that the ''​%%delimiter%%'' ​parameter is a character from the delimiter-set or nil.   
 + 
 +And skipping to the end (of the line), one can immediately see that the entire function returns a ''​%%merge-result%%'',​ which is defined as a Tuple containing a word-vector,​ delimiter (or nil), and String--all without reading the function body.
  
-And this bit of up-front information made (re)reading the body of the <​code>​merge-strings</​code> ​function much easier.+I found that this bit of up-front information made (re)reading the body of the ''​%%merge-strings%%'' ​function much easier, even after only a few days away from it.
  
 ===== Retrospective ===== ===== Retrospective =====
Line 155: Line 153:
 With this in mind, I would like to offer the following thoughts about this experiment: With this in mind, I would like to offer the following thoughts about this experiment:
  
-* I felt the experiment was successful. ​ I believe the code I wound up with explains the original author'​s intentions better than the original code. +  ​* I felt the experiment was successful. ​ I believe the code I wound up with explains the original author'​s intentions better than the original code. 
-* Only time will validate the <​code>​=></​code> ​macro, and I'm sure it will evolve over time.  But I sincerely hope something like it makes it into Specs in the end. +  * Only time will validate the ''​%%=>%%'' ​macro, and I'm sure it will evolve over time.  But I sincerely hope something like it makes it into Specs in the end. 
-* More generally, I feel that this code illustrates how even quite straightforward functions can become opaque very quickly, and how providing explicit specifications describing what data a function accepts ​and provides can significantly enhance communication.+  * More generally, I feel that this code illustrates how even quite straightforward functions can become opaque very quickly, and how providing explicit specifications describing what data a function accepts/provides can significantly enhance communication.
  
 ==== ...and a word from our sponsors ==== ==== ...and a word from our sponsors ====
  
-In closing, I'm available for new Clojure ​gigs right now.  If this kind of thinking and expertise is welcome on your Clojure ​team or on your Clojure ​project, feel free to email me using the address on the "​Contacts"​ page off my home page.+In closing, I'm available for new gigs right now.  If this kind of thinking and expertise is welcome on your team or on your project ​(whatever the language), feel free to email me using the address on the "​Contacts"​ page off my home page.