Presented by @stuartsierra
Stuart presented using a deck created with org-html-slideshow, a Clojure library for formatting org-mode notes as HTML slides.
- Be careful not to make function calls across component boundaries.
- Protocols should be minimalistic. They should have the fundamental operations of your component.
- Keep the Protocol definition and its implementations in separate namespaces.
- Don't use a Protocol if you will only ever have one implementation.
- Don't do this, ever:
(def report-count (atom {}))
Quote: "The last 50 years of software development have been about avoiding global-scope mutable variables."
At this point, someone observed that the above rules effectively recreate the
defsystem
from Common Lisp.
Stuart commented on the problems around cleanly starting/stopping large applications. The solution his team has devised is to define a "Lifecycle" protocol with start/stop methods, and then have something like this in your -main
:
(map start [config-module db-module webapp-module])
He also showed various code snippits for interactively reloading application instances without having to do a full startup/shutdown.
Stuart also mentioned various REPL helpers for interactively restarting application instance. I don't recall the URL or name of this project (if it is publicly available).
Note: Stuart mentioned that he doesn't put his slides online. However, I got the impression that some sample code (or an alpha library) is available online somewhere.
Stuart opened the floor for informal Q & A.
- Question:
use
orrequire
?
Answer: NEVER use
use
! Userequire
.
- Question: Why ever use Protocols?
Answer: Dispatches on the object. They implement Java interfaces. Multimethods can become tangled and difficult to maintain.
Presented by David Nolen (@swannodette)
https://github.com/clojure/core.logic/
Disclaimer: I'm new to Clojure, but really new to logic programming (other than my limited exposure to Datomic), so apologies if I got something wrong or left out an interesting detail.
If you want to understand what core.logic does, you need to learn Prolog (see Recommended Books below). Many of operations and concepts in core.logic are ported from that language.
Much of what David described as a bit abstract, so he gave a concrete example that demonstrates how core.logic really shines. Although I didn't sketch Evan's solution, I found the rough equivalent on StackOverflow
Baker, Cooper, Fletcher, Miller, and Smith live on different floors of an apartment house that contains only five floors. Baker does not live on the top floor. Cooper does not live on the bottom floor. Fletcher does not live on either the top or the bottom floor. Miller lives on a higher floor than does Cooper. Smith does not live on a floor adjacent to Fletcher's. Fletcher does not live on a floor adjacent to Cooper's. Where does everyone live?
(run* [tenants]
(fresh [a b c d e]
(== [a b c d e] tenants)
(permuteo tenants '[Cooper Baker Fletcher Miller Smith])
(!= e 'Baker)
(!= a 'Cooper)
(!= a 'Fletcher)
(!= e 'Fletcher)
(beforeo 'Cooper 'Miller tenants)
(not-adjacento 'Smith 'Fletcher tenants)
(not-adjacento 'Fletcher 'Cooper tenants)))
;; ([Smith Cooper Baker Fletcher Miller])
Awesome. Note that the final code looks almost exactly like the English-language description of the problem. I don't want to think about trying to solve this problem with traditional, iterative style.
Brief discussion on how to use core.logic (aside from solving logic puzzles). David mentioned that many firms are using core.logic for internal, closed source applications.
Some potential applications:
- "Business rules"
- HTML generation (offered by an audience member)
- Online RPG, i.e. create a table of character classes vs allowed weapons / skills / equipment, etc. Then devise a function that tests if a character is allowed to take an action based on the table. Compare with the typical polymorphic approach to such problems.
- Using core.logic with Datomic
Being completely new to logic programming, I was having trouble following much of the presentation and ad-hoc discussion. I noted some terminology to research later:
- "goals" and "relations"
- "existential variable"
- "proper tails"
- "relational binary system based on half-adders"
- cKanren / miniKanren
- "backtracking algorithm"
- "subsitution maps"
- "unification" / "unity"
During this dicussion, David observed that "Prolog is always depth-first."
Someone asked how to debug complex core.logic functions. David didn't have a canned answer, other than to point out that under the covers, core.logic is threading standard Clojure persistent collections through decision trees and backing out when a test fails.
So, for most purposes, it seems just inserting a standard println
or REPL breakpoint will do the job.
During the presentation, David and several audience members recommended a few books. In particular, David whole-heartedly endorsed The Reasoned Schemer.
Why would you prefer a Var over an atom to hold a "user" state in ClojureScript?