multiple sets on one item - is this a good idea?

1 view
Skip to first unread message

chris

unread,
Dec 20, 2008, 1:15:09 AM12/20/08
to Clojure
Hello, I am gearing up to write some swing code, specifically some
stuff where I want to use the grid bag layout system, and I remember
something I hated about java:

--c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 2;
c.gridy = 0;
--

You repeated c all over the place so you didn't dare name it something
reasonable.

I noticed in some closure code this looks about the same (actually
worse):
(set! (.fill c) GridBagConstraints/HORIZONTAL)
(set! (.anchor c) GridBagConstraints/PAGE_END)
(set! (.weightx c) 1.0)
(set! (.weighty c) 1.0)

So I decided to do something about it:

user> c
#<GridBagConstraints java.awt.GridBagConstraints@f01381>
user> (defmacro sets! [vars & rest] `(do ~@(map (fn [flds] `(set! (.
~vars ~(first flds)) ~(second flds))) rest)))
nil
user> (macroexpand '(sets! c (fill GridBagConstraints/HORIZONTAL)
(weightx 1.0)))
(do (set! (. c fill) GridBagConstraints/HORIZONTAL) (set! (. c
weightx) 1.0))
user> (sets! c (fill GridBagConstraints/VERTICAL) (weightx 1.0))
1.0
user> (. c fill)
2
user> (sets! c (fill GridBagConstraints/VERTICAL) (weightx 1.0))
1.0
user> (. c fill)
3
user>

Is this a good idea or did I do something ridiculous? I am a clojure
newb, so please feel free to be quite explicit.

Chris

Christophe Grand

unread,
Dec 20, 2008, 2:28:57 AM12/20/08
to clo...@googlegroups.com
chris a écrit :

> Hello, I am gearing up to write some swing code, specifically some
> stuff where I want to use the grid bag layout system, and I remember
> something I hated about java:
>
> --c.fill = GridBagConstraints.HORIZONTAL;
> c.weightx = 0.5;
> c.gridx = 2;
> c.gridy = 0;
> --
>
> You repeated c all over the place so you didn't dare name it something
> reasonable.
>
> I noticed in some closure code this looks about the same (actually
> worse):
> (set! (.fill c) GridBagConstraints/HORIZONTAL)
> (set! (.anchor c) GridBagConstraints/PAGE_END)
> (set! (.weightx c) 1.0)
> (set! (.weighty c) 1.0)
>
Your sets! macro seems a good idea to me.

When an object provides proper accessors, you can write:

(doto c
(.setFill GridBagConstraints/HORIZONTAL)
(.setAnchor GridBagConstraints/PAGE_END)
(.setWeightx 1.0)
(.setWeighty 1.0))

But in your case, the only way to use doto I can think of is:

(doto c
(-> .fill (set! GridBagConstraints/HORIZONTAL))
(-> .anchor (set! GridBagConstraints/PAGE_END))
(-> .weightx (set! 1.0))
(-> .weighty (set! 1.0)))

which does not repeat c but is even slightly longer than the naive
sequence of set!.

I think that you could remove some parens from your macro form to just
write:

(sets! c
fill GridBagConstraints/HORIZONTAL
weightx 1.0)

(I think it's a more idiomatic form, see cond or bindings: pairs aren't
paired together.)

(defmacro sets! [vars & rest]
`(do ~@(map (fn [flds] `(set! (. ~vars ~(first flds)) ~(second flds)))

(apply array-map rest))))

Christophe

chris

unread,
Dec 20, 2008, 11:15:50 AM12/20/08
to Clojure
That helped, thanks Christophe.

I have one more problem:

I put it in a util file, under a util namespace:

(ns lambinator.util)

(defmacro sets! [vars & rest]
`(do ~@(map (fn [flds] `(set! (. ~vars ~(first flds)) ~(second
flds))) (apply array-map rest))))


Now I want to use it outside that namespace. It seems that I have to
do two things when I load from a jar...

