Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ck/3154288 to your computer and use it in GitHub Desktop.
Save ck/3154288 to your computer and use it in GitHub Desktop.
Rough Notes from the July 18 Clojure NYC Meetup

Organizing Clojure Projects

Presented by @stuartsierra

Stuart presented using a deck created with org-html-slideshow, a Clojure library for formatting org-mode notes as HTML slides.

General Code Structure / Organization
  1. Be careful not to make function calls across component boundaries.
  2. Protocols should be minimalistic. They should have the fundamental operations of your component.
  3. Keep the Protocol definition and its implementations in separate namespaces.
  4. Don't use a Protocol if you will only ever have one implementation.
  5. 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.

Application Lifecycle

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.

Q & A

Stuart opened the floor for informal Q & A.

  • Question: use or require?

Answer: NEVER use use! Use require.

  • Question: Why ever use Protocols?

Answer: Dispatches on the object. They implement Java interfaces. Multimethods can become tangled and difficult to maintain.

What's New in core.logic

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.

tl;dr

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.

Constraint Puzzles

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.

Practical Applications of core.logic

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
New Terminology

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."

Debugging core.logic

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.

Recommended Books

During the presentation, David and several audience members recommended a few books. In particular, David whole-heartedly endorsed The Reasoned Schemer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment