Fork me on GitHub
#beginners
<
2015-07-26
>
lucien.knechtli03:07:30

if you do (alter-var-root foo #(* % 2)), it will apply the function #(* % 2) to the value of foo and set foo to that value. In this case, if foo is 2 before, it will be 4 after. set! can only be used for global vars, and will evaluate the expression, rather than applying it. (set! global-var #(* % 2)) will actually change the definition of global-var to the anonymous function: (global-var 2) will evaluate to 4 after the set!.

meow11:07:14

@lucien.knechtli: right, I understand all of that. What I wanted to know is if the need is to change a global var and the new value can simply be applied, then either set! or alter-var-root can do the job, but is there any difference in choosing one over the other? I've seen both used in the wild.

meow11:07:46

The official docs say this about set!:

When the first operand is a symbol, it must resolve to a global var. The value of the var's current thread binding is set to the value of expr. Currently, it is an error to attempt to set the root binding of a var using set!, i.e. var assignments are thread-local.

lucien.knechtli11:07:35

I think that the main difference in this case is that alter-var-root is an atomic operation. Set! Doesn't claim to be.

meow11:07:44

I thought that at first as well, but I think set! is also atomic but it has to do with threading.

meow11:07:46

using set! on a global var does not set the root binding of that var, instead it alters the current thread-local binding of that var

lucien.knechtli11:07:18

Vars provide a mechanism to refer to a mutable storage location that can be dynamically rebound (to a new storage location) on a per-thread basis. Every Var can (but needn't) have a root binding, which is a binding that is shared by all threads that do not have a per-thread binding. Thus, the value of a Var is the value of its per-thread binding, or, if it is not bound in the thread requesting the value, the value of the root binding, if any.

lucien.knechtli11:07:28

Copied from docs

lucien.knechtli11:07:54

I think that yes, set! Would only affect the current thread

meow12:07:43

I wonder why alter-var-root doesn't have a bang suffix: alter-var-root!?

lucien.knechtli15:07:01

you'd think it would, wouldn't you?

rauh18:07:28

@meow: set! is thread local, alter-var-root is not.

ska18:07:02

Hi. Does anybody have a lot of experience with clojure.java.jdbc? I tried to use a prepared update statement and it took me some hours to find out, how to actually use it and that I have to explicitly have to pass true for transaction? which is an optional parameter and only checked for String

ska18:07:43

Would a PR be interesting that improves checking for the type of call?

ska18:07:26

Oh, hang on, that is an official clojure project. So, no PR but Jira etc...

ska18:07:26

Ok, raised an issue in Jira. Just as a side-note: clojure.java.jdbc works with DB2, too. At least I didn't encounter problems so far.

coyotespike19:07:55

Vars, atoms, and macros Suppose I have a var, bound to a reaction which updates when another reaction changes. (A subscription in the re-frame design pattern is just a reaction, which is to say an atom).

coyotespike19:07:11

(def total-days-selected (reaction (days-between @(subscribe [:date-moved-out]) today)))

coyotespike19:07:25

This works correctly - I can use the var total-days-selected elsewhere, and it updates based on the most current value of subscribe [:date-moved-out] (which, remember, is just a reaction).

coyotespike19:07:49

However, suppose I put the subscription in a var: (def start-date (subscribe [:date-moved-out])).

coyotespike19:07:05

Now, when I use this var, like so: (def total-days-selected (reaction (days-between @start-date today)))

coyotespike19:07:13

then I find that total-days-selected does not update properly.

coyotespike19:07:58

I've had this problem elsewhere. In general, it seems that binding an atom in a var, and then using that var in other vars, means that the client vars won't have access to the right value of the atom in the var.

coyotespike19:07:47

1. Does wrapping an atom in a var, and then wrapping that var in other vars, evaluate the atom's value right then and "store" it?

coyotespike19:07:02

In other words, does using an atom in this way "freeze" its value?

coyotespike19:07:54

This problem also occurs when in let statements, by the way.

coyotespike19:07:28

2. How do I get the atom's value at runtime? Use macros? So far I am just learning to avoid vars.

meow19:07:57

@coyotespike: I haven't done that much with re-frame, but from what I remember I think you've got a few things mixed up here.

meow19:07:29

I don't think a reaction is an atom.

meow19:07:24

You get the value of an atom by dereferencing it, usually using @.

meow19:07:30

Reagent has a variation of an atom, a reactive atom or ratom.

coyotespike19:07:12

I feel pretty mixed up, and any clarification is welcome 😉

coyotespike19:07:37

I believe a reaction is an atom, which updates its value upon another atom's change in value.

coyotespike19:07:32

My broader question is just how atoms are used. If I put in an atom in a var, then dereference that var inside another var, then update the original atom, the second var doesn't see that change.

coyotespike19:07:48

The second var, the one relying on the atom var, can be bound through a let or a def - either way, it doesn't see the change to the atom var.

surreal.analysis19:07:24

You should be able to bind the atom from (subscribe) via a let - that’s the encouraged way throughout re-frame’s documentation

meow19:07:55

A reaction is not an atom.

meow19:07:00

The 2nd building block, reaction, acts a bit like a function. It's a macro which wraps some computation (a block of code) and returns a ratom holding the result of that computation.

The magic thing about a reaction is that the computation it wraps will be automatically re-run whenever 'its inputs' change, producing a new output (return) value.

coyotespike19:07:53

@meow Thanks for that - so the result of a reaction is a ratom, but it is not itself a ratom.

meow19:07:03

I believe a reaction is more like a watcher on an atom.

meow19:07:37

@coyotespike: if you a new to clojure and re-frame then there are a lot of concepts and terminology at play

meow19:07:50

it gets mighty confusing

coyotespike19:07:50

I'm coming up with a better example of my question about vars and atoms...

meow19:07:13

atom is just a special var in a way

coyotespike20:07:01

Okay, here we go...although I think I understand slightly better already.

coyotespike20:07:37

(def my-atom (atom 2))

coyotespike20:07:58

(def atom-client (+ 1 @my-atom))

coyotespike20:07:12

(swap! my-atom 3)

coyotespike20:07:43

atom-client still returns 3, not 4.

coyotespike20:07:31

Because otherwise atom-client wouldn't be immutable...

meow20:07:51

@coyotespike: yes, but not for that reason

Alex Miller (Clojure team)20:07:59

swap! applies a fn- that code is bad

meow20:07:00

what do you do in line 2?

meow20:07:18

and what alex said

meow20:07:33

(def foo (atom 42))
=> #'play.core/foo
foo
=> #object[clojure.lang.Atom 0x1a59d8b {:status :ready, :val 42}]
@foo
=> 42
(swap! foo inc)
=> 43
@foo
=> 43

coyotespike20:07:15

@alexmiller - you're right, I corrected that in REPL, but not before pasting original.

coyotespike20:07:08

Okay - I want atom-client's value to change when my-atom's value changes.

meow20:07:40

you understand that @ is the same as (deref ...) and when you deref an atom you get what?

meow20:07:14

right, and that value is now independent of its origin

meow20:07:35

doesn't know, doesn't care, from where it came

coyotespike20:07:41

That makes so much sense it seems painfully obvious now...just wasn't obvious when I was debugging 😉

coyotespike20:07:42

So I could make atom-client a function, which takes an atom as argument

Alex Miller (Clojure team)20:07:50

Generally it's best to write the vast majority of your functions as pure data transformations from value to value

meow20:07:52

with an atom, or reagent/atom, or any of the "reactive" atoms you just have to be aware of when you need to deref the atom to get its value and when deref'ing is actually a mistake because then you lose the "magic" auto-updating that reagent and re-frame and such add onto the atom semantics

Alex Miller (Clojure team)20:07:19

And then have a thin layer at the top that applies those functions to your stateful reference

coyotespike20:07:41

@meow Nicely put - helps place what I've wound up doing in context

coyotespike20:07:02

@alexmiller - so I should beware of functions which depend on atoms?

Alex Miller (Clojure team)20:07:49

I think you should strive to minimize the number of functions that depend on state

coyotespike20:07:43

@alexmiller - writing that down, and will contemplate where/how to apply that as a general pattern.

meow20:07:39

while alexmiller's advice is correct, in re-frame you are dealing with state a lot and some of that involves functions that pass around atoms/ratoms and so you do need to understand the difference between passing an atom vs. passing the value of the atom gotten via deref

coyotespike20:07:25

@meow - yeah, definitely, and I feel better equipped to do that now

coyotespike20:07:32

Many thanks to you both. After a couple debugging sessions I figured out that I was missing something fundamental about using atoms and vars at runtime.

coyotespike20:07:39

It feels good to have sifted through that confusion.

meow20:07:06

@coyotespike: cool. There are also #C073DKH9P and #C0620C0C8 channels for followup. simple_smile

coyotespike20:07:42

I love those channels...like the Clojure community in general, they're very helpful. simple_smile

roberto21:07:43

Is there style guide for writing doc strings?

coyotespike22:07:55

I haven't found an official one. I believe they should come before rather than after the parameters, though they're allowed to go either way.

coyotespike22:07:01

My statement that they're "allowed" to go either way is not quite accurate. See weavejester's comments here: https://news.ycombinator.com/item?id=5819487