(require 'lambinator.util)

(lambinator.util/sets! c fill GridBagConstraints/VERTICAL weightx 1.5)

How do I use the function/macro outside the namespace it was created
in without prefixing it with lambinator.util? Import, require don't
seem to do what I want...

Chris

chris

unread,
Dec 20, 2008, 11:25:52 AM12/20/08
to Clojure
nm, I need to use 'use'.

I am unclear as to the difference between refer, import use, and
require.

Chris

Chouser

unread,
Dec 20, 2008, 11:40:29 AM12/20/08
to clo...@googlegroups.com
On Sat, Dec 20, 2008 at 11:25 AM, chris <cnue...@gmail.com> wrote:
>
> I am unclear as to the difference between refer, import use, and
> require.

'require' finds a .clj file in your classpath (which must define a
matching namespace) and loads it into Clojure.

'refer' brings definitions from some other already-loaded namespace into
your current namespace.

'use' does both in one step (use can use the :verbose flag to see it do
this)

All of these are for working with namespaces defined via Clojure code.
'import' is different in that it's only for dealing with .class files.

--Chouser

Meikel Brandmeyer

unread,
Dec 20, 2008, 11:48:16 AM12/20/08
to clo...@googlegroups.com
Hi,

Am 20.12.2008 um 17:25 schrieb chris:

> I am unclear as to the difference between refer, import use, and
> require.

require loads a lib from the classpath. The you can
access the public names of the lib's namespace via
full qualified names.

(require 'foo.bar)
(foo.bar/baz "Hurray")

refer let's you refer an already require'd namespace,
so you can access the names directly.

(require 'foo.bar)
(refer 'foo.bar)
(baz "Hurray")

use is basically a require followed by a refer.

(use 'foo.bar)
(baz "Hurray")

alias can be used to introduce an alias for a require'd
namespace.

(require 'foo.bar)
(alias 'fb 'foo.bar)
(fb/baz "Hurray")

import is a "refer for classes". It is a direct translation
of the Java import.

w/o import:
(new java.io.BufferedReader ...)

w/ import:
(import '(java.io BufferedReader))
(new BufferedReader ...)

However these are all very low-level. The preferred
way is to use the :keyword notation in the ns macro.

(ns my.name.space
(:require
[foo.bar :as fb])
(:import
(java.io BufferedReader)))

(fb/baz "Hurray")
(new BufferedReader ...)

There are lots of other options :exclude, :only, ...

Hope this helps.

Sincerely
Meikel

Stuart Sierra

unread,
Dec 20, 2008, 11:53:56 AM12/20/08
to Clojure
This might make a good FAQ question:

On Dec 20, 11:25 am, chris <cnuern...@gmail.com> wrote:
> I am unclear as to the difference between refer, import use, and require.

Hi Chris,

require: Load a Clojure library from a file on classpath. Use this
when you want to load a library, but leave it in its own namespace.

refer: Bring public symbols from one namespace into the current
namespace. Use this when a library has already been loaded (example:
clojure.set) but you want to use its public symbols without a
namespace qualifier.

import: Copy Java class names into current namespace. Use this when
you want to use a Java class without typing the full package name.

use: Combination of require and refer. Use this when you want to load
a library AND use its symbols without a namespace qualifier.

All four are available as keyword arguments to "ns", which is the
preferred way to use them:

(ns foo.bar
(:use my.little.lib)
(:require clojure.contrib.duck-streams)
(:import (java.io File InputStream))
(:refer clojure.set))

":require" also allows aliasing, like this:
(ns foo.bar
(:require [clojure.set :as set]))

Now you can write the symbol "clojure.set/union" as "set/union".

-Stuart Sierra

Brian Doyle

unread,
Dec 20, 2008, 12:25:17 PM12/20/08
to clo...@googlegroups.com
This would make an excellent FAQ question and answer!

Brian Doyle

unread,
Dec 20, 2008, 12:26:41 PM12/20/08
to clo...@googlegroups.com

Instead of require call use and should get you what you want.

On Dec 20, 2008 9:15 AM, "chris" <cnue...@gmail.com> wrote:


That helped, thanks Christophe.

I have one more problem:

I put it in a util file, under a util namespace:

(ns lambinator.util)

(defmacro sets! [vars & rest] `(do ~@(map (fn [flds] `(set! (. ~vars ~(first flds)) ~(second flds...

Now I want to use it outside that namespace.  It seems that I have to
do two things when I load from a jar...

(require 'lambinator.util)

(lambinator.util/sets! c fill GridBagConstraints/VERTICAL weightx 1.5)

How do I use the function/macro outside the namespace it was created
in without prefixing it with lambinator.util?  Import, require don't
seem to do what I want...

Chris

On Dec 20, 12:28 am, Christophe Grand <christo...@cgrand.net> wrote: > chris a écrit : > > > Hello,...

chris

unread,
Dec 20, 2008, 12:44:32 PM12/20/08
to Clojure
That really helped out a lot, thanks guys!

As long as we are on the subject of helping out newbs...

I like to program from the repl.

I have a namespace that maintains some state, mainly integration with
openGL. It has some essentially static variables that can't get
initialized until openGL is initialized (like recording the version,
vendor, extensions, and the renderer).

I have put everything ui-related in one namespace called
lambinator.ui. I have split this namespace up into two files, on for
variable definitions and another for function definitions. The reason
is that I usually don't want my variables cleared when reloading the
file through load-file using the repl.

I am using simple def's to define the variables; the ones that may be
changed through opengl callbacks on other threads I am protecting with
refs.

So to be clear, I have:

<main-ns-file> -> this contains defs and calls "load" on the function
file
<function def file> -> this gets loaded all the time when I am working
in the repl.

1. Is using defs the best way to go about this? They are global
variables; they model static state of the system, essentially. I
might associate them with the main window, however, instead of global
defs like they are now.
2. How do defs handle updating when I reload a file in the repl and
then something in some other thread triggers a render? Lets say I
change the render function which is called from the GLEventListener
object on display. It is defined through a simple defn. Does this
change propagate through to the other threads? Are defs themselves
protected if they are at the module/file level? I was thinking maybe
all the threads shared an environment which is itself a functional
datastructure protected by refs...
3. If this is the case, do I need to protect my def'd variables with
refs?
4. Is there some way to call load that ignores variable definitions
if it has already seen them but does update function definitions
regardless?
5. Finally, in the function-def file I have an in-ns definition, not
a 'ns definition. This means that I *have* to use (import and (use,
and I can't use :import or :use, correct? The documentation for in-ns
didn't indicate that the keyword options were viable or I missed that
piece of info. An answer to 4 would make 5 irrelevant.

Again, thanks to everyone for their replies!

Chris

Christian Vest Hansen

unread,
Dec 20, 2008, 2:47:44 PM12/20/08
to clo...@googlegroups.com
Q:

> On Sat, Dec 20, 2008 at 11:25 AM, chris <cnue...@gmail.com> wrote:
>> I am unclear as to the difference between refer, import use, and
>> require.

A:


On Sat, Dec 20, 2008 at 5:40 PM, Chouser <cho...@gmail.com> wrote:
> 'require' finds a .clj file in your classpath (which must define a
> matching namespace) and loads it into Clojure.
>
> 'refer' brings definitions from some other already-loaded namespace into
> your current namespace.
>
> 'use' does both in one step (use can use the :verbose flag to see it do
> this)
>
> All of these are for working with namespaces defined via Clojure code.
> 'import' is different in that it's only for dealing with .class files.
>
> --Chouser

Good material for the FAQ, no?

Or maybe just the docs. Regardless, this is a good explanation.

--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

Stuart Sierra

unread,
Dec 20, 2008, 4:08:35 PM12/20/08
to Clojure
On Dec 20, 12:44 pm, chris <cnuern...@gmail.com> wrote:
> 1.  Is using defs the best way to go about this?  They are global
> variables; they model static state of the system, essentially.  

Yes, global variables are probably best as Vars (which is what def
creates).

> 2.  How do defs handle updating when I reload a file in the repl and
> then something in some other thread triggers a render?  

Re-evaluating a def changes the "root binding" of a Var, which affects
all threads but NOT in a protected, transactional way as with Refs.

> 3.  If this is the case, do I need to protect my def'd variables with
> refs?

If you want Ref-style transaction semantics, then yes. But if they
are static values that don't change, then no.

> 4.  Is there some way to call load that ignores variable definitions
> if it has already seen them but does update function definitions
> regardless?

Yes, use "defonce", which won't re-def things that already exist:
clojure.core/defonce
([name expr])
Macro
defs name to have the root value of the expr iff the named var has
no root value,
else expr is unevaluated

> 5.  Finally, in the function-def file I have an in-ns definition, not
> a 'ns definition.  This means that I *have* to use (import and (use,
> and I can't use :import or :use, correct?  

Not exactly. It's true, "in-ns" takes no other arguments. But
typical usage would be something like this:

top-level file "my/cool/lib.clj":
(ns my.cool.lib
(:use ...) ; everything you need to use
(:import ...) ; everything you need to import
(:load "my/cool/lib/part1" "my/cool/lib/part2"))

second file "my/cool/lib/part1.clj":
(in-ns 'my.cool.lib)
....

third file "my/cool/lib/part2.clj":
(in-ns 'my.cool.lib)
....

The top-level "ns" contains all the imports/uses/requires you need for
the namespace, and the (:load ...) controls the order in which the
subsidiary files are loaded. Each of the subsidiary files just does
(in-ns ...), giving them access to everything used/imported in the top-
level "ns".

I forgot whether the arguments to (:load ...) need slashes at the
beginning. Either way, they're inside CLASSPATH.

Ok, that was a lot; I hope some of it made sense.

-Stuart Sierra

Chouser

unread,
Dec 20, 2008, 6:28:06 PM12/20/08
to clo...@googlegroups.com
On Sat, Dec 20, 2008 at 2:47 PM, Christian Vest Hansen
<karma...@gmail.com> wrote:
>
> Q:
>> On Sat, Dec 20, 2008 at 11:25 AM, chris <cnue...@gmail.com> wrote:
>>> I am unclear as to the difference between refer, import use, and
>>> require.
>
> Good material for the FAQ, no?
>
> Or maybe just the docs. Regardless, this is a good explanation.

As far as I can tell, libs aren't really documented anywhere (besides
in the docs strings for 'ns' and the various helper function). If
I've missed it somewhere, please correct me, but It probably belongs
at http://clojure.org/namespaces

--Chouser

Meikel Brandmeyer

unread,
Dec 20, 2008, 6:49:18 PM12/20/08
to clo...@googlegroups.com
Hi,

Am 21.12.2008 um 00:28 schrieb Chouser:
> As far as I can tell, libs aren't really documented anywhere (besides
> in the docs strings for 'ns' and the various helper function). If
> I've missed it somewhere, please correct me, but It probably belongs
> at http://clojure.org/namespaces

There is an empty http://clojure.org/libs at the moment.
So I guess Rich is working on it. On the wiki there is also
a section but it's out dated and no very comprehensive.

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages