Skip to content

Instantly share code, notes, and snippets.

@noahlz
Created July 20, 2012 17:28
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save noahlz/3152034 to your computer and use it in GitHub Desktop.
Save noahlz/3152034 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.

@tolitius
Copy link

I still feel that "ever" might be too strong for

(def report-count (atom {}))

Thinking about ClojureScript.. So what a user owns that atom? It is isolated (by a single threaded nature of JS) to that particular user for the live of request/response, HTTP session, etc...

But the notes are great, thanks Noah.
/Anatoly

@noahlz
Copy link
Author

noahlz commented Jul 20, 2012

If you're in a single-threaded host, no reason to use an atom. Just use a var.

@tolitius
Copy link

Why would you prefer a Var over an atom to hold a "user" state in ClojureScript?

@noahlz
Copy link
Author

noahlz commented Jul 21, 2012

I don't know. Can you share your reasoning?

My thought was that if you truly are in a single thread of execution, there's no reason to use atomic operations.

@tolitius
Copy link

sure.

usually atoms are used in ClojureScript to keep a state between JS events. e.g. take this game for example: http://bit.ly/QchE1q notice how a game state is initialized:

(defn ^:export init []
        ...
        state (atom {})
        ...

and then swapped (e.g. changed) on every game event, e.g. "click", "mouse-move", "tick-ball", etc..

I am not using ClojureScriptOne, but if you look at the docs ( http://clojurescriptone.com/documentation.html ), they use atoms (just search for "atom") all over the place to keep state. One of them is actually ironically called "state":

(def
  state (atom {}))

In general, if we not talking about Web Workers, which provide true multithreading, JavaScript is single threaded, but it also (can be, and most of the time is) async. Which just means there is a "main" loop that roughly does this:

while ( true ) {
    event = nextEvent( all_event_sources );
    handler = findEventHandler( event );
    handler( event );
}

Hence each event can potentially change the state of a page, especially given the approach of "single page" applications (e.g. "gmail" is a good example of that).

Therefore ClojureScript "atom" is a perfect candidate to keep that cross event state.

/Anatoly

@noahlz
Copy link
Author

noahlz commented Jul 24, 2012

Videos of both presentations are up on Vimeo:

Clojure in the Large: http://vimeo.com/46163090
core.logic: http://vimeo.com/46163091

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