beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2020-11-25T00:00:22.370700Z

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
??

dgb23 2020-11-25T00:01:20.370900Z

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?

dgb23 2020-11-25T00:01:32.371100Z

Also thank you for the response

2020-11-25T00:02:48.371300Z

because float is a concrete type with less precision, you might want the double function instead

2020-11-25T00:03:30.371500Z

in the jvm, Float / float are IEEE floats with 32 bits, Double/ double are 64 bits

2020-11-25T00:03:55.371700Z

see also "int", which is 32 bits, and "long", which is an integer value with 64 bits

2020-11-25T00:04:27.371900Z

ah ok, that makes sense. Thank you for help.

seancorfield 2020-11-25T00:06:52.372100Z

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...

👍 1
seancorfield 2020-11-25T00:08:20.372300Z

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.

👍 1
seancorfield 2020-11-25T00:09:30.372700Z

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.

dgb23 2020-11-25T00:10:18.372900Z

Thank you for the clarifications. I didn’t think about the incidental complexity my workaround implies!

seancorfield 2020-11-25T00:14:56.373100Z

Mostly we've just avoided using Spec in those (few) cases where we would otherwise need to build them dynamically after app startup.

dgb23 2020-11-25T00:24:15.373300Z

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.

voortuck 2020-11-25T06:28:53.375900Z

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))

2020-11-25T06:46:21.376700Z

In the instructions that contain that expression, several lines above is this one: (use 'etaoin.api). Did you perhaps skip that?

2020-11-25T06:48:00.378Z

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

voortuck 2020-11-25T07:45:35.379300Z

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

voortuck 2020-11-25T07:45:43.379500Z

I appreciate your help.

Aviv Kotek 2020-11-25T10:04:40.380500Z

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?

Aviv Kotek 2020-11-26T08:48:23.436Z

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?

2020-11-26T16:31:39.450500Z

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

Aviv Kotek 2020-11-29T08:54:25.019600Z

cool, ty

Yosevu Kilonzo 2020-11-25T12:27:54.385200Z

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."))

Yosevu Kilonzo 2020-11-26T12:50:11.441100Z

Makes sense about relying on short circuit for simplicity especially with a few simple conditions Alex.

Yosevu Kilonzo 2020-11-26T12:52:59.444300Z

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.

👍 1
Yosevu Kilonzo 2020-11-25T12:29:33.385300Z

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.”

alexmiller 2020-11-25T14:21:48.386900Z

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

neilyio 2020-11-25T15:21:13.392300Z

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.

neilyio 2020-11-25T15:22:19.393900Z

I only need to use a single NPM package, is there a manual / hacky workaround with externs and the Closure Compiler?

neilyio 2020-11-25T15:24:23.395Z

The package is react-spring, I can’t find it on CLJSJS.

dgb23 2020-11-25T15:46:45.395100Z

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.

2020-11-25T16:18:39.395500Z

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)

🙏 1
2020-11-25T16:19:43.395700Z

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.

st3fan 2020-11-25T19:16:10.396800Z

@seancorfield i look forward to your REPL presentation next month

seancorfield 2020-11-25T19:31:40.398100Z

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!

👍 4
st3fan 2020-11-25T19:31:53.398300Z

Just show us the magic 🙂

👍 3
zach 2020-11-25T21:05:02.401500Z

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”.

zach 2020-11-25T21:06:31.402900Z

For example, a fn “text->env-map” which takes text like CLOJURE=cool\nREPL=awesome\n and converts it to ({"CLOJURE" "cool"}{"REPL" "awesome"})

zach 2020-11-25T21:07:12.403500Z

I have it written, and working, as so:

(defn text->env-map
  [text]
  (map #(conj {} %)
        (map #(clojure.string/split % #"=")
             (clojure.string/split-lines text))))

2020-11-25T21:07:47.404500Z

You can use a transducer here, or threading

2020-11-25T21:07:52.404800Z

for all f,g (map f (map g x)) can be replaced with (map (comp f g) x)

zach 2020-11-25T21:07:55.404900Z

But i am still pretty new to clojure, and am not sure if i’m taking too blunt of an approach.

zach 2020-11-25T21:08:02.405200Z

it’s just feeling increasingly unreadable.

2020-11-25T21:08:59.406800Z

so in your case (comp #(conj {} %) #(string/split % #"=")) or just #(conj {} (string/split % #"="))

zach 2020-11-25T21:09:39.407500Z

Ah! I had not used comp before, that’s really nice!

2020-11-25T21:10:11.408Z

Check out the threading macros for making deeply nested chains of calls like that more readable, too: https://clojure.org/guides/threading_macros

2020-11-25T21:11:02.409100Z

(->> x (map f) (map g)) is worse than comp IMHO

2020-11-25T21:11:10.409400Z

(though I see it more often)

zach 2020-11-25T21:11:24.409600Z

Good point. I’ve used threading macros, but maps trip me up because of the order of the variable being put in.

zach 2020-11-25T21:12:07.410700Z

(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)

2020-11-25T21:12:08.410800Z

Agree in this specific case, comp is a better way to go

phronmophobic 2020-11-25T21:12:35.411500Z

I still prefer the threaded version. for me, it's easier to read and extend

zach 2020-11-25T21:12:47.411900Z

Thank you!

2020-11-25T21:12:54.412100Z

There is also ->> to put the thread in the last position

2020-11-25T21:13:05.412600Z

and some-> to short circuit in case a nil happens

👍 1
2020-11-25T21:13:20.412900Z

Yeah, there are two ‘styles’ of threading operators, thread-first -> and thread-last ->>, the latter works great with collections sequences 🙂

➕ 2
2020-11-25T21:19:48.415200Z

I like comp more for named functions: (comp foo bar) reads nice and clean. With literal functions, I may go with -> and multiple maps for readability. Puts each unit of work on different lines.

2020-11-25T21:20:38.415600Z

I suppose you can do that with comp, too

2020-11-25T21:20:48.416Z

or just do that inside the function literal

2020-11-25T21:20:52.416200Z

yeah, or that

2020-11-25T21:21:28.416800Z

#(-> % (string/split #"=") (->> (conj {})))

2020-11-25T21:21:58.417100Z

OK never mind - the nesting works but it's dumb

😄 1
zach 2020-11-25T21:24:44.418700Z

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.

2020-11-25T21:27:22.419100Z

It really depends on what exactly you’re doing.. judgement call which is better in a particular case

2020-11-25T21:27:59.420Z

Over-testing is as bad as under-testing IMO

2020-11-25T21:28:26.420500Z

The natural question is then what’s the right balance? Well.. hard to answer in a general way.

2020-11-25T21:29:08.421200Z

Haha yeah, correct lingo is important here, thanks for the correction 🙂!

2020-11-25T21:30:56.423500Z

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.

phronmophobic 2020-11-25T21:33:08.423800Z

don't map ,`filter`, etc. take both collections and sequences? don't conj, nth,etc. also take both collections and sequences?

seancorfield 2020-11-25T21:45:06.424Z

@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

phronmophobic 2020-11-25T21:48:16.424200Z

so is mapv a sequence function or a collection function?

seancorfield 2020-11-25T21:51:26.424400Z

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 [].

👍 1
seancorfield 2020-11-25T21:52:17.424600Z

The single sequence argument version is an optimization that reduce's it into a (transient) [] via conj!

seancorfield 2020-11-25T21:54:04.425Z

The distinction isn't 100% consistent but it's a useful rule of thumb in most cases.

zach 2020-11-25T21:54:25.425400Z

hahaha, yah my scenario is a bit less mission critical.

phronmophobic 2020-11-25T21:55:02.425500Z

:thumbsup: , makes sense

seancorfield 2020-11-25T21:56:45.425700Z

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! 🙂 ).

phronmophobic 2020-11-25T21:57:45.425900Z

> conj preserves the type of its first argument with small caveats. I believe maps might change classes, but not the "abstract" type

phronmophobic 2020-11-25T21:59:22.426100Z

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)

seancorfield 2020-11-25T21:59:51.426400Z

"type" is such a fuzzy concept 🙂

phronmophobic 2020-11-25T22:00:30.426600Z

depends on what the definition of is is

seancorfield 2020-11-25T22:01:00.426800Z

(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)

😁 1