This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-08-03
Channels
- # beginners (98)
- # boot (18)
- # chestnut (2)
- # cider (90)
- # cljdoc (3)
- # cljs-dev (1)
- # clojure (64)
- # clojure-dev (14)
- # clojure-dusseldorf (4)
- # clojure-italy (11)
- # clojure-nl (5)
- # clojure-spec (9)
- # clojure-uk (69)
- # clojurescript (63)
- # code-reviews (2)
- # core-logic (20)
- # cursive (13)
- # datomic (52)
- # dirac (2)
- # emacs (4)
- # figwheel (6)
- # hyperfiddle (13)
- # luminus (4)
- # nrepl (1)
- # off-topic (7)
- # onyx (9)
- # overtone (3)
- # parinfer (3)
- # pedestal (1)
- # re-frame (31)
- # reagent (74)
- # reitit (34)
- # rum (3)
- # shadow-cljs (51)
- # spacemacs (22)
- # specter (7)
- # tools-deps (23)
- # uncomplicate (3)
- # vim (9)
so a couple of noob questions...
1. why when rand-nth
is used in-line, it consistently returns the same value? Is clojure caching the result? For example, (def rand-alpha (rand-nth (map char (range 65 91)))
returns the same character everytime. However,
(def rand-alpha (map char (range 65 91)))
(rand-nth rand-alpha) (rand-nth rand-alpha)
Gives the desired results?second question, why does the entire function call need to be dereferenced in the following example?
(defrecord Person [age])
(defn set-age [] (Person. (atom (10)))
(defn get-age [person] @(:age person))
(defn reset-age [person] (reset! (:age person) 12))
I'm not understanding the dereferencing of the @(:age person)
line, and why it doesn't need to be dereferenced in the reset-age
line?Because def
defines a global that is evaluated just once when the ns is loaded @ryan.russell011
So (def rand-alpha ...)
evaluates the ...
once and that's the value of the global rand-alpha
.
For the second Q, the age
in the Person
record is an atom so it needs to be dereferenced to get the value.
(:age person)
returns the value for the key :age
, which is an atom, and the @
reaches into the atom and gets the current value.
Hope that helps @ryan.russell011?
(the first Q indicates why you need to be careful not to do anything with side-effects in a def
)
reset!
is a function that takes an atom as the first argument, not a number, so dereferencing the :age
atom before passing it to reset!
wouldn't be correct
If you wanted to make a rand-alpha
then it would need to be a function, e.g. (defn rand-alpha [] (rand-nth (map char (range 65 91))))
. Then you could do (rand-alpha)
and it'll evaluate that each time.
it does... especially for the def
, I wasn't aware of that. For the Person, I was thinking that it should have been (:name @robot)
, and through playing around with it got it to work. I get what it is doing now.. :name
is a function into robot
which is returning an atom value... so the entire thing needs to be dereferenced.
I am guessing there is very few reasons to use def
and I should just make that defn
instead?
def
is fine for constants (technically they create Var
s and those are still "variable" but it really helps to not think of them that way -- pretend they're constants!).
I will do that. thank you @seancorfield, @sundarj, @james082 as well for your responses!
So you could do (def letters (map char (range 65 91)))
and then (defn rand-alpha [] (rand-nth letters))
. Where letters
is your constant and rand-alpha
is a function that uses that constant.
user=> (def foo 42)
#'user/foo
user=> (* foo 2)
84
user=> (alter-var-root #'foo dec)
41
user=> (* foo 2)
82
user=>
Just to show that def
'd Var
s can be updated... But don't do this (in general) 🙂Technically speaking functions are constants that you just happen to execute. So (defn foo [n] (+ 3 n))
could be done as (def foo (fn [n] (+ 3 n)))
.
Thanks again for the help earlier guys, I got the roman numerals program working: https://gist.github.com/cellularmitosis/567d7fb89b35e1e8cf7743ad5a278b4e
The table might be better stored as an array-map
(def table (array-map 1000 "M" 500 "D" 400 "CD" 100 "C" 50 "L" 40 "XL" 10 "X" 9 "IX" 5 "V" 4 "IV" 1 "I"))
A reduction is more suitable here than using recur
or looping IMHO.
(defn roman [num table]
(reduce
(fn [{:keys [result num]} [amt rom]]
(if (= num 0)
(reduced result)
{:result (apply str result (repeat (quot num amt) rom))
:num (rem num amt)}))
{:result "" :num num}
table))
Destructuring and reduced
are useful things.
quot
and rem
give you quotient and reminder while still being checked math (protects you from overflows, not that you'll encounter many here 😉 )
Because tail recursion is its own reward, here's a recursive version.
(defn roman
([num] (roman "" num (array-map 1000 "M" 500 "D" 400 "CD" 100 "C" 50 "L" 40 "XL" 10 "X" 9 "IX" 5 "V" 4 "IV" 1 "I")))
([num table] (roman "" num table))
([result num table]
(if (or (= num 0) (empty? table)) result
(let [[[amt rom] & next-table] (seq table)]
(recur (apply str result (repeat (quot num amt) rom))
(rem num amt)
next-table)))))
This one also shows off clojure's multi-arity functions.
Because the JVM doesn't eliminate arbitrary tail calls, you'll blow the stack if you call a function recursively. You can use recur
instead.is here someone familiar with yada
and bidi
? i want to add a 404-ressource-not-found page instead of the fiven default 204.
So I was looking at (source defn)
as you do and I saw (fn defn [&form &env name & fdecl] ...
I'm familiar with &
for variadic functions but please can someone tell me what &form
means?
Being a beginner, I checked the Clojure doc, and found this: https://clojure.org/reference/macros
> &form
- the actual form (as data) that is being invoked
And this blog post with a little bit more of explanation: http://blog.jayfields.com/2011/02/clojure-and.html
Secondary question - I'm using (binding [*ns* ...] (defn ...))
and when I eval
in that ns later, my function isn't available. intern
works. Any ideas please? binding
definitely works as I had to (refer-clojure)
this way (why is another mystery)
@alee &form
and &env
are special variables that are available automatically within macros, you're seeing them as explicit arguments to that fn
because defmacro
hasn't been bootstrapped yet by the time defn
is implemented
&form
is bound to the original form of a macro (e.g. if you (defmacro foo [& _] (list 'quote &form))
and you (foo (1 2 (3 4 ))
, you'll get '(my-ns/foo (1 2 (3 4)))
back
&env
is a bit more internal, it's the compiler environment at the time of macroexpansion of that macro
binding
is a runtime construct, defn
is a macro that expands to 2 special forms evaluated at compile time
intern
OTOH is a runtime construct and that's why binding
"works" with intern
and not with def
I guess there's something special about (the first?) ns
form which tells defn where to put stuff?
I did originally use (try (in-ns ..) ... (finally (in-ns ..)))
but I got an exception saying I couldn't set *ns*
outside a binding
and compilation units that are compiled/evaluated afterwards can see the effects of previously evaluated ones
what you're trying to do can be achieved either by binding *ns*
+ using runtime intern
, or by doing the entire thing in a macro
Wow, that was easy: https://github.com/clojure/clojure/blob/master/test/clojure/test_helper.clj#L24
you probably want https://github.com/clojure/clojure/blob/master/test/clojure/test_helper.clj#L32-L37 instead
in your namespace declaration you do:
(ns my-ns.thing
(:require [other-namespace]
[some-other-namespace]
[more-namespace]))
yeah, I want to see from the repl which are required, does the result of all all-ns
shows all that are required or just in the classpath?
when I start a REPL in my project, (all-ns)
lists mostly just clojure.*. after I require a ns, the list returned by all-ns
grows
Hi everyone! I'm trying to use https://github.com/venantius/ultra. I've added it to my ~/.lein/profiles.clj
and am starting a repl with lein repl :connect 6005
.
Lein installed the dependency, but I'm still not seeing any of the features, like syntax highlighting. Is there something else that I still need to do to initialize it?
@juan458 are you using Clojure 1.9? Per the ultra docs, they won't play well together.
is there a reason to prefer defaults being added through destructing the function or being passed in as a map to the function? i suppose the use case might matter...
what does "destructuring the function" mean?
providing an or option when destructing
If you have a function where the options will be passed down to one or more others, perhaps through multiple levels of function calls, and perhaps with the options map augmented or changed down the call tree, it is a bit less code to pass it as a map argument.
Passing the options as [arg1 arg2 & opts] throughout that call tree would require doing (apply child-fn x y opts) in potentially many places.
Hey all! is this a good place to ask for some code review / nitpicking ? I made this for a project at work https://gitlab.com/vise890/parseq and i'd love to get some feedback on ways to make it better / faster
Sure, either here or in #code-reviews
@vise890108 That looks way beyond #beginners code by the way 🙂
haha ok thank you @seancorfield, i'll ask in #code-reviews
Hey all, curious if there is a more idiomatic way of doing this...can't think of it off the top of my head:
(if (or (= x true) (= x nil)) ;; better way to write this or?
something-1
something-2)
If the condition was instead (or (= x false) (= x nil))
, then a more idiomatic way would be (not x)
. But there isn't anything built into Clojure that represents the combination of conditions you have.
Some people might write (or (true? x) (nil? x))
instead of what you have, but I don't see it as being any clearer.
The only other variation I can think of is (contains? #{true nil} x)
but I think that is less clear.
yeah, I think the last one comes closest to what I was thinking, but all of these suggestions are good to know.
I think the if or version you have is the most clear of the conditions that trigger something-1. I would leave it there
note that when something like this is hard, it may indicate that you should re-think whether it was a good idea to get to the point where you need to check those two things in the first place
why is nil special? can you make it not be and/or avoid having it as a possibility?
(not looking for an actual answer, but these are the questions I would ask myself)
For sure. I am running through those questions myself. This is a front end component, so right now I am running through edge cases - trying to handle defaults in a sane way.
with interface option type logic, you probably want (def default-options {:easy true})
instead of having code that looks like (if (or (= (:easy opts) true) (= (:easy opts) nil)) ...)
(the piece in the middle is merging the options explicitly set onto the defaults)
Hi are hyphens (`-`) allowed in project names/namespaces? I created a rum project using this command lein new shadow-cljs rum-workshop +rum
and couldn’t get the app to load. Then I created another project, lein new shadow-cljs rumworkshop +rum
and it worked.
- is allowed in the name of a namespace, but the file will have _ instead of - in the file name
what does "couldn't get the app to load" entail - can you give some detail of what went wrong?
Thanks @noisesmithfor responding. When I launch the app with command shadow-cljs watch app
the app did not mounted on the div.
It remained as the index.html.
no error messages?
Right I saw a message [Show/hide message details.] ReferenceError: workshop is not defined
this might be some bug in the shadow-cljs template, clojure definitely accepts names like this
(I mean, even shadow-cljs itself has a hyphen in the name...)
Yeah you’re right hahaha. I’ll submit an issue for the shadow-cljs. Thanks so much for replying!