I just wish more people appreciated those things at all. What's the name of the rule where everyone with a sufficiently large codebase has a partially implemented, buggy time oriented data log somewhere and it's too bad people didn't realize that sooner. Datomic et all are so cool and solve so many interesting and pervasive problems but people would rather just mangle those bits themselves. The glory of refactoring a large, purely immutable piece of software is so glorious and easy. The wrapper/adapter hell that comes out of the Expression Problem is just one more thing that people just assume is unavoidable and the amount of gross code that falls out of that is so painful.
For me, it was PageObjects all the way down, which "just composed".
Something like this:
(defprotocol IPageObject
"Each PageObject MUST implement the IPageObject protocol."
(page-object [this])
(exists? [this])
(visible? [this]))
And then an implementation like this:
(defrecord Checkbox [target-css]
IPageObject
(page-object [this]
this)
(exists? [this]
;; webdriver check if target-css exists
)
(visible? [this]
;; webdriver check if target-css is visible
)
Selectable
(select [this]
;; webdriver select the target
)
(deselect [this]
;; webdriver undo selection
)
(selected? [this]
;; webdriver return true if target selected
)
Value
(get-value [this]
;; webdriver return current selection state
(selected? this)))
Thanks to Records (stateless, type-identified, hash-map semantics), the PageObject model composes arbitrarily. i.e. A Page is itself a PageObject containing a tree of arbitrarily nested PageObjects... i.e. PageObjects all the way down.
This example just scratches the surface.
I don't think it's showing any Clojure-magic per se,
b/c in effect what you're showing is just "implementing an interface".
You can do that in most languages.
The magic of protocol/records is that they work across library boundaries.
A library may provide a record
- and then you can extend the record with new protocols.
Key is that it's all without needing to explicitly creating new agglomeration types.
You can take some Dog record from some pet-simulation library and then `extend-type` it with the IPageObject protocol and make the Dog record now something that can be displayed on a webpage
The magic of Clojure is that if a problem can be solved with an interface Clojure lets you solve it with an interface. It doesn't have any magic to show off, it is just a relentless implementation of a lot of basic good ideas.
*But beware. Once you see, you cannot un-see the fact that…*
Any sufficiently complicated data system contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of a bitemporal database.
— Henderson's Tenth Law. (= henderson https://github.com/jarohen)
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
> -- Philip Greenspun,
Circa 2013-15, I was a QA having to write some Selenium / WebDriver test suites for the web application side of our product.
At the time, when our team was tiny, Clojure was one of our "company languages" (besides JavaScript/YUI for web, and iOS/Objective-C, Android/Java for our mobile SDKs).
Our extant web testing framework was written in Clojure, so I continued refactoring that to support our people through "hypergrowth" feature churn including a web tech migration from YUI to React (took about four years for YUI to completely factor out, IIRC).
I did not fully grok just how much Clojure's multiple dispatch facility helped me then. I knew that feature was useful, of course, but the magnitude became clearer as the years progressed.
It allowed a single person---yours truly---to address the real needs of a ridiculously complex moving target (because B2B web products be like that --- full of special cases and customisation options and spooky effects at a distance).
I would not want to try supporting a test suite in that sort of situation, without at least these Clojurish programming facilities, that I have come to expect as a given these days.
Incidentally, I implemented the refactored test suite API using the "PageObject" abstraction. An apt OO abstraction is worth its weight in gold, provided the implementation composes naturally.
I miss the days when productivity booster posts were all about REPL, functional programming, composability, immutability.
I just wish more people appreciated those things at all. What's the name of the rule where everyone with a sufficiently large codebase has a partially implemented, buggy time oriented data log somewhere and it's too bad people didn't realize that sooner. Datomic et all are so cool and solve so many interesting and pervasive problems but people would rather just mangle those bits themselves. The glory of refactoring a large, purely immutable piece of software is so glorious and easy. The wrapper/adapter hell that comes out of the Expression Problem is just one more thing that people just assume is unavoidable and the amount of gross code that falls out of that is so painful.
> The wrapper/adapter hell that comes out of the Expression Problem
Yeah, I got lucky in my work (see sibling comment https://news.ycombinator.com/item?id=45207880).
For me, it was PageObjects all the way down, which "just composed".
Something like this:
And then an implementation like this: Thanks to Records (stateless, type-identified, hash-map semantics), the PageObject model composes arbitrarily. i.e. A Page is itself a PageObject containing a tree of arbitrarily nested PageObjects... i.e. PageObjects all the way down.Solid gold!
(edit: x-link to sibling comment)
This example just scratches the surface. I don't think it's showing any Clojure-magic per se, b/c in effect what you're showing is just "implementing an interface". You can do that in most languages.
The magic of protocol/records is that they work across library boundaries. A library may provide a record - and then you can extend the record with new protocols. Key is that it's all without needing to explicitly creating new agglomeration types.
You can take some Dog record from some pet-simulation library and then `extend-type` it with the IPageObject protocol and make the Dog record now something that can be displayed on a webpage
The magic of Clojure is that if a problem can be solved with an interface Clojure lets you solve it with an interface. It doesn't have any magic to show off, it is just a relentless implementation of a lot of basic good ideas.
Sounds like type-classes from haskell/scala? Or is that a different thing?
"Henderson's Tenth Law" :D
I blogged about it here: https://www.evalapply.org/posts/poor-mans-time-oriented-data... (discussed here recently: https://news.ycombinator.com/item?id=44583790 ).(edit: add links)
Greenspun's tenth rule:
> Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp. > -- Philip Greenspun,
So. Much. This!
Circa 2013-15, I was a QA having to write some Selenium / WebDriver test suites for the web application side of our product.
At the time, when our team was tiny, Clojure was one of our "company languages" (besides JavaScript/YUI for web, and iOS/Objective-C, Android/Java for our mobile SDKs).
Our extant web testing framework was written in Clojure, so I continued refactoring that to support our people through "hypergrowth" feature churn including a web tech migration from YUI to React (took about four years for YUI to completely factor out, IIRC).
I did not fully grok just how much Clojure's multiple dispatch facility helped me then. I knew that feature was useful, of course, but the magnitude became clearer as the years progressed.
It allowed a single person---yours truly---to address the real needs of a ridiculously complex moving target (because B2B web products be like that --- full of special cases and customisation options and spooky effects at a distance).
I would not want to try supporting a test suite in that sort of situation, without at least these Clojurish programming facilities, that I have come to expect as a given these days.
Talk: https://www.youtube.com/watch?v=hwoLON80ZzA&list=PLG4-zNACPC...
PDF Deck (contains reading references): designing_object_functional_system_IN-Clojure_2016.pdf, here https://github.com/adityaathalye/slideware/
Incidentally, I implemented the refactored test suite API using the "PageObject" abstraction. An apt OO abstraction is worth its weight in gold, provided the implementation composes naturally.
Thanks, Martin Fowler! This very post helped me back then: https://martinfowler.com/bliki/PageObject.html
(edit: formatting, small bit of context)