Nubie Question

124 views
Skip to first unread message

WoodHacker

unread,
Mar 23, 2010, 9:35:07 AM3/23/10
to Clojure
I understand how conj works. But how do you add a value to a
persistent vector? You have to add the new item to the vector with
(conj vector item), but how do you assign the return value to the
persistent vector. So far I have it working with a def -- (def
vector (conj vector item)) -- but I'm not sure this is 'pure'
Clojure. Is there a better way, functionally, or am I there?
Functional programming is the best way to go, but very few programs
can exist without mutable persistent data.

Bill

David Nolen

unread,
Mar 23, 2010, 9:46:13 AM3/23/10
to clo...@googlegroups.com

conj _does_ let you add a value to a persistent vector. The question is how do you save this value for future use. Don't use def. Use a mutable reference type. One way:

(def my-vector (atom []))
(swap! my-vector conj 'foo)
@my-vector ; [foo]

Depending on your use case agents or refs might be better suited.

David

Per Vognsen

unread,
Mar 23, 2010, 9:57:11 AM3/23/10
to clo...@googlegroups.com
By definition, persistent data structures are never mutable. But there
are various kinds of mutable references (vars, refs, atoms, agents)
that can _refer_ to persistent (hence unchanging) data structures.

While David has given you an answer to your immediate query, I would
ask you to step back and consider whether you're sure you really need
references. You haven't supplied enough context for us to make that
call.

-Per

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>
> To unsubscribe from this group, send email to clojure+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
>

WoodHacker

unread,
Mar 24, 2010, 5:43:49 PM3/24/10
to Clojure
Actually, swap! doesn't seem to work in my case. I should state
what I'm
trying to do. I'm writing a graphics editing program where I want
the user
to be able to choose and save color values. I start out with a
vector
containing blank and white. When the user selects a new color and
wants
to save it I add the new color to the vector.

(def savedColors [black, white])
.....
(defn saveColor [color panel]
(swap! savedColors (conj savedColors color)) <-- this does not
work
..... also show the colors in the editor
))

I get the following error:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException:
clojure.lang.PersistentVector cannot be cast to clojure.lang.Atom

I assume I'm using swap! correctly, but may not be.

Bill

On Mar 23, 9:57 am, Per Vognsen <per.vogn...@gmail.com> wrote:
> By definition, persistent data structures are never mutable. But there
> are various kinds of mutable references (vars, refs, atoms, agents)
> that can _refer_ to persistent (hence unchanging) data structures.
>
> While David has given you an answer to your immediate query, I would
> ask you to step back and consider whether you're sure you really need
> references. You haven't supplied enough context for us to make that
> call.
>
> -Per
>

> On Tue, Mar 23, 2010 at 8:35 PM, WoodHacker <ramsa...@comcast.net> wrote:
> > I understand howconjworks.    But how do you add a value to a


> > persistent vector?    You have to add the new item to the vector with

> > (conjvector item), but how do you assign the return value to the


> > persistent vector.   So far I have it working with a def  -- (def

> > vector (conjvector item)) -- but I'm not sure this is 'pure'

David Nolen

unread,
Mar 24, 2010, 5:51:49 PM3/24/10
to clo...@googlegroups.com
On Wed, Mar 24, 2010 at 5:43 PM, WoodHacker <rams...@comcast.net> wrote:
Actually, swap! doesn't seem to work in my case.    I should state
what I'm
trying to do.    I'm writing a graphics editing program where I want
the user
to be able to choose and save color values.   I start out with a
vector
containing blank and white.  When the user selects a new color and
wants
to save it I add the new color to the vector.

