https://github.com/active-group/reacl-pointfree
https://github.com/active-group/reacl-pointfree
Last synced: 8 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/active-group/reacl-pointfree
- Owner: active-group
- Created: 2019-04-15T11:48:10.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2019-08-02T16:45:30.000Z (almost 7 years ago)
- Last Synced: 2025-03-12T01:56:10.818Z (over 1 year ago)
- Language: Clojure
- Size: 7.81 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Reacl pointfree style
This project demonstrates pointfree component composition. A style of
programming with Reacl components.
## Background: pointfree function composition
Functions can be composed by applying them to explicitly named
arguments inside a lambda:
```clojure
(defn not-zero? [x]
(not (zero? x)))
```
Alternatively, you can also compose functions by using function
combinators such as `partial`, `comp`, etc.
```clojure
(def not-zero? (comp not zero?))
```
## Pointfree component composition
Reacl components can be composed in similar ways and in similar
styles. In most cases we take to the point-based style.
```clojure
(defclass to-do-item this item []
render
(div
(checkbox
(opt :embed-app-state (fn [i d] (assoc i :done? d)))
(:done? item))
(text-input
(opt :embed-app-state (fn [i t] (assoc i :text t)))
(:text item))))
```
But at some instances we may be better served by a pointfree
definition:
```clojure
(def to-do-item
(p/div
(p/focus :done? checkbox)
(p/focus :text text-input)))
```
Here, `p/div` is a higher-order component that just wraps its arguments
in a `dom/div`. App-state is not transformed at all. In contrast,
`focus` is a higher-order component that takes a lens and a
subcomponent and returns a component that manages the subcomponent's
app-state according to the lens.
```clojure
(defn focus [lens component]
(class
"focus"
this
app-state
[& args]
handle-message
(fn [sub]
(return
:app-state (lens/shove app-state lens sub)))
render
(apply
component
(opt
:reaction
(pass-through-reaction this))
(lens/yank app-state lens)
args)))
```
This project provides a set of combinators for Reacl components in
`pointfree.pointfree` and a simple example of their usage in
`pointfree.core`. The usual caveats of higher-order components apply.
## Discussion
With the point-based style we give the input app-state a name that is
bound to a value at runtime.
```clojure
(defclass to-do-item this item ...)
```
This is similar to naming arguments in a function definition and is
therefore easy to understand. However, components must do more work
than functions. In addition to an input ("downward") app-state we must
also specify what happens with output ("upward") app-state.
```clojure
...
render
...
(checkbox
(opt :embed-app-state (fn [i d] (assoc i :done? d)))
(:done? item))
...
```
With `:embed-app-state` we specify the upward flow of app-state.
Still, syntactically, we specify this flow as an option that we pass
"down".
In pointfree style we can avoid this awkwardness with a careful design
of the combinators. `focus` is a perfect example. A lens is all it
takes to fully specify both upward and downward flow of app-state.
### Local state
We have not yet found a satisfying way of dealing with local-state in
pointfree style. We might need to get rid of the concept
entirely in order to find a more principled solution for the problems
that we currently try to solve with local-state.
### Names
Without rigid static types, the inputs of pointfree definitions are
harder to infer than with point-based definitions, which give the
reader the names of the inputs as one more data point. You should
probably either specify the input types via comments, `:pre`, specs or
by giving the definition itself a more descriptive name.
```clojure
(defrecord Todo [done? text id])
(def todo-component
(p/div
(p/focus :done? checkbox)
(p/focus :text text-input)))
```