Lazy binding

20 views
Skip to first unread message

ngocdaothanh

unread,
Sep 3, 2009, 8:42:18 AM9/3/09
to Clojure
Hi,

In Rails you can create a view like this:

my_view.erb:
<%= "hello" + @name %>

I'm new to Clojure. I want to create something like the above like
this:

(defn my-view []
(str "hello" name))

The point is:
* name is not known when my-view is defined
* I don't want to pass name as a argument of my-view, this is kind of
ugly boilerplate

How can I implement this feature in Clojure?

Thanks.

Jarkko Oranen

unread,
Sep 3, 2009, 9:19:29 AM9/3/09
to Clojure
On Sep 3, 3:42 pm, ngocdaothanh <ngocdaoth...@gmail.com> wrote:
> Hi,
>
> In Rails you can create a view like this:
>
> my_view.erb:
> <%= "hello" + @name %>
>
> I'm new to Clojure. I want to create something like the above like
> this:
>
> (defn my-view []
>   (str "hello" name))
>
> The point is:
> * name is not known when my-view is defined
> * I don't want to pass name as a argument of my-view, this is kind of
> ugly boilerplate
>
If you don't pass the name as an argument, whence does my-view get it,
then?

I suppose you could use a global binding, and do (binding [*name*
"whatever"] (my-view)), but I'm not sure that's any better than just
passing in the parameter. eg.

(def *name*)

(defn my-view []
(str "hello " *name*))

(binding [*name* "world"]



Another option would be to pass the view functions a map of keywords
to replacements so that you can have a single data structure that's
easily applied to multiple views.

--
Jarkko

Jarkko Oranen

unread,
Sep 3, 2009, 9:20:45 AM9/3/09
to Clojure
Oops, accidentally sent the post before it was ready. Oh, well...

Stuart Sierra

unread,
Sep 3, 2009, 9:23:50 AM9/3/09
to Clojure
Check out www.stringtemplate.org, a Java template library with a
functional design.
-SS

tmountain

unread,
Sep 3, 2009, 9:32:38 AM9/3/09
to Clojure
I believe the way this works in rails has to do with the order in
which variables are resolved. In this case, @name is an instance
variable that's already been assigned elsewhere (your controller).
Rails loads the view after the controller class has been instantiated.
For this to work, the view is probably "mixed-into" the controller
class somehow to ensure that the instance variable is visible. If you
try this with a standard ruby function, it doesn't work.

def my_view
"hello #{name}"
end

name = "bilbo"
puts my_view()
`my_view': undefined local variable or method `name' for main:Object
(NameError)

You could obviously use a global variable, but that would be really
nasty. With Clojure, your function can see the variable so long as
it's defined ahead of time.

(def my-name "bilbo") ; name is a reserved word

(defn my-view []
(str "hello " my-name))
(my-view)
"hello bilbo"

The my-name variable doesn't even have to be bound in this case, just
created. I think what you're really looking for though is a way to
define a function with no arguments referencing a variable that isn't
defined at all. I'm not sure how you would accomplish that.

-Travis

On Sep 3, 8:42 am, ngocdaothanh <ngocdaoth...@gmail.com> wrote:

ngocdaothanh

unread,
Sep 3, 2009, 9:10:07 PM9/3/09
to Clojure
Using a map of keywords is a good solution, but I think it is somewhat
just boilerplate code to satisfy the compiler, not to satisfy
developers (yes, human). In view we have to write (:my-var map)
everywhere. Is there a better way, something like lazy variable
binding or lazy function binding?

To repair the Ruby code, instance variable or method can be used:

def my_view
"hello #{@name}"
end
@name = "bilbo"
puts my_view

Or
def my_view
"hello #{name}"
end
def name "bilbo" end
puts my_view

Ruby does not force @name or name in my_view to exist before hand,
this is really dynamic.

Well, the idea is I want to create a web framework based on Compojure
with as less code as possible in view (may be designers would like it,
it's not normal HTML anyway). Place-holders in view should be lazy,
then when an action wants to use a view, it just has to declare a
bunch of concrete values and include the desired view, something like:

(defn my-action []
(with-variables [foo "hello", bar "world"] my-view))

The Clojure code may be incorrect, but I think you have captured the
idea.

James Sofra

unread,
Sep 3, 2009, 9:07:43 PM9/3/09
to Clojure
Not to distract from the talk on how to achieve these things in
Clojure but I really want to second Stuart's recommendation of
StringTemplate, it really is a great library and is seamless to use
from Clojure.

I would recommend it for any templating needs, not just HTML views, in
fact I have used it at a micro level in Java to do just the kind of
lazy binding you are talking about, passed a template as a String
argument to a method and let the method then provide the attributes to
be bound to the template worked great.

Cheers,
James Sofra

On Sep 3, 11:23 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> Check outwww.stringtemplate.org, a Java template library with a

ngocdaothanh

unread,
Sep 4, 2009, 1:04:13 AM9/4/09
to Clojure
Compojure's default view syntax looks great (same idea as Yaw's EHTML:
http://yaws.hyber.org/dynamic.yaws). It allows one to "stay in
Clojure".

I found an example of integrating StringTemplate (and Hibernate) with
Clojure:
http://bitbucket.org/kumarshantanu/blogjure/

Is there more example of integrating StringTemplate with Clojure?
Reply all
Reply to author
Forward
0 new messages