(def savedColors [black, white])
.....
(defn saveColor [color panel]
 (swap! savedColors  (conj savedColors color))   <-- this does not

What you want is:

(swap! savedColors conj color)

David

Brendan Ribera

unread,
Mar 24, 2010, 5:53:59 PM3/24/10
to clo...@googlegroups.com
Also, you need to use swap! on an atom, so you'll need to make savedColors one. Read here: http://clojure.org/atoms

--

Meikel Brandmeyer

unread,
Mar 24, 2010, 6:04:39 PM3/24/10
to clo...@googlegroups.com
Hi,

On Wed, Mar 24, 2010 at 02:43:49PM -0700, WoodHacker wrote:
> (def savedColors [black, white])

(def savedColors (atom [black white]))

> Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException:
> clojure.lang.PersistentVector cannot be cast to clojure.lang.Atom

This message should be pretty obvious, no?

Sincerely
Meikel

WoodHacker

unread,
Mar 25, 2010, 7:52:55 AM3/25/10
to Clojure
I thank all the people who have sent me solutions to my Conj
problem. Unfortunately, none of them seem to work.

The issue is adding a value to a defined vector - (def savedColors
[black, white])

One solution was given as: (swap! savedColors conj newcolor)

This produces still the following runtime error:

Exception in thread "AWT-EventQueue-0"
java.lang.ClassCastException: clojure.lang.PersistentVector cannot
be cast to clojure.lang.Atom

Another solution was to make savedColors an atom - (def savedColors
(atom [black, white]))

This produces a new compile error:

Exception in thread "AWT-EventQueue-0"
java.lang.IllegalArgumentException: Don't know how to create ISeq
from: clojure.lang.Atom

Changing savedColors to a list instead of a vector gets the same
error. The atom example on the web is for a map.

I'm sure this can be done. So far I just don't know how.

Bill

Per Vognsen

unread,
Mar 25, 2010, 8:05:23 AM3/25/10
to clo...@googlegroups.com
On Thu, Mar 25, 2010 at 6:52 PM, WoodHacker <rams...@comcast.net> wrote:
> I thank all the people who have sent me solutions to my Conj
> problem.   Unfortunately, none of them seem to work.
>
> The issue is adding a value to a defined vector - (def savedColors
> [black, white])
>
> One solution was given as:  (swap! savedColors conj newcolor)
>
> This produces still the following runtime error:
>
> Exception in thread "AWT-EventQueue-0"
> java.lang.ClassCastException:     clojure.lang.PersistentVector cannot
> be cast to clojure.lang.Atom
>
> Another solution was to make savedColors an atom - (def savedColors
> (atom [black, white]))
>
> This produces a new compile error:
>
> Exception in thread "AWT-EventQueue-0"
> java.lang.IllegalArgumentException: Don't know how to create ISeq
> from: clojure.lang.Atom

The def cannot produce that error. You probably mean that it
originates with some reference to savedColors that is expecting a
sequence. You must explicitly dereference savedColors by prefixing it
with @ to get at the contained value:

user> (def saved-colors (atom [1, 2]))
#'user/saved-colors
user> (doseq [x @saved-colors] (println "Saved color:" x))
Saved color: 1
Saved color: 2
nil

-Per

Mark J. Reed

unread,
Mar 25, 2010, 8:53:08 AM3/25/10
to clo...@googlegroups.com
Right. Let's make this clear: outside of the Java interoperability
stuff, you cannot change the value of a variable in Clojure. Ever.
All the data types are immutable; you can only build new values on top
of existing ones, not modify the old ones.

When you conj something onto a vector, it doesn't change that vector;
it returns a new vector. The new vector reuses the old one's memory
for efficiency, but if you look at the old one it doesn't have the new
member. It's unchanged.

What can change are references. So you can make a reference to the
vector, and then build a new vector with the new items, and then
change the reference to point to the new vector. That's what (swap!)
does.

But you have to have a reference to start with. Which (atom) gives
you. But a reference is not the same as a vector; you can't use it
directly when you need a vector, but must dereference it with @.

Example:

Clojure 1.1.0
user=> (def start-colors [:black :white])
#'user/start-colors
user=> (def saved-colors (atom start-colors))
#'user/saved-colors
user=> start-colors
[:black :white]
user=> @saved-colors
[:black :white]
user=> (swap! saved-colors conj :red)
[:black :white :red]
user=> start-colors
[:black :white]
user=> saved-colors
#<Atom@1d256fa: [:black :white :red]>
user=> @saved-colors
[:black :white :red]
user=>
--
Mark J. Reed <mark...@gmail.com>

Reply all
Reply to author
Forward
0 new messages