Reagent deep dive part 3: Sequences



(ns my.reagent-examples
(:require
[clojure.string :as string]
[reagent.core :as reagent]
[reagent.ratom]))

(enable-console-print!)
Welcome to the third leg of the Reagent deep dive tour.

In part 2 we got into the thick of the component lifecycle. Today we move on to nuances surrounding the representation of sequences. This will be quite a change of pace as it is a somewhat less tangible topic. We'll need to use our imaginations a little.




Sequences

Seven. Apparently this is the minimum number of times a person has to try to do something before said person actually succeeds in doing it. -- Rusty Bentley


In part 1 it was noted that [[:div] [:div]] is not a valid Reagent component. However [:div [[:div] [:div]]] is a valid component, and has a special meaning. Reagent forms do allow a sequence as a child of a tag. A sequence is interpreted as a React array, which is interpreted to mean a collection of elements which may get swapped around.

So we can write a component as:

Example U: A sequence of circles


[:svg {:width 200 :height 200}
(for [i (range 30)]
[:circle
{:r (* (inc i) 5)
:cx 100
:cy 100
:fill "none"
:stroke (str "rgb(0, " (* i 10) "," (* (- 30 i) 10) ")")}])]

Instead of using into like we have been thus far:
(into [:svg] (for ...))

Note that the former results in something like:
[:svg [[:circle] [:circle] ...]]

While the later results in something like:
[:svg [:circle] [:circle] ...]

What difference does it make? Not much that we can notice at first glance. We'll only notice a difference if we open the developer console. Example U causes React to print warnings in the developer console:
Warning: Every element in a seq should have a unique :key

What the heck is React complaining about?

Well, imagine for a moment that you had written the following list:
  • Princess torte.
  • Black forest gateau.
  • Apple pie.



And then you realized that ice cream should be in the list, at the very top. The logical thing to do would be to insert ice cream above everything else. But another way to get the final list would be to replace princess torte with ice cream, then replace black forest gateau with princess torte, replace apple pie with black forest gateau, and finally add apple pie at the very end. The reason that we chose the first, more logical and faster method was because we knew that we wanted to insert a new element.

Imagine for a moment that you emailed me the initial list and I put it into a spreadsheet. Then you sent me the final list. I could probably figure out to insert ice cream at the top is the most efficient change. So I make the update. But what if you emailed me a list of about 100 things, and then later emailed me the same list but with 20 items inserted, moved, deleted, or changed? It would be really hard for me to figure out the minimal updates required to update my spreadsheet. So instead I would copy the entire list over the top of my spreadsheet.

There is however another solution! When you sent me the list you could have assigned each logical item an id number. That way when I got the second list, I could quickly identify the minimal updates required. I could walk through the new list, looking only at the id number. If the number was in the existing list and at the same position I could check that the item contents haven't changed and then move on. If the existing list had the id number at a different position I could move it to the correct position. If the existing list didn't have the id number, I would know to insert a new row. This sounds a little tedious, but I hope it is clear that there is a mechanical process available that which would ultimately result in less change to the spreadsheet.

This minimal update scenario applies to HTML elements. React's job is to figure out the minimal changes to make to a HTML page in order to transition from the existing view state to the desired view state. If it can swap HTML elements around, that is far fewer updates than recreating them in different positions. You can give React the hint it needs by passing a unique key per element. To specify a key on an element in a sequence, use the mysterious ^{:key k}

Tip: The item key is represented in metadata attached to the item. ^ is shorthand to set the metadata of an object. You can also pass :key as an attribute of a hiccup form.

Here is how we represent a sequence of keyed entities:

Example V: A sequence of sub-components, with identity keys


(def favorites
(reagent/atom
{"d632" {:name "Princess torte."
:order 2}
"1ae2" {:name "Black forest gateau."
:order 3}
"5117" {:name "Apple pie."
:order 4}
"42ae" {:name "Ice cream."
:order 1}}))

(defn list-by [entities sort-k]
[:ul
(for [[k v] (sort-by (comp sort-k val) @entities)]
^{:key k}
[:li (:name v)])])

(defn favorites-by-order-and-name []
[:div
[:h3 "By order"]
[list-by favorites :order]
[:h3 "By name"]
[list-by favorites :name]])

The idea in this example is that each item has a unique ID, perhaps assigned by a database, generated when the item was created. That unique ID is used as the key in the sequence.

This example is clearly contrived! These components aren't big enough or numerous enough for us to waste our precious brainpower worrying about their rendering performance. For small lists of small components, you wont notice any performance difference between the three options for representing them; as direct children of a parent element, as a keyless sequence of elements, or as a keyed sequence of elements. However this tiny example does allow us to discuss exactly what those 3 alternatives look like, and why in the broader picture we should care.

  1. We could have used into to make all the list items direct children of the unordered list. This would have resulted in no warnings and not required the key. Updating the list with new data would result in some unnecessary DOM updates.
  2. We could have left off the ^{:key k} and ignored the warnings in the developer console. Updating the list with new data would result in some unnecessary DOM updates.
  3. As presented there was a natural key available, so we annotated the each list item with metadata. There are no warnings in the console and updating the list will result in fewer DOM updates.

The pattern of "I have a bunch of entities I need to render" pops up here and there in practice. That is why the distinction between expressing sequences exists. By way of illustration I shall describe one such scenario. In my Napkindo app I display a gallery of drawings. Each drawing is an entity that has a database assigned id, and various information attached to it such as the drawing title, the line paths in the drawing, and the owner. For very large collections of large elements, assigning React keys improves UI performance. And it turns out that many of the large collections of large elements we run into fit this pattern nicely.

There is another scenario where entity identity should be preserved; animating transitions of elements. Generally we don't care which elements in our DOM contain what HTML because it all looks the same once the updates are applied. But when rendering transitions, it becomes obvious which elements are linked to which logical entities. Visual identity must follow logical identity. I'll let you ponder that.




In this highly technical diagram each box is logically bound to its contents. Keys provide the mechanism to make this binding.

Coming back to our options... now that we have pondered at length the 3 ways of expressing sequences we can happily choose whichever we prefer, realizing that it wont make any difference in most circumstances. My personal opinion is that warnings are best heeded, so I avoid keyless sequences. My rule of thumb is to key my sequences if there is a natural key available. If there is no natural key, I default to using into the parent tag instead. I think it best to avoid the temptation to make up a sequential key by assigning each item in the sequence an index number. Doing so complicates the code to avoid a warning by deceiving React about the identity semantics of the sequence. The semantic of a key is that it identifies a unique entity in the sequence. In short, if there is a natural key, use it. If there is no natural key, put the items into the parent tag as children.

There is one final consideration when using sequences; lazy deref is not allowed.

Example W: A button that does not work due to lazy deref


(def message (reagent/atom "Everything is fine"))

(defn bad []
[:div
[:button
{:on-click
(fn [e]
(reset! message "Oh no, oh dear, oh my."))}
"Panic!"]
(for [i (range 3)]
[:h3 @message])])
This example does not work! It will produce a warning:
Warning: Reactive deref not supported in lazy seq, it should be wrapped in doall
From Reagent's perspective calling bad does not deref message. Rendering [:h3 @message] occurs later but at that point Reagent no longer knows that the parent component is bad. Because Reagent doesn't evaluate the lazy sequence immediately it is unaware that badshould respond to changes in message.



We can force evaluation of a lazy sequence that derefs by wrapping it in a doall, or by using using vec or into to realize the sequence, and then it will work just fine.

Exercise: Pressing the panic button does nothing. Fix example V by forcing evaluation of the lazy sequence, and press the panic button.

Fortunately this somewhat confusing circumstance of deref inside a lazy sequence occurs rarely, and produces a warning with the advice on how to remedy it. Notice that we don't need to force a lazy sequence that consumes a deref, which is far more common. For example (for [x @xs] ...) does not need to be forced because the deref is not inside the lazy sequence.

An intuition for sequence performance

The proof is in the pudding. -- Unknown


I made some pretty bold claims about the impact of keys and haven't provided a guide for exactly when performance starts to be impacted aside from some vague notion of "large". It is not a simple thing to quantify and will of course be situational, but we can gain a bit of intuition here of what a "large" sequence is. Let's play out our earlier thought experiment about a list of tasty desserts getting updated.

Example X: A large keyed sequence of delectable disposition


(def words
["ice" "cream" "chocolate" "pastry" "pudding" "raspberry" "mousse"
"vanilla" "wafer" "waffle" "cake" "torte" "gateau" "pie" "cookie"
"cupcake" "mini" "hot" "caramel" "meringue" "lemon" "marzipan" "mocha"
"strawberry" "tart" "custard" "fruit" "baklava" "jelly" "banana" "coconut"])

(defn rand-name []
(string/capitalize (string/join " " (take (+ 2 (rand-int 5)) (shuffle words)))))

(def desserts (reagent/atom ()))

(defn make-a-dessert [e]
(swap! desserts conj {:id (random-uuid)
:name (rand-name)}))

(defn make-many-desserts [e]
(dotimes [i 100]
(make-a-dessert nil)))

(defn color-for [x]
(str "#" (.toString (bit-and (hash x) 0xFFFFFF) 16)))

(defn dessert-item [{:keys [id name]}]
[:li
[:svg {:width 50 :height 50}
[:circle
{:r 20 :cx 25 :cy 25 :fill (color-for id)}]
[:rect {:x 15 :y 15 :width 20 :height 20 :fill (color-for name)}]]
[:span [:em [:strong name]]]])

(defn desserts-list []
[:ol
(for [dessert @desserts]
^{:key (:id dessert)}
[dessert-item dessert])])

(defn dessertinator []
[:div
[:button {:on-click make-a-dessert} "Invent a new dessert"]
[:button {:on-click make-many-desserts} "Invent 100 new desserts"]
[desserts-list]])

Exercise: desserts-list currently keys each dessert-item. Invent 2000 desserts by pressing the "100" button 20 times. Then add another single dessert. Creating desserts should be fairly fast. Next delete the ^{:key (:id dessert)} line in desserts-listand perform the same steps. At about 2000 desserts, it takes noticeably longer to create new desserts!

Tip: Every time you change the code, the desserts list is reset, so you might want to make another change to the code so that you can finish the article when you are done experimenting.

As you can see, computers are amazing and it really does take a very large sequence before performance is impacted by the lack of a key. With the keyed approach we can preserve performance with many, many items in our sequence.


Conclusion

A gene is a long sequence of coded letters, like computer information. Modern biology is becoming very much a branch of information technology. -- Richard Dawkins




Phew! We made it to our third stop off.

We observed 3 different ways to express a sequence of elements and discussed how React treats them. In the 4th leg of the tour we shall not be encountering any new concepts. Instead we will be applying some of the concepts we have already encountered to build out a mini sketching application.

I hope to see you again soon for part 4, where we'll handle some more practical UI challenges.

Permalink

Reagent deep dive part 2: The lifecycle of a component



(ns my.reagent-examples
(:require
[reagent.core :as reagent]
[reagent.ratom]))

(enable-console-print!)

Welcome to the second leg of the Reagent deep dive tour!

In part 1 we saw how to create components and react to change. Now we move on to some more challenging aspects of UI building. Today we examine how to represent component local state and how to access the rendered DOM nodes. We will need these capabilities in order to create some more interesting components and make use of third party JavaScript libraries so that we can render a 3D scene.




Component forms

One of the basic things about a string is that it can vibrate in many different shapes or forms, which gives music its beauty. -- Edward Witten


We have a mechanism for change, which we examined in part 1, and it works great for everything we can define in Reagent that has access to an external reagent/atom. But not everything falls into this neat view of the world. There are two exceptions:
  1. What if we want a self-contained component with it's own state? We want to be able to define and retain a local reagent/atom instead of relying on one from the surrounding environment in order to build reusable components.
  2. What if we want a component to call JavaScript functions on to the actual HTML elements after they are rendered? There are many great JavaScript libraries that operate directly on HTML elements, and in order to use them we need to be able to invoke them after the component has created the elements.
The Reagent answer comes in two additional forms of specifying what a component is. So far we have been using the first form, a function that returns hiccup. In total there are 3 important forms for specifying components:
  1. A function that returns a hiccup vector.
  2. A function that returns a function.
  3. A function that returns a Class.


Moodswingerscale


We saw plenty of examples of form 1 in part 1 of the deep dive tour. All the examples were functions that returned hiccup. So let's examine form 2 more closely now.


Example K: Reagent component form 2 - A function that returns a function


(defn greetings []
(fn []
[:h3 "Hello world"]))

Here is a function that returns a function. The returned function (the inner function) returns a hiccup vector representing HTML. The outer function just returns the inner function.

Exercise: Is a function that returns a function that returns a function a valid Reagent component? Find out by modifying the examples above. Wrap the inner function in yet another function.

Form 2 is useful for performing initial setup for a component. A common usage of this form is to establish some local state. Consider this example which creates a reagent/atom counter per instance:

Example L: Reagent component form 2 - A function that returns a function


(defn a-better-mouse-trap [mouse]
(let [mice (reagent/atom 1)]
(fn render-mouse-trap [mouse]
(into
[:div
[:button
{:on-click
(fn [e]
(swap! mice (fn [m] (inc (mod m 4)))))}
"Catch!"]]
(repeat @mice mouse)))))

[:div
[a-better-mouse-trap
[:img
{:src "https://www.domyownpestcontrol.com/images/content/mouse.jpg"
:style {:width "150px" :border "1px solid"}}]]
[a-better-mouse-trap
[:img
{:src "https://avatars1.githubusercontent.com/u/9254615?v=3&s=150"
:style {:border "1px solid"}}]]]

These mice traps each have their own count of mice per trap. Compare this example to the previous counter in part 1 example H, which relied on a single global count. Global state, and state passed as arguments tend to be useful for application features. Local state tends to be useful for self contained components.

Notice that this example is really just a closure (variable capture) occurring inside a function.

Seeing that this is a common pattern, Reagent also provides with-let which will take care of the inner function for you:

Example M: Using with-let to avoid returning a function


(defn lambda [rotation x y]
[:g {:transform (str "translate(" x "," y ")"
"rotate(" rotation ") ")}
[:circle {:r 50, :fill "green"}]
[:circle {:r 25, :fill "blue"}]
[:path {:stroke-width 12
:stroke "white"
:fill "none"
:d "M -45,-35 C 25,-35 -25,35 45,35 M 0,0 -45,45"}]])

(defn spinnable []
(reagent/with-let [rotation (reagent/atom 0)]
[:svg
{:width 150 :height 150
:on-mouse-move
(fn [e]
(swap! rotation + 30))}
[lambda @rotation 75 75]]))

(defn several-spinnables []
[:div
[:h3 "Move your mouse over me"]
[a-better-mouse-trap [spinnable]]])


This is a slightly more compact way of expressing the same concept. The rotation atom is created only once, while the component will be re-rendered when the rotation value is modified.


O.K. so what about form 3? Let's look at how to create a Class:

Example N: Reagent component form 3 - A function that returns a Class


(defn announcement []
(reagent/create-class
{:reagent-render
(fn []
[:h3 "I for one welcome our new insect overlords."])}))





This code should look familiar in that the reagent-render function is exactly like any other component function we have seen before. It has been wrapped explicitly in a create-class call. The only difference is that we can also specify other lifecycle functions, which we will make use of soon. 

A React Class lifecycle consists of:

Mounting (Occurs once when the component is created)

  • constructor
  • componentWillMount
  • render
  • componentDidMount

Updating (Occurs many times as the component reacts to change)

  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

Unmounting (Occurs once when the component will be removed from the DOM)

  • componentWillUnmount
You can read more about the component lifecycle in the React docs.

Fortunately we can take a much simpler view of the world in Reagent! The two lifecycle functions that can be of additional use to us are the componentDidMount function, which will allow us to interact with the created DOM elements, and the componentWillUnmount which will allow us to do cleanup.

Before we go much further into what create-class allows us to do, let's take a brief interlude to examine a case where it looks like we need to use a class, but in fact we can avoid using one.

Example O: Performing cleanup


(defn mouse-position []
(reagent/with-let [pointer (reagent/atom nil)
handler (fn [e]
(swap! pointer assoc
:x (.-pageX e)
:y (.-pageY e)))
_ (js/document.addEventListener "mousemove" handler)]
[:div "Pointer moved to: " (str @pointer)]
(finally
(js/document.removeEventListener "mousemove" handler))))

The finally clause of with-let will run when mouse-pos is no longer tracked anywhere, in this case when tracked-pos is unmounted. The same thing could be achieved with a Class that specified a component-will-unmount.

Reagent has well thought out facilities that allow us to write our components as simple functions that respond to change. Knowing the model of how those function relate to the underlying React lifecycle is useful for reasoning about component behaviors and being able to choose concise functions as much as possible. The bottom of the abstraction is creating the Class directly. Why do we still need the ability to create a Class?





Well... one case is that when we need to access the DOM node of the component we constructed. This comes up when making use of non-React JavaScript UI libraries. For instance if you want to use Google Charts; you need to call a render function on a target element after it is created. This is where the the component-did-mount lifecycle method becomes valuable.

