This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-09-12
Channels
- # aleph (11)
- # aws-lambda (1)
- # beginners (158)
- # boot (19)
- # cider (14)
- # clara (23)
- # cljs-dev (3)
- # clojars (4)
- # clojure (133)
- # clojure-dev (57)
- # clojure-dusseldorf (1)
- # clojure-finland (2)
- # clojure-gamedev (31)
- # clojure-greece (15)
- # clojure-ireland (1)
- # clojure-italy (3)
- # clojure-russia (8)
- # clojure-spec (149)
- # clojure-uk (51)
- # clojurescript (88)
- # community-development (1)
- # component (5)
- # cursive (17)
- # datomic (3)
- # emacs (6)
- # fulcro (142)
- # graphql (1)
- # juxt (15)
- # lein-figwheel (1)
- # luminus (3)
- # lumo (6)
- # off-topic (11)
- # om (8)
- # onyx (5)
- # portkey (6)
- # proton (2)
- # protorepl (3)
- # quil (6)
- # re-frame (14)
- # reagent (9)
- # shadow-cljs (226)
- # specter (11)
- # testing (96)
- # uncomplicate (5)
- # unrepl (8)
- # vim (11)
a couple code-structure/practice questions 1. Should the code be structured from the top down based on function use order, or bottom up. ie. if func-a depends on func-b, shoud it be
(defn func-b (something))
(defn func-a [] (func-b))
or the other way around
2. Should my tests cover only the functions I expect to provide an api, or use defn-
any helper functions and only test my public api functions@theeternalpulse clojure won't even compile the opposite order (unless you go out of your way to use declare, which is rarely done)
with tests I start with public API but I also make regression tests as needed for implementation details (and "needed" for regression tests is subjective, but I tend to find that my problems often come up repeatedly in one place in the code, and regression tests help expose that and can sometimes help you find your way out of it too)
I see, yeah the first one I've forgotten from doing javascript for a career -_-, that and adding commas to my clojure list structures
what would be an example of the regression tests
a regression test is a test written to 1) prove that you can replicate a bug 2) prove that the bug is fixed 3) make sure you know if some future change makes the same bug come back
arguably you never need them if your design fits your problem properly, but for stuff that gets quickly iterated they can be a lifesaver
ah yes, I was more wondering how a regression helped in the development process, I tend to just write negative test cases if I can see a point of failure through the input to the function
I'm not talking about TDD here at all
if you are, my comment was accidentally off the topic
regarding "TDD", my 2 cents: 1) write tests for what you aim to achieve - don't test for errors 2) don't write narrow tests for impure stuff. large 'integration" type tests > small "unit" type tests for side-effecting stuff 3) feel free to test your pure functions as narrowly as you want 4) if you can cover 95% of your codebase with a short (10-20) lines main, that's a useful test
assuming your functions and namespaces are named in a way that Makes Sense (tm) [ideally for more than just yourself], getting backtraces from your test main will help you fix stuff just as quickly as a test suite
yeah, I'm always bouncing between functional vs integration of functions. The thing I'm confused about is 1) then my unit tests become fiddling with repl to get them right 2) expanding functionality requires more repl fiddling if it's a pervasive change.
like I made a macro recently, it uses about 4 functions. The defmacro body is only really 1 line
TDD is ok for pure stuff imo, especially if you have a nice IDE like cider or cursive (that will run the tests for you when you edit the test file). usecase would be, some data massaging namespace, or some maths/number crunching
do not test file i/o, databases, and the like, in a "unit" way. it's a path to hell, paved with mocks and nonsense
@didibus having worked on a single app written by a group of devs over many years, I find the relative lack of tests incredibly frustrating if I have to change anything. I don't know what you did in the repl to establish your code "works", and I don't know what to try in the repl to see if my enhancement or course change or simplification worked without doing a lot of tedious work and printlns and... ugh
if nothing else the tests would establish what the fuck someone was trying to do (as opposed to what the code does...)
Workflow is personal. But my recommendation is to think about why you are testing or writing tests. Failure to truly understand why (its a best practice or duh is not a good answer), would be a problem, and you'd need to think more about why you are doing it.
what helped me was just having #_ exprs with each test in the repl, but I can't imagine not covering those in tests. The repl helped me get there, adding the tests show other's who would use it that it works
but I don't particularly Beleive in TDD if you have a REPL
sorry, not each test, each function call with example inputs
so I'd have #_(some-macro (example-form etc etc))
@didibus it's personal if nobody else has commit access to the code
Often, there are the: How do I know this function works? And the answer is because I tried it with a bunch of test cases, and asserted it did what it should. This in Clojure is best done with the REPL. You can choose to put them into unit test also, but I find that's mostly a waste of time.
@didibus that's fine for write-only code
There's the: What is the expected behaviour, those are worth putting test cases, as a way to make sure the desired behavior is not broken in the future. Or documentation.
@noisesmith This has been my practice, with my team, its worked great. I can more easily understand what a function does by reading its doc + looking at the code + exploring its behavior at the REPL. I find unit tests often harder to get a good grasp, and they often assert things badly.
OK - my experience is every time I try to change something I didn't personally author I have a hard time distinguishing what it does intentionally from what it does accidentally or erroneously, and I need to get invasive with the running system to even have an idea what its input and output are supposed to look like
one factor here is that I have worked with people who are learning clojure on the job, so there's plenty of unidiomatic code in the code base
Hum, maybe. But do you trust the tests to be any more accurate? Like what if they tested the unintentional behavior?
I can simply comment out a bad test - commenting out the code leaves a broken app
I mean, I'm not saying not to have unit tests, you should have some. Just I feel you need a lot less then for languages that don't have a REPL, since in those, a lot of your unit tests are there to help you figure out the correct implementation and fix bugs.
OK - I think we are in agreement actually, I'm not talking about high test coverage percentage here, I'm talking about like, at least having one per important top level pure function with a basic test of typical inputs / outputs
if that calls 20 other functions that are not tested, but are easy to read, I'm fine with that
perhaps you've dealt with the "cargo cult testing" extreme case, while I live with the "nobody but me ever writes tests, period, or even knows how" side of things
Write tests, just don't go writing a test for every single small thing you would try out in your REPL, and don't write the tests first, write them in parallel or after the fact, the cases that make the most sense for the functions that make the most sense.
I like that
also the occasional "stay safe' test as @noisesmith said
I'm learning that when you work for a software company, no matter what, testing will eventually pop up with incentives, or simply as decree every once in a while
I think I keep getting jobs with people who suffered through "everything must be tested I don't care if it makes sense" bosses, and now they refuse to do it at all
I've seen code bases with 100% code coverage, yet when deployed it doesn't do what it should. When you don't test the right things, who cares if you have a test for every line.
I've never experienced actual encouragement to do testing at work, hah
we just got patted on the back for getting to 60, and then we recently got ordered to make it 70%
A good advice I got from a principal engineer which I've personally tried to apply since, was that never write a unit test, when an integ test is required. So if you are doing I/O, don't bother mocking anything, instead, properly setup an integrated integ test with a real DB, etc. and have it test the real IO.
definitely I prefer to keep things that do IO so simple that they are tested by the library in question, and then use pure functions to generate their input
that's an ideal that doesn't always work out 100%, but you can get pretty damn close
BTW, a good trick for getting more coverage in Clojure (which you shouldn't really do, but I'm not here to stop you), is to put more things on the same lines. Since most coverage tool will measure lines and most managers look at that, since nobody understand what a form is at that level. So if you put more forms on a single line, you get more coverage 😛
(if (something?) (bla bla bla) (blip blop bloup))
Now it doesn't matter which branch the if goes into, the line was touched.see, I think that leads to terrible code formatting - anything that worries about line count does. I want one useful unit of information per line (eg. I usually have hash-map literals with one key/value pair per line, and I am not going to write a unite test for each key value pair)
Actually, the forms covered metric is really nice in clojure. Most languages can't get that precise.
that sounds like a more fun macro than I made lol
We seem to have a lot of chatter about testing here -- we have a #testing channel -- perhaps folks didn't know that exists?
if I wanted to map within a nested structure like this
(map #(map (partial + 2) %) '((1) (2) (3)))
is map, map the way to go or is there a more functional way to do this?map
-over-`map` seems reasonable.
Nothing wrong with map over map. But for some reason, I tend to do for over map, like the difference in construct seems to help my brain clearly differentiate which level I'm at.
The for macro can also be used by itself, depending what you want:
(for [lists '((1) (2) (3))
item lists]
(list (+ 2 item)))
What is recommended for writing tests on clojure? Deftest? I really dunno how to write tests, I never did
Starting with clojure.test
and deftest
is fine when you're getting started. But there's a lot more available once you get into it. Feel free to join #testing and chat in more depth.
hey all, can someone school me on a nicer way to do this?
(def user {:age 100})
(def user2 {:name "Noman"})
(defn punctuate [user]
(def name (:name user))
(and
name
(str ", " name)))
(defn greet
"This greets a user."
[user]
(str
"Hello"
(punctuate user)
". How are you?"))
(println (greet user)) ;Hello. How are you?
(println (greet user2)) ;Hello, Noman. How are you?
i wanted to avoid defining a variable for name
but i couldn't figure out a way to not write (:name user)
twice
You should use let
instead os def
for binding local value
(defn punctuate [user]
(let [name (:name user)]
(and
name
(str ", " name))))
You can also use map destructuring like this
(defn punctuate [user]
(let [{:keys [name]} user]
(and
name
(str ", " name))))
or
(defn punctuate [{:keys [name]}]
(and
name
(str ", " name)))
https://clojure.org/guides/destructuring#_associative_destructuringwhere can i learn more about this {:keys}
thing? i'm having trouble finding it in the documentation
looks like i can do this as well, which seems a little nicer
(defn punctuate [{name :name}]
(and
name
(str ", " name)))
:keys
is a shorthand for destructuring and using the same name.
You can do like this to assign value from :name
to n
(defn punctuate [{n :name a :age}] ...)
But if you use :keys
, you will get variables with the same name
(defn punctuate [{:keys [name age]}] ...)
Detailed explanation is in the lower section of that doc page.i wanted to avoid defining a variable for name
but i couldn't figure out a way to not write (:name user)
twice
You should use let
instead os def
for binding local value
(defn punctuate [user]
(let [name (:name user)]
(and
name
(str ", " name))))
You can also use map destructuring like this
(defn punctuate [user]
(let [{:keys [name]} user]
(and
name
(str ", " name))))
or
(defn punctuate [{:keys [name]}]
(and
name
(str ", " name)))
https://clojure.org/guides/destructuring#_associative_destructuring@noman another option
(some->> user
(:name)
(str ", "))
this stops if it finds a nil and returns nil, otherwise you get the stringuser=> (defn punctuate [user]
#_=> (some->> user
#_=> (:name)
#_=> (str ", ")))
#'user/punctuate
user=> (punctuate {})
nil
user=> (punctuate {:name "Joe"})
", Joe"
So how can I cause boot to pass arguments passed to a task onto the program like is done if I do something like lein run -p 3000
. How do I define the arguments to (deftask run)
in boot? Basically the equivalent of [& args]
for tasks
actually, with-pass-thru might not be right, I was just basing that off the boot for lein users lein run
implementation, but (doc with-pass-thru)
looks like it does something different, I'm interested to see what they say over there
yeah accoring to doc
, with-pass-thru just passes on the fileset, which isn't as useful in this case
Hello, does clojure have smth like reduce but which can have several seqs as arguments?
@yanis what do you want to do with the several seqs? process in parallel like map does?
@noisesmith yeah, exactly, thank you, didn't know map can take several seqs
yeah, I use map as a helper for this
user=> (reduce (fn [acc [a b c]] (conj acc [c b a])) #{} (map vector [1 2 3] [4 5 6] [7 8 9]))
#{[9 6 3] [7 4 1] [8 5 2]}
I guess using a set as the accumulator makes it less clear, but the three sequences are consumed in parallel with the help of map
and then destructuring lets us pretend they are separate args in the reduce
@noisesmith thank you for the example, very clear, very beautiful
as a more advanced usage, you can use (map vector)
standalone as a transducer arg to transduce
with a small adjustment to the reducing function to support the one argument arity
this has the same behavior but the added flexibility of using a transducer (these compose nicely) and a moderate boost in performance
oh, wait, that wouldn’t work because we want to consume multiple collections so it can’t be the transducing map
@noisesmith thank you again) https://www.dropbox.com/s/j451ou0em6hd51k/img-2017-09-12-20-41-15.png?dl=0
so, you could replace my_f
with something like #(into acc (filter some?) %)
- then you don’t need that ->>
or into
(this time a transducer does actually help)
it's from 4clojure 🙂 I see other solutions now & mine sucks https://www.dropbox.com/s/oddzzwwx58qvz1r/img-2017-09-12-20-44-13.png?dl=0
@yanis that’s what 4clojure is for of course, is finding out that other people know some tricks you don’t know yet, and learning their tricks 😄
I have a Clojure service that I want to extract into a Java library so that a separate Scala app can call the service directly rather than incur network overhead. I have a single entrance method: (defn process [m1 m2] ..)
where m1
and m2
are both maps, and the return value is a map from keywords => vectors. I'd like to be able to do Engine e = new Engine(); m3 = e.process(m1, m2)
from Java. Can anyone point me in the right direction to enable this? I have a :gen-class
to build Engine
and provide init/state/methods
, but I'm not sure which data types to use in the methods
definition for process
. clojure.lang.IPersistentMap
? java.util.Map
?
Map is probably the safe/convenient one
if you want to be defensive about it you can call (into {} m)
in the function body to get the PersistentHashMap version
but even a Map should work with most clojure code
(also if this idea seems crazy and there's a simpler way to get to clojure from Scala, let me know!)
also, an alternative to gen-class
is to make a stub java class that uses clojure.java.api.Clojure
to require and then launch your code https://clojure.org/reference/java_interop#_calling_clojure_from_java
then your type declarations come from java code (which might be friendlier anyway, eg. that way you can end up with javadoc and annotations as consumers might want)
so I'd just have a wrapper Java class, with a method process
which under the hood redirects to IFn process = Clojure.var("some.ns.engine", "process"); m3 = process.invoke(m1, m2)
?
right, be sure to call require in there too (maybe in a static initializer)
but require is idempotent and not super expensive so you could put it in the method, depending on what you are doing and what the use case looks like
it's possible that standard *out*
is not being captured properly. I forget the details exactly, but in some lein repl
contexts it might not work as you expect
@rinaldi not sure how much your app relies upon lein
, but if you do java -cp $(lein classpath) clojure.main
to start a bare repl (non nREPL-based), see if it fixes your issue
hey all! i also have a question related to future
. i have something like this:
(map #(and @(future (process-a %) true) @(future (process-b %) true)) list-of-strings)
i don't want to complete the function unless every value in this collection is true but if i do
(let [proccessed (map #(and @(future (process-a %) true) @(future (process-b %) true)) list-of-strings)]
(when (every? true? processed)
(something)))
process-a
or process-b
. why could that be? is there a way to get these logs to show? (just to clarify these are AWS logs)when
block the proper way to evaluate that processed
is complete?@irasantiago It could be that's because the whole thing is lazy, so your map wasn't actually executed yet
@irasantiago But also, I just don't think what you're doing works at all.
Your futures are really adding no value here, since you're dereferencing them at their creation site
if you want them to execute in parallel, bind the futures in a let block, then deref in the let body
something like
(let [processed (doall (mapcat #(vector (future (process-a %))
(future (process-b %))))
list-of-strings)]
(when (every? (comp true? deref)
processed)
(something)))
So you're code is doing: 1. Loop on every string in a list-of-strings 2. For each string, call process-a and wait for it to be done, then call process-b and wait for it to be done. Once both are done, (and) the results. 3. Once all string have been mapped, then check that all results were true, and if so, do (something).
when
does not wait for any results, if that is what you were looking for. It is essentially just an if
without an else
clause.
like most other forms it will wait for the first arg to return a value though
@ghadi I wonder if I’m misreading what you say, because while the map form is lazy, the values inside the function passed to map are consumed one at a time in a blocking way and the new futures aren’t going to start until previous ones complete, right?
the code above is launching two things at a time per element in the list of strings, then it's dereffing everything to force realization
oh, right, thanks
fixed (I think - I think there’s still a problem compared to the original logic (I’m not sure what the and was supposed to accomplish, I’m sure it didn’t do the right thing in the original, but I also don’t think my code does what that and was intended to do))
@ghadi What do you mean? You talking about my #2 which was describing @irasantiago 's code? Or you talking about @noisesmith 's code?