Hacker News new | ask | show | jobs
by thmzlt 5313 days ago
Can you elaborate on this? I'm mostly curious because I have used Sinatra, and I am starting to look at both Noir and Compojure.
1 comments

Sure thing. So superficially, Compojure and Sinatra look similar:

    get "/greet/:name" do |name|
      "Hello #{name}"
    end

    (GET "/greet/:name" [name]
      (str "Hello " name))
But these snippets of code are actually pretty different in what they do.

The Sinatra code generates a new route and adds it to an instance variable on the current object: it's a side-effectful method.

The Compojure code returns an anonymous function; it has no inherent side effects. If we wanted to do anything with it we'd want to bind it to a symbol:

    (def greeting
      (GET "/greet/:name" [name]
        (str "Hello " name)))
The other main difference is that Compojure has no implicit variables like "params" or "request". For instance, in Sinatra we could rewrite the example as:

    get "/greet/:name" do
      "Hello #{params[:name]}"
    end
But in Compojure, you don't get access to any variable you haven't explicitly bound:

    (GET "/greet/:name" {params :params}
      (str "Hello " (params :name)))
So Compojure is effectively very explicit where Sinatra is implicit.

The advantage of this approach is that Compojure is (IMO at least) better at nesting and abstracting functionality. For example:

    (context "/user/:id" [id]
      (let [user (find-user id)]
        (routes
          (GET "/history"
            (get-history user))
          (GET "/profile"
            (get-profile user)))))