thanks, another question if I may. Why does (float) reduce precision?
(let [op1 (Float/parseFloat "4.42")
op2 (Float/parseFloat "5.55")]
(+ op1 op2))
=> 9.970000267028809
(let [op1 (Float/parseFloat "4.42")
op2 (Float/parseFloat "5.55")]
(float (+ op1 op2)))
=> 9.97
??why would you say it is hard? wouldn’t it simply mean to lets say create a function that binds the values and registers a spec? I tried that in a REPL and it seemed to work. Am I missing something?
Also thank you for the response
because float
is a concrete type with less precision, you might want the double
function instead
in the jvm, Float / float are IEEE floats with 32 bits, Double/ double are 64 bits
see also "int", which is 32 bits, and "long", which is an integer value with 64 bits
ah ok, that makes sense. Thank you for help.
Spec 1 is nearly all macros and isn't designed for parameterized specs. You can't just write top-level Specs like that. You've found a workaround, but you've also now got to make sure that your function is called early enough in your app setup process to happen before anything that needs the spec -- and any Specs that depend on those specs also have to be registered dynamically, and that's going to start to affect your ability to do instrument
in tests and so on...
Spec 2 draws a clearer line between spec forms and spec objects and provides facilities for constructing specs dynamically directly. You'll still have some of the ordering issues to deal with, but it's much easier to build and use dynamic specs.
We maintained a Spec 2 compatible branch of our codebase at work for several months, but it's very buggy and it's still changing a lot -- Rich is apparently going to redesign function specs fairly substantially at some point -- so we gave up after a while.
Thank you for the clarifications. I didn’t think about the incidental complexity my workaround implies!
Mostly we've just avoided using Spec in those (few) cases where we would otherwise need to build them dynamically after app startup.
My instinct is that using something like integrant would maybe help to structure this. But I’m working on a small one man thing so I can afford it for now.
does anyone have an example of using etaoin? https://github.com/igrishaev/etaoin
I've tried lein new app etaoinexample
and then adding the dependency.
:dependencies [[org.clojure/clojure "1.10.1"]
[etaoin "0.4.1"]]
but I get this error:
Unable to resolve symbol: firefox in this context
where I try to call this in core.clj: (def driver (firefox))
In the instructions that contain that expression, several lines above is this one: (use 'etaoin.api)
. Did you perhaps skip that?
It might be preferable to create a namespace where you write your code, e.g. (ns my.project.namespace (:require [etaoin.api :as api]))
and then you can use api/firefox
instead of just firefox
this helps me, thanks. It's one of the hardest parts of clojure to me: trying to figure out the ceremony of :require I'm not sure where I'm missing the explanation in the docs
https://8thlight.com/blog/colin-jones/2010/12/05/clojure-libs-and-namespaces-require-use-import-and-ns.html this article might be handy
I appreciate your help.
with lein
, there is :dev and :test profiles, it's https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#task-specific-profiles not to use :test profile, should the :dev profile be used for test purposes? i.e include test-lib dep, etc? then "lein with-profiles dev test"
? I find it more intuitive just to run "lein test"
(which merges :test profile if declared), what are the differences between :dev and :test profiles? which should I pick?
there is one disadvantage of "test" not able to run in REPL (running tests), so in conclusion- it's common to use the :test profile for tests?
the :test
profile is always used for the :test
task, if you want to run tests in your repl, you might want to add with-profile +test
to your repl startup
cool, ty
Which way of writing conditions is preferable in Clojure, in general? Should I rely on implicit, imperative order or explicit, declarative order?
;; Implicit, imperative order dependency. The most specific condition must be first.
(defn deaf-grandma [s]
(cond
(and (question? s) (upper-case? s)) "Calm down, I know what I'm doing!" ; Must be first!
(question? s) "Sure."
(upper-case? s) "Whoa, chill out!"
(str/blank? s) "Fine. Be that way!"
:else "Whatever."))
;; Explicit, declarative order. Conditions can be in any order.
(defn deaf-grandma [s]
(cond
(and (question? s) (not (upper-case? s))) "Sure."
(and (upper-case? s) (not (question? s))) "Whoa, chill out!"
(and (question? s) (upper-case? s)) "Calm down, I know what I'm doing!"
(str/blank? s) "Fine. Be that way!"
:else "Whatever."))
Makes sense about relying on short circuit for simplicity especially with a few simple conditions Alex.
Thanks for the counter example dgb23. I agree it shouldn’t be black and white. So you made the conscious choice not to rely on the order of the conditions in your case because of their complexity AND the nature of the problem/app.
My concern with order is inspired by this passage from Out of the Tar Pit by Ben Mosely: > “The difficulty is that when control is an implicit part of the language (as it almost always is), then every single piece of program must be understood in that context — even when (as is often the case) the programmer may wish to say nothing about this. When a programmer is forced (through use of a language with implicit control flow) to specify the control, he or she is being forced to specify an aspect of how the system should work rather than simply what is desired.”
cond short circuits and I think most people explicitly rely on that to simplify cond branches, so that would be the former. In this case the scope of the order is encapsulated in 5 lines so it's not a load
What are my options when using NPM packages with Figwheel? I don’t have shadow-cljs as an option anymore, and figwheel’s NPM guide constantly leads to compilation errors for me.
I only need to use a single NPM package, is there a manual / hacky workaround with externs and the Closure Compiler?
The package is react-spring, I can’t find it on CLJSJS.
I had a recent case where I used the explicit branching method (not in Clojure but the principle applies), because the code in question was rather complex and important for the overall consistency of the application. I also annotated all the branches with sufficient comments to understand them in isolation. My takeaway: It depends.
lein test
should already merge in the dev
profile IIRC, and with-profile foo
is almost always an error (it replaces the profile for the task), you usually want with-profile +foo
(it merges new data to the profile for the task)
Each profile should be dedicated to some task, and you might want a supplimental profile to enable integration as opposed to just unit tests for example. You shouldn't need to manipulate profiles for normal tasks.
@seancorfield i look forward to your REPL presentation next month
Thanks @st3fan -- if there's anything specific you'd like to see/hear about, LMK via DM and I'll see whether I can incorporate it. I want it to be an interactive talk/demo so folks can get as much out of it as possible!
Just show us the magic 🙂
Hi! I’m not sure if this is an appropriate question for the channel, but was wondering if y’all had advice on “code smells”, or moving toward more idiomatic clojure. I have a couple functions that map a map of a map, and the repetition is making me feel like i’m doing something incorrect…but am not quite sure how to best phrase the incorrectness beyond “doesn’t feel clojurey”.
For example, a fn “text->env-map” which takes text like CLOJURE=cool\nREPL=awesome\n
and converts it to ({"CLOJURE" "cool"}{"REPL" "awesome"})
I have it written, and working, as so:
(defn text->env-map
[text]
(map #(conj {} %)
(map #(clojure.string/split % #"=")
(clojure.string/split-lines text))))
You can use a transducer here, or threading
for all f,g (map f (map g x)) can be replaced with (map (comp f g) x)
But i am still pretty new to clojure, and am not sure if i’m taking too blunt of an approach.
it’s just feeling increasingly unreadable.
so in your case (comp #(conj {} %) #(string/split % #"="))
or just #(conj {} (string/split % #"="))
Ah! I had not used comp before, that’s really nice!
Check out the threading macros for making deeply nested chains of calls like that more readable, too: https://clojure.org/guides/threading_macros
(->> x (map f) (map g))
is worse than comp IMHO
(though I see it more often)
Good point. I’ve used threading macros, but maps trip me up because of the order of the variable being put in.
(e.g. i have a set of functions where (-> ) works great, as the second position is perfect, but then i want to pass that to a map, and it now needs to be the last position)
Agree in this specific case, comp is a better way to go
I still prefer the threaded version. for me, it's easier to read and extend
Thank you!
There is also ->>
to put the thread in the last position
and some->
to short circuit in case a nil happens
Yeah, there are two ‘styles’ of threading operators, thread-first ->
and thread-last ->>
, the latter works great with collections sequences 🙂
I like comp
more for named functions: (comp foo bar)
reads nice and clean. With literal functions, I may go with ->
and multiple map
s for readability. Puts each unit of work on different lines.
I suppose you can do that with comp, too
or just do that inside the function literal
yeah, or that
#(-> % (string/split #"=") (->> (conj {})))
OK never mind - the nesting works but it's dumb
comp plus named functions seems to perhaps lend itself more nicely to spec and testing too? Or at least it’s encouraging a number of small, easy to reason about functions.
It really depends on what exactly you’re doing.. judgement call which is better in a particular case
Over-testing is as bad as under-testing IMO
The natural question is then what’s the right balance? Well.. hard to answer in a general way.
Haha yeah, correct lingo is important here, thanks for the correction 🙂!
Adding ever more tests is diminishing returns, after some point. Each unit of resources to add a new test prevents fewer bugs. Bugs are, by definition, problems nobody thought of. Problems for which there was no test case. There are plenty of code bases with a million micro-unit tests for every (+ 2 2)
level function, which still have large bugs anyway.
It depends a lot on how much damage happens if there’s a failure. If you are launching a Space Shuttle or doing life-critical code like building a pacemaker, you should probably go overboard on testing every possible code path. Most commercial software has much less harsh failure modes, though.
don't map
,`filter`, etc. take both collections and sequences?
don't conj
, nth
,etc. also take both collections and sequences?
@smith.adriane map
, filter
etc call seq
on their argument and return sequences -- and are considered "sequence functions"; conj
preserves the type of its first argument (as do several collection functions). Per https://clojure.org/guides/faq#_collections_sequences_and_transducers
so is mapv
a sequence function or a collection function?
Technically still a sequence function. It can take multiple sequence arguments and in that case calls map
on them, and then pours the result into
a []
.
The single sequence argument version is an optimization that reduce
's it into a (transient) []
via conj!
The distinction isn't 100% consistent but it's a useful rule of thumb in most cases.
hahaha, yah my scenario is a bit less mission critical.
:thumbsup: , makes sense
It's also a useful guideline when thinking about your own functions (one I often used to violate, and then curse my younger self for when doing maintenance on said code some months or years later! 🙂 ).
> conj
preserves the type of its first argument
with small caveats. I believe maps might change classes, but not the "abstract" type
it always amazing how human made categorizations become fuzzy very quickly as soon as you leave mathematics (or maybe even while still in mathematics, IANAM)
"type" is such a fuzzy concept 🙂
depends on what the definition of is is
(I am a mathematician by training and did a bunch of category theory etc -- but I mostly have to "forget" that when programming, except occasionally)