This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-11-25
Channels
- # architecture (12)
- # asami (6)
- # aws (2)
- # babashka (2)
- # beginners (71)
- # bristol-clojurians (1)
- # calva (1)
- # cider (2)
- # clojure (136)
- # clojure-australia (6)
- # clojure-dev (14)
- # clojure-europe (11)
- # clojure-italy (3)
- # clojure-nl (2)
- # clojure-sanfrancisco (29)
- # clojure-uk (9)
- # clojuredesign-podcast (12)
- # clojurescript (23)
- # code-reviews (7)
- # core-logic (5)
- # cryogen (7)
- # datomic (7)
- # depstar (7)
- # events (3)
- # fulcro (11)
- # helix (1)
- # jobs (2)
- # juxt (4)
- # kaocha (25)
- # leiningen (2)
- # malli (11)
- # off-topic (8)
- # pathom (1)
- # pedestal (9)
- # portkey (1)
- # reitit (3)
- # ring (2)
- # sci (46)
- # shadow-cljs (21)
- # tools-deps (15)
- # xtdb (31)
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
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?
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.
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."))
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
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.
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.
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?
@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!
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.
so in your case (comp #(conj {} %) #(string/split % #"="))
or just #(conj {} (string/split % #"="))
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
There is also ->>
to put the thread in the last position
Yeah, there are two âstylesâ of threading operators, thread-first ->
and thread-last ->>
, the latter works great with collections sequences đ
Being nit-picky: ->>
works great with sequence functions. Functions that take collections tend to have them as the first argument (such as conj
, nth
).
Haha yeah, correct lingo is important here, thanks for the correction đ!
don't map
,`filter`, etc. take both collections and sequences?
don't conj
, nth
,etc. also take both collections and sequences?
@U7RJTCH6J 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.
: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)
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 {})))
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
Being nit-picky: ->>
works great with sequence functions. Functions that take collections tend to have them as the first argument (such as conj
, nth
).
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.
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.