Tip: You can alternatively provide a ref function as an attribute to a hiccup form to access DOM nodes. The ref function will be called with the DOM element on every render. This is less generally useful so we'll focus on using the class lifecycle.

Let's see how to make use of form 3 by creating a ThreeJS canvas.

Example P: Reagent component form 3 - Creating a ThreeJS canvas


(defn create-renderer [element]
(doto (js/THREE.WebGLRenderer. #js {:canvas element :antialias true})
(.setPixelRatio js/window.devicePixelRatio)))

(defn three-canvas [attributes camera scene tick]
(let [requested-animation (atom nil)]
(reagent/create-class
{:display-name "three-canvas"
:reagent-render
(fn three-canvas-render []
[:canvas attributes])
:component-did-mount
(fn three-canvas-did-mount [this]
(let [e (reagent/dom-node this)
r (create-renderer e)]
((fn animate []
(tick)
(.render r scene camera)
(reset! requested-animation (js/window.requestAnimationFrame animate))))))
:component-will-unmount
(fn [this]
(js/window.cancelAnimationFrame @requested-animation))})))

This is a more involved example to show the use of a non-React JavaScript UI library. We are using the ThreeJS library to render a scene. The important thing to look for in this example code is the use of lifecycle methods; reagent-render is a component function that returns hiccup HTML, component-did-mount is called when the element is mounted into the page, and component-will-unmount is called just before the element leaves the page.

There are 2 interop tasks we do in our ThreeJS component:
  1. We start a request animation frame loop to render the scene. But we are careful to stop the animation loop when the component is unmounted. This will allow our component to play nicely with our page if we add and remove it.
  2. We create a renderer that targets the DOM node after it is mounted into the page.
Ok great, but where's our scene? We need to construct some lights and objects to see anything interesting. Let's make a 3D version of the concentric circles we made in SVG earlier.

Example Q: A ThreeJS version of concentric circles


(defn create-scene []
(doto (js/THREE.Scene.)
(.add (js/THREE.AmbientLight. 0x888888))
(.add (doto (js/THREE.DirectionalLight. 0xffff88 0.5)
(-> (.-position) (.set -600 300 600))))
(.add (js/THREE.AxisHelper. 50))))

(defn mesh [geometry color]
(js/THREE.SceneUtils.createMultiMaterialObject.
geometry
#js [(js/THREE.MeshBasicMaterial. #js {:color color :wireframe true})
(js/THREE.MeshLambertMaterial. #js {:color color})]))

(defn fly-around-z-axis [camera scene]
(let [t (* (js/Date.now) 0.0002)]
(doto camera
(-> (.-position) (.set (* 100 (js/Math.cos t)) (* 100 (js/Math.sin t)) 100))
(.lookAt (.-position scene)))))

(defn v3 [x y z]
(js/THREE.Vector3. x y z))

(defn lambda-3d []
(let [camera (js/THREE.PerspectiveCamera. 45 1 1 2000)
curve (js/THREE.CubicBezierCurve3.
(v3 -30 -30 10)
(v3 0 -30 10)
(v3 0 30 10)
(v3 30 30 10))
path-geometry (js/THREE.TubeGeometry. curve 20 4 8 false)
scene (doto (create-scene)
(.add
(doto (mesh (js/THREE.CylinderGeometry. 40 40 5 24) "green")
(-> (.-rotation) (.set (/ js/Math.PI 2) 0 0))))
(.add
(doto (mesh (js/THREE.CylinderGeometry. 20 20 10 24) "blue")
(-> (.-rotation) (.set (/ js/Math.PI 2) 0 0))))
(.add (mesh path-geometry "white")))
tick (fn []
(fly-around-z-axis camera scene))]
[three-canvas {:width 150 :height 150} camera scene tick]))

Tada! We have a 3D scene.


Exercise: Add some more meshes to the scene. Complete the Lambda symbol (λ) by adding a diagonal down mesh.

What I find really neat is that this 3D scene composes well with our existing components, here it is inside the mouse trap:

Example R: Composing 3D concentric circles with a counter


[:div
[a-better-mouse-trap [lambda-3d]]
[a-better-mouse-trap [spinnable]]]

With attention to the component lifecycle we were able to make use a library that was not designed with React or Reagent in mind. We didn't need a complicated wrapper; creating a class was the easy bit. Most of our effort was specifying the scene itself.

Seeing as we created a 3D scene, let's make use of it to draw something else. A Sierpinski 3D gasket is a recursively defined object with volume that approaches zero each step, while the surface area remains constant. That's pretty weird huh?

Example S: Sierpinski Gasket in 3D


(def pyramid-points
[[-0.5 -0.5 0 "#63B132"] [-0.5 0.5 0 "#5881D8"] [0.5 0.5 0 "#90B4FE"] [0.5 -0.5 0 "#91DC47"] [0 0 1 "white"]])

(defn add-pyramid [scene x y z size color]
(.add scene
(doto
(let [g (js/THREE.Geometry.)]
(set! (.-vertices g)
(clj->js (for [[i j k] pyramid-points]
(v3 i j k))))
(set! (.-faces g)
(clj->js (for [[i j k] [[0 1 2] [0 2 3] [1 0 4] [2 1 4] [3 2 4] [0 3 4]]]
(js/THREE.Face3. i j k))))
(mesh g color))
(-> (.-position) (.set x y z))
(-> (.-scale) (.set size size size)))))

(defn add-pyramids [scene x y z size color]
(if (< size 4)
(add-pyramid scene x y z (* size 1.75) color)
(doseq [[i j k color] pyramid-points]
(add-pyramids scene
(+ x (* i size))
(+ y (* j size))
(+ z (* k size))
(/ size 2)
color))))

(defn gasket-3d []
(let [camera (js/THREE.PerspectiveCamera. 45 1 1 2000)
scene (doto (create-scene)
(add-pyramids 0 0 0 32 "white"))
tick (fn [] (fly-around-z-axis camera scene))]
[three-canvas {:width 640 :height 640} camera scene tick]))

Suggested soundtrack for appreciating the Sierpinski gasket:





Exercise: Can you make a tetrahedral gasket by modifying the points and faces list? (Hint: you just need to delete one of the base points to make a triangular prism.) How about a cubic gasket?

ClojureScript really shines in it's facilities for avoiding repetitive boilerplate.


Lifecycle Review

Twice and thrice over, as they say, good is it to repeat and review what is good. -- Plato




Most Reagent components can be expressed as a function, especially if they rely on state being passed to them as an argument. Some components will create local state, access the DOM node, or need to do some setup/teardown. All components have a lifecycle. They get created, mounted into the DOM, rendered, and unmounted from the DOM. A component potentially calls render many times as it's inputs change. It remains in the DOM until unmounted.

In order to show the lifecycle in action, let's log what's happening in a form 2 component (a function that returns a function).

(def messages (reagent/atom []))

(defn log [& args]
(apply cljs.core/println args)
(swap! messages
(fn [xs]
(doall (take 10 (cons (apply str (.toLocaleTimeString (js/Date.)) "| " args) xs))))))

(defn with-log [component]
[:div
component
(into
[:ul]
(for [line @messages]
[:li line]))])

Example T: Observing the lifecycle of a puppy


(defn puppy [x]
(log "puppy created, x:" x)
(let [mouse-over? (reagent/atom false)]
(fn [y]
(log "puppy rendered, x:" x " y:" y " mouse-over?:" @mouse-over?)
[:span {:on-mouse-over (fn [e] (reset! mouse-over? true))
:on-mouse-out (fn [e] (reset! mouse-over? false))}
[:img {:src "https://goo.gl/fMzXOU"
:style {:width "150px",
:border "1px solid",
:transform (str "scale(" (if @mouse-over? 1.1 1) ")")}}]])))

(defn lifecycle-review []
(reagent/with-let [x (reagent/atom "1")]
[:div
[:label "Type in a value for x: "
[:input {:on-change (fn [e] (reset! x (.. e -target -value)))}]]
[with-log [a-better-mouse-trap [puppy @x]]]]))

Pop quiz: Enter a string in the text box above and mouse over the puppy. You should see that x and y do not match. Why are they different? Now click "catch" to create a new puppy. See that x now has the new value when you mouse over the new puppy, but x still has the old value when you mouse over the old puppy. Can you explain why?

As you can see by playing with this example, puppy is called once at creation, but the function it returns is called whenever you mouse over the puppy. One trap to avoid is forgetting to specify the inner function arguments, or giving them a different name. I intentionally gave them different names in the above example to demonstrate that x is being captured from the outer function. The captured value won't change! However this is easily avoided if you keep the inner function arguments identical to the outer arguments. If the arguments are identical, the inner function will not capture any of the outer bindings.


Conclusion

The pain of parting is nothing to the joy of meeting again. -- Charles Dickens


We have reached the second stop of our deep dive tour.

At this point we have covered the principal syntax and features of Reagent. We observed a variety of UI challenges and the forms Reagent provides to address them. Reagent's fundamental abstraction is a view component. A function that returns HTML as hiccup is a component. A function that returns a function is a component. A function that returns a Class is a component. These three forms allow us to manage lifecycle concerns such as state and DOM node interaction.

Editing code in the browser itself is a great way to try out Reagent. If you want to build some larger ideas you might find KLIPSE useful. If you enjoy interactive tutorials, make sure you check out the excellent articles in the KLIPSE blog.

In the next leg of our tour we will examine the nuances of dealing with sequences of subcomponents. I hope you can join me again for that soon!

Permalink

Expressive types, not oppressive types

Uncle Bob wrote a recent post in which he warns programmers against the "dark path" some modern languages have taken - that is to "double down" on static typing. He cites Swift and Kotlin as examples, though his argument is meant to be interpreted more generally.
I share many points of view in common with Uncle Bob. I find the dynamically typed Clojure programming language beautiful and expressive - most of my personal projects are written in Clojure. I think that TDD (test-driven design) is a valuable and important discipline - I work for an agile consulting company where most of our projects include helping clients to get better at testing.

But I disagree strongly with the way Uncle Bob frames this discussion on static types.

Uncle Bob looks at advanced type systems and sees them as more oppressive rather than more expressive. Being able to describe whether or not a function can return null is an opportunity, not a constraint. Being able to use types to describe your code's intent is an opportunity, not a constraint. Being able to reason about the behaviour of a function based on its type signature is an opportunity, not a constraint.
The kicker is that this is almost exactly the fallacy about TDD that we have railed against for years. We call it "test-driven design" because we know that evolving code in response to examples is a great way to inform a design. Folks who have not learnt to listen to their unit tests see them as nagging constraints that prevents them from writing code in the way they'd like. A master of TDD uses tests as feedback for their design.

Anyone who sees unit test as mere "checks" that make changing code needlessly difficult isn't getting the most out of test-driven design. Anyone who sees static types as mere "checks" that make changing code needlessly difficult isn't getting the most out of type-driven design.
Based on his post Uncle Bob falls into the latter category. He sees types as ad hoc antidotes for specific mistakes rather than tools for thought - "Every time there’s a new kind of bug, we add a language feature to prevent that kind of bug."
If that's Uncle Bob's experience of Swift and Kotlin, he should try Elm. Or F#. Or Haskell. If his experience is anything like mine, he would find that more sophisticated types lead to less ad hockery, not more.

In a follow-up post, Uncle Bob is explicit about what he wants in a programming language - "There is a balance point after which every step down The Dark Path increases the cost over the benefit. I think Java and C# have done a reasonable job at hovering near the balance point."
I couldn't disagree more. Java and C# have two of the most onerous and least beneficial type systems. Their complexity and absence of type inference force excessive bookkeeping on the programmer. They lack of basic features like sum types, which denies the programmer an important expressive idiom.
Java and C# represent the nadir of the type system trade-off, not the zenith. Type systems are tools. Better tools help us write better code. We should welcome each and every advance in the tools we use to do our job, because frankly we could do a lot better than what we have now.

To argue that employing more expressive types is a "dark path" that leads developers away from personal responsibility isn't accurate or helpful.

Permalink

Front-end Engineer (ClojureScript/Reagent/re-frame) at Apstra, Inc (Full-time)

We’re extending our front-end team to work on our main application (AOS) and a set of internal tools. All applications are SPAs written in ClojureScript/Reagent/re-frame on top of REST-like APIs and can be classified as “Admin Dashboards” for infrastructure management software (tables, menus, forms, charts, wizards and custom visualizations)

Day-to-day job

  • Collaborate with product management team to define UI for existing use cases
  • Design UI and collaborate with back-end team to define API that will allow to implement given use cases
  • Implement UI for given use cases and provide a feedback to back-end team and product management team on what can be changed in API/use case to achieve better UX
  • Work with the rest of the front-end team to define common approaches, utils and UI-components that can be reused across multiple applications
  • Keep codebase up-to-date with the latest and greatest technologies and patterns which appear in the industry

Successful Candidate Profile

  • Recent experience in building complex UIs (preferably SPAs)
  • Working knowledge of Clojure(Script), Reagent and re-frame
  • Good enough knowledge of HTML/CSS/JavaScript and experience using CSS frameworks
  • Ability to work without supervision and proactively communicate about issues/blockers
  • Background in Computer Science (or self-education in algorithms/data structures)
  • (Bonus) Experience in building custom visualizations for large datasets (preferably with D3)
  • (Bonus) Basic understanding of computer networks and networking domain

Get information on how to apply for this position.

Permalink

Clojure in Berlin: Red Pineapple Media

Love that you can use the same language for both the front and the back end. This is the best decision this company has made.

Targeted Video Advertising

Red Pineapple Media is a Berlin based video advertising company. They supply a video player with advanced tracking and targeted advertising capabilities, providing services to clients such as newspaper websites and gaming companies.

They have been going for 5 years and all of the original code was written using PHP. They started the switch to Clojure about two years ago and it's becoming their primary programming language of choice.

To learn more about this Clojure adoption story I had a video chat facilitated by Head of Engineering Luis Soares, with full stack developer Damiano Rühl and recent hire Joaquim Torres.

Background

Jon: So what is the tech story of Red Pineapple Media?

Damiano: The original system was built using PHP, JavaScript and MySQL. Then we moved step by step to Clojure, still using MySQL. At some point we moved to Cassandra and now we are moving to Elasticsearch.

Luis and Joaquim

Jon: Why did you move away from PHP?

Damiano: The main reason was for performance; PHP has a model that doesn't fit well for lots of requests on the same server, with each request requiring its own process. We knew we had to switch tech. We thought about Node.js and we also investigated Clojure. We had big debates.

Node.js was the obvious choice; everyone can do JavaScript so it was the safe decision.

Jon: And Clojure?

Damiano: A former colleague was advocating doing Clojure for the functional approach. We read a lot about it; we knew we wanted the Java ecosystem which we saw as a big benefit, but then no one wanted to do Java. In the end we decided to try Clojure, and if it didn't work then we would go with Node.js.

Jon: How did you introduce it?

Damiano: We wanted to do some mobile web advertising and we chose Clojure for this.

Damiano

After about two weeks with two of us, we were like: 'yes this feels right - we definitely have to do this with Clojure'. It took us more time to understand the concepts and we made mistakes design wise such as how to structure code etc.

But from the beginning we were more productive and the code was better quality - already much better than PHP.

From there we re-implemented the whole of our tracking code and it looked much better. We then made the whole delivery of ads - the biggest part of the system - use Clojure.

Now our code is much cleaner, nicer, and easier to change, as well as being more easily testable. We have much more trust in the code that everything is fine.

We recently implemented a new way of delivering ads and it was really easy to add, especially when compared to the old OO approach in PHP.

PHP/OO Diversion:

Jon: OO in PHP? I had no idea.

Damiano: Yeah. PHP became an OO language at some point. Started as a template language then became OO.

Joaquim: We were using Laravel - the Rails of PHP. It has a hollistic approach like Rails

The JVM:

Jon: So you moved to the JVM. Did you have any concerns about it being too heavy?

Damiano: For me personally - yes. I was concerned about the JVM; I'd worked in a Java company before and I disliked Java.

But actually we didn't feel the JVM so much. You have the project and Leiningen and the JVM; it all just worked. We didn't have to understand the JVM internals.

The Java ecosystem is a big benefit. Whenever we have a big problem to solve - usually it's solved in Java in a performant way.

I have a different feeling about the JavaScript ecosystem. I often find something I want to use but when I look into it I find it's broken. In Java maybe they do things a bit more professionally. We really like the possibility of using Java libraries where we can.

Jon: The JVM isn't too slow?

Damiano: Of course starting the REPL is slow. Then we read a post from Stuart Sierra on his REPL workflow - stopping and starting the app - so then we didn't have to restart the REPL all the time.

ClojureScript

Jon: Have you guys looked into ClojureScript?

Damiano: Next year we want to use ClojureScript for the front end. We use ClojureScript for one internal tool, using React native.

Jon: What ClojureScript framework will you choose next year?

Damiano: Reagent and Re-frame. Om is a possibility, but we'll probably go for Reagent.

Clojure

Jon: So overall has Clojure been good for you?

Damiano: A big part of Clojure is the advantage of FP over OO - a new thing for me and a rethink. It just feels right - it's fun to do Clojure.

Luis: A common trait is that people say the language is very well balanced. Clojure has the nice strengths of FP, of dynamic languages, of Lisp, and now we've got spec. Put all of this on top of a super strong ecosystem - the JVM - it becomes very hard to beat. Clojure is such a well balanced language.

Damiano: I came from JavaScript. I never really liked Java - it's too strict. JavaScript was super dynamic and I got the same feeling from Clojure; something I really enjoy coding. The problem of a lack of a type system has been solved by the community (Schema, spec).

I also love that you can use the same language for both the front and the back end. This is the best decision this company has made.

Hiring

Jon: How have you found hiring?

Damiano: It's much easier now than hiring PHP devs. With PHP you find a lot of devs but not many that are so experienced or passionate. Also they often had the wrong idea of how experienced they were - super genius developers that were actually relatively junior. The hiring process improved a lot moving to Clojure.

Luis: When we started hiring we removed the constraint that the person needed to know Clojure - but rather that they wanted to learn. Hire for the attitude, train for the skill.

Training

Jon: How about training?

Joaquim: When I joined it was still the case that most developers didn't know Clojure. We paired developers that were proficient with developers that were less proficient. People really enjoyed learning and it surpassed every expectation we had.

Luis: We started a reading group meeting every week. A chapter at home, then we do exercises together. Right now we're going through the Joy of Clojure.

We also participate in local Clojure meet ups and tech talks in companies. We go to conferences such as EuroClojure.

IDE wars

Jon: What IDE do you guys use?

Luis: I'm using plain vanilla Emacs, two of us are using Spacemacs. One person is using Intellij and is slowly thinking of switching to Cursive.

Damiano: I just switched from Light Table to Spacemacs. Light Table was really good to start with; the REPL being 'in there' was really nice, but this feature got removed at some point. It wasn't super stable and was difficult to find documentation. Spacemacs added all these features so I knew I had to use it.

State of Clojure?

Jon: What's the state of Clojure in Berlin?

Damiano: It's a nice community but still quite small. When Zalando moved in it was a big thing. We have a small set of companies. Fy is one company I know of that's been into Clojure from the beginning.

It's small community but it's growing.

Joaquim: We also have clojureD that is really cool with good talks.

Frustration

Jon: Any frustrations with Clojure?

Damiano: Error messages and stacktraces. Also even with Component you have to restart the REPL occasionally, it's a bit annoying.

Jon: Is PHP better for error messages?

Damiano: Haha. Not good, but maybe better.

Technologies

Jon: What technologies would you like to give a shout-out to?

Damiano: One big learning that has helped us a lot is Schema. It helps us to design the code from the beginning and makes implementation easier. I'd like to move to spec but this is a big thing because we have a lot of code.

Luis: There was a talk in the Berlin Clojure meeting (2 nights ago) by Jan Stepien on Mutation Testing. It's nice to see it's possible to rewrite the problems as they are running. This is complimentary to traditional code coverage tools. I find it a really interesting idea. Maybe for me there are some doubts about scaling it on a bigger codebase, but we will see.

Timbre for logging is a nice library. I'm curious about Onyx. We did some experiments and liked it, it's a possibility for us.

Joaquim: We are rooting for Clojure 1.9 to be released so that we can start seeing and playing with Spec.

Technologies

Check out the brand new Red Pineapple Media engineering blog. Luis also maintains a blog.

Permalink

XT16 - Portia Tung

"If creativity is intelligence having fun, what's behind that intelligence?"

Portia and I used to work together with Exoftware in the noughties, training and consulting in XP. Those years were very educational for me, working in lots of companies, mostly trying to contribute to solving difficult problems in big teams and code-bases. It was far from an easy role.

But the best thing about the company was the regular offsites where we all came together to share stories, and that's when I got to see first-hand Portia's skill of looking at problems a different way, often perceiving the human aspects behind the seemingly intractable technical or organisational issues. As Jon is fond of reminding us, software is, ultimately, a people problem.

I bumped into Portia at the station on the way down to London from Milton Keynes and we were able spend the train journey catching up, and she told me of her role as a playmaker. We discussed how computer programming, as a series of fun and addictive puzzle-solving activities, is a form of play. But Portia explained how it went much further than that...

From the very beginning you'll see Portia's unique style, involving all of us in her talk. I hope you enjoy it and if you have any comments, use our Disqus forum below.

Permalink

The REPL

The REPL

Playing chess without the board
View this email in your browser

The REPL

Notes.

It's a lovely sunny day here in Auckland. I hope you're enjoying summer if you're in the southern hemisphere, and keeping warm if you're in the north.

-main

Libraries & Books.

  • Mach is a remake of make in ClojureScript
  • Tire-iron brings the reloaded workflow to ClojureScript
  • Dirac has reached 1.0.0 and one year old. I use Dirac heavily for the inline REPL in Chrome devtools and debugging at breakpoints. I highly recommend looking at if if you do ClojureScript development.
  • Ring 1.5.1 released to fix a path traversal vulnerability
  • Pedestal 0.5.2 has some bug fixes and security updates for the path traversal vulnerability too
  • Immutant 2.1.6 was also released

People are worried about Types. ?

Foundations.

Tools.

Learning.

Misc.

"I want you to imagine you live in a world where everyone plays Chess against everyone else. … The oddest part of this however is that no-one in this world has actually seen a Chessboard. … Unbeknownst to either player, there is actually a board and when they press a piece then a piece of that type is randomly selected. … But all of this is hidden from the players, they don’t know any of this exists, they have no concept there is a board … all they see is the control panel and the sequence of presses."

- Why big data and algorithms won't improve business strategy
Copyright © 2017 Daniel Compton, All rights reserved.


Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list

Email Marketing Powered by MailChimp

Permalink

Immutant 2.1.6 Release

We just released Immutant 2.1.6. This release includes the following changes:

  • Update to Ring 1.5.1 to address a security vulnerability. This vulnerability only affects applications that are running from the filesytem, not from an uberjar or war, so most users aren't affected.
  • Remove our dependency on Potemkin. This was a common source of collision with other application dependencies, so we now use an internal copy of Potemkin under different namespaces so it doesn't conflict.
  • A minor update of the version of tools.nrepl on which we depend (0.2.11 -> 0.2.12)

What is Immutant?

Immutant is an integrated suite of Clojure libraries backed by Undertow for web, HornetQ for messaging, Infinispan for caching, Quartz for scheduling, and Narayana for transactions. Applications built with Immutant can optionally be deployed to a WildFly or JBoss EAP cluster for enhanced features. Its fundamental goal is to reduce the inherent incidental complexity in real world applications.

Get In Touch

As always, if you have any questions, issues, or other feedback about Immutant, you can always find us on #immutant on freenode or our mailing lists.

Issues resolved in 2.1.6

Permalink

Immutant 2.1.5 Release

We just released Immutant 2.1.5. This release includes the following changes:

  • Support for deployment to the newly-released JBoss EAP 7
  • A fix to allow you to use XA, immutant.messaging/listen, and a remote context together
  • We no longer transitively depend on two different versions of jboss-logging
  • Our Undertow dependency has been updated to 1.3.23.Final (from 1.3.0.Beta9) to fix issues with HTTP/2 in Chrome (IMMUTANT-621)
  • Other updates to our transitive depenencies, including:
    • java.classpath 0.2.2 -> 0.2.3
    • potemkin 0.4.1 -> 0.4.3
    • tools.nrepl 0.2.7 -> 0.2.11

We're especially proud to announce full support for EAP7 on the day of its official release, expanding our supported deployment options to the following:

  • Standalone
  • JBoss EAP 6.4 and 7.0
  • WildFly 8, 9, and 10
  • Clojure 1.7, 1.8, and 1.9

Our comprehensive, automated integration test suite verifies all of these whenever a change to Immutant is committed.

What is Immutant?

Immutant is an integrated suite of Clojure libraries backed by Undertow for web, HornetQ for messaging, Infinispan for caching, Quartz for scheduling, and Narayana for transactions. Applications built with Immutant can optionally be deployed to a WildFly or JBoss EAP cluster for enhanced features. Its fundamental goal is to reduce the inherent incidental complexity in real world applications.

Get In Touch

As always, if you have any questions, issues, or other feedback about Immutant, you can always find us on #immutant on freenode or our mailing lists.

Issues resolved in 2.1.5

Permalink

Log API for Memory Databases

The most recent Datomic Release provides access to the Datomic Log API for memory databases. I would like to take this opportunity to describe some of the features and uses of the Datomic Log API.

The transactional log is a fundamental component of ACID database systems, a durable record of the transactions performed by the database. In addition to its critical function in ensuring ACID semantics, the Datomic log, as a sequential (in database time, t) record of all transactions, also functions as a time-ordered index to the data stored in a Datomic database.

Datomic provides access to the transaction log directly via the tx-range function and from within query using the tx-ids and tx-data functions.

An example using the log in query is now available in the Day of Datomic repo. Our example database records the streets on which our three protagonists, John, Mary, and Joe, live.

Let’s find out when Joe moved to Broadway:

(d/q '[:find ?tx
:in $ ?name ?street
:where
[?e :person/name ?name]
[?e :person/street ?street ?tx true]]
(d/history (d/db conn)) "Joe" "Broadway")

This query returns 13194139534317, the transaction ID of the transaction that asserted Joe’s street is Broadway. As in all Datomic databases, every transaction also records a timestamp, the :db/txInstant. Let’s see what wall-clock time is associated with this transaction entity:

(d/pull (d/db conn) '[:db/txInstant] 13194139534317)

So Joe moved to Broadway in 1983.

Issuing queries against the transaction log is a powerful approach for auditing the operational history of a database. Because every transaction is an entity in Datomic, we can easily retrieve the entire set of datoms for a given transaction entity. Let’s find out what else happened in the ‘Joe moves to Broadway’ transaction. This query returns all the datoms associated with the given transaction:

(d/q '[:find ?e ?a ?v ?tx ?op
:in ?log ?tx
:where [(tx-data ?log ?tx)[[?e ?a ?v _ ?op]]]]
(d/log conn) 13194139534317)

;; result:
#{[17592186045420 64 "2nd" 13194139534317 true]
[17592186045419 64 "Broadway" 13194139534317 true]
[13194139534317 50 #inst"1983-01-01T00:00:00.000-00:00" 13194139534317 true]
[17592186045420 64 "Elm" 13194139534317 false]
[17592186045419 64 "1st" 13194139534317 false]}

Note that we see the same wall clock time we found previously as well as 4 other datoms. One is the assertion of Joe moving to Broadway, one is the retraction of his previous street (1st), and the remaining two datoms are about someone else entirely. Let’s find out who:

(d/pull (d/db conn) '[*] 17592186045420)

;; result:
{:db/id 17592186045420, :person/name "Mary", :person/street "2nd"}

By using the log, we’ve determined that Mary’s move to 2nd Street was recorded at the same time (during the same transaction) as Joe's move to Broadway.

The ability to query the Datomic transaction log directly is a powerful tool for managing, administering, and using a Datomic database. The addition of the Log API to memory databases enables low-overhead testing and evaluation of Datomic’s Log API feature. We hope you find the Log API on memory databases a helpful addition for lightweight development and unit testing.

Permalink

Copyright © 2009, Planet Clojure. No rights reserved.
Planet Clojure is maintained by Baishamapayan Ghose.
Clojure and the Clojure logo are Copyright © 2008-2009, Rich Hickey.
Theme by Brajeshwar.