Something at work that's been really painful is people breaking staging by simply not testing their code properly. I want to use lisp to generate some integration tests that verify the status of the API. Using JS the tests really aren't too bad but it's a ton of boilerplate. I'd love to write a DSL or something in Lisp to generate the integration tests. The end result will have to be working javascript code, though. Could anyone point me in the right direction? This is an opportunity for me to learn clojure better, solve a problem that will make my life easier when I get back to work in jan, and potentially convince my CTO that clojure is a real thing. Someone pointed me at parenscript. That's not really what I'm looking for. I don't want to write the tests in lisp and transpile them, I want to write lisp code that generates javascript code that does the testing for me.
@tylertracey09 If you can describe the sorts of actions or sequences of actions that need to be exercised by the tests, you could probably write Clojure Specs for those actions. Then you could use s/exercise
to generate random, conforming sequences of actions. Then you'd just need code that took each action as input and wrote out the JS equivalent to taking that action against the API.
We do something similar at work for some of our apps: we write Specs for "user actions", use those to generate sequences of "pretend users", and then we have an interpreter that runs those actions against the application being tested. That last stage for you would be to generate JS that ran those actions instead of just running the actions directly.
That last part is the one I'm least mentally prepared for right now. Is there an example I can reference that shows an idiomatic way to generate code in other languages? Or rather just generating larger things from templates of some kind? Ideally I would want to write little templates and replace parts of them rather than write long string things in lisp itself.
It's something that Eric Normand covers in his three "Property-Based Testing with test.check
" courses at http://purelyfunctional.tv -- $49 / month. Definitely worth the money for at least a month or two while you work through those courses (some of his other courses are great too: his REPL-Driven Development course is worth a month's subscription just on its own).
I guess I could just read the templates from a file and replace strings of a certain format with actual data?
We use Selmer for template-driven generation of content -- mostly HTML but you could use it to generate JS too.
Interesting, I'll check into it. Thanks!
Would the specs approach be viable for cljs as well?
You mean for generating cljs at the end instead of JS?
No I meant for writing my little program in cljs instead of full clojure
Not that I'm particularly opinionated I just have a clojurescript envrionment/project up and running already.
I've no idea how much of the generative side of Spec (and test.check) is available for cljs. I don't do any cljs (just Clojure for a decade).
Gotcha. No worries I think I have some other ideas on that front as well if it doesn't pan out. Selmer looks dead simple, exactly what I needed for that portion.
Looks like test.check is written mostly as .cljc
so it ought to work for cljs.
Selmer is pure Clojure, no cljs. But I think there are similar libraries that work for cljs. At least, I'd expect there to be...
thanks, that was super helpful
For a function that takes two args, is it possible to pass a single function call to it and destructure the arguments?
(defn two []
[3 4])
(defn add [x y]
(+ x y))
(add [[x y] (two)])
is what you want calling add with the value in two
? it could be apply add (two)
*
that's equivalent to add 3 4
that changes the semantics, I want the last call to be add not apply
I though this was possible with destructuring but don't remember
going to read up on it again
đ
@kaxaw75836 destructuring is a syntactic operation inside bindings (function args, let clauses, for clauses, etc.)
it can't be done in arbitrary forms
maybe a simpler way to put it: you can only destructure in specific contexts where you give a value a name
@kaxaw75836 for (add [[x y] (two)])
to work, it would need to rewrite the args to add before add sees them, and there's nothing (besides reader macros) that does that in clojure, and even reader macros don't turn one form into N forms in the parent
macros can rewrite parts of a form before emitting it (so something else with add inside it, like eg. let
) can do precisely what you want
(let [[x y] (two)] (add x y))
Ok, I see what you mean, yeah guessing hehe since I don't remember much Clojure
it's actually a really great property of clojure that makes the language easier to understand than most
(the fact that a child form can never rewrite a parent)
yep, that's is exactly how I solved right now, was worried about recur not being in tail position with a let
but it didn't complain (let [[x y (add)] (recur x y
right, tail positions look recursively into the expression tree
you can also recur from eg. a branch of an if
yep
Yes, apply was the correct answer. It lets you call a function with the arguments being a sequence instead of having to be explicitly given.
(add (two))
;; Becomes
(add [3 4])
Which can work, but for it to work like that you need to make add into:
(defn add [[a b]]
(+ a b))
Which I think is what you meant by using destructuring.
This solution requires changing the function arguments to take a sequence as the first arg, which you then destructure into your arguments.
You can also instead change the call site to do this, and that's done using apply.
(defn add [a b]
(+ a b))
;; And now if you call it like
(apply add (two))
;; It becomes
(apply add [3 4])
;; Which becomes
(add 3 4)
I find that when writing functions that are a tad bit complex I tend to use a number of let bindings to process different steps instead of inlining them into one functional pipeline. Is this an acceptable Clojure style? I enjoy constructing the oneliners but I tend to not enjoy reading them after the fact.
I find using let bindings to be great. I do also use the threading macros which have the main benefit of not requiring names for intermediate values.
Hello!
In the loop
documentation page, there is this example:
(loop [xs (seq [1 2 3 4 5])
result []]
(if xs
(let [x (first xs)]
(recur (next xs) (conj result (* x x))))
result))
I wonder to know why the vector is transform to a seq. (seq [1 2 3 4 5])
For better performance?because empty vector is truthy, empty seq is not
(if [] 1 2) ;; => 1
but I think there is no need to transform vector to seq in loop declaration. it is better to check emptiness of a collection using (seq col)
(loop [xs [1 2 3 4 5]
result []]
(if (seq xs)
(let [x (first xs)]
(recur (next xs) (conj result (* x x))))
result))
Perfect answer! Thx @delaguardo đ
> but I think there is no need to transform vector to seq in loop declaration. it is better to check emptiness of a collection using Yes tottaly agree. It would have been clearer if it had been at the level of the condition and I would not even have asked about this.
To be even clearer - calling seq
on an empty collection returns nil
- not an empty sequence
Hello!
If the range
function is lazy, why does the following function create an infinite loop in REPL?
(defn even_squares
"Returns a seq of even squares less than the given number"
  [max]
  (for [x (filter even? (range))
        y [(* x x)]
        :when (< y max)] y))
but works fine with iterate inc 0
:when is a filter not a break; when you take
past the condition it enters n infinite loop looking to reify a head
thanks @claudius.nicolae, I just realized it also doesn't work with iterate inc 0
Could I ask what the idiomatic way to write this function?
:while instead :when
but that also still creates an infinite loop
You still end up with an infinite lazy seq, but now it always reifies an element, wheres before it would be stuck in an infinite loop after max takes
Clojure 1.10.1
(defn even-squares [max]
(for [x (filter even? (range)),
y [(* x x)],
:while (< y 100)] y))
#'user/even-squares
user=> (take 2 (even-squares 100))
(0 4)
actually both have the same problem if you try to take beyond the elements that are yielded, at some point it just loops and never finds more
I think I found it
(defn even_squares
[max]
(take-while (partial > max)
(map (fn [x] (* x x)) (filter even? (range)))))
thank you again for the help @claudius.nicolae
Hello, may I ask how popular is Fulcro among Clojure devs?
For a beginner, would you recommend Fulcro?
@raymond150 There is a thread about fulcro here: https://www.reddit.com/r/Clojure/comments/kibrfs/fulcro_as_fullstack_framework_or_else/
@borkdude thanks!
calling first or next already invokes seq internally, which is a cached transformation - the seq call in the bindings is redundant but not an extra computation
OK, this explains why it works without seq
.
Thank you for this good information @noisesmith!
Relying on first and next is not really safe, eg.
(loop [xs []
result []]
(if xs
(let [x (first xs)]
(recur (next xs) (conj result (* x x))))
result))
;; => Execution error (NullPointerException) at user/eval138 (REPL:5)
So seq
is required somewhere, in bindings or in condition expression@delaguardo yeah, I hadn't considered the case where xs isn't a literal, but style wise I would put the seq
call inside the if
yep, I suggested to do the same
@raymond150 If you haven't already found it, there's a #fulcro channel here, so you could ask there about how beginners have gotten on with it.
Has anybody compared cljfmt and clj-kondo and weighed their pros and cons? Any other linter that people use?
funny that apples and oranges are quite similar and it seems not unreasonable to be comparing them.
cljfmt is not a linter, its a code formatter
đ đ
Good complimentary linters would be: clj-kondo, eastwood and kibit
Joker is another one, but it overlaps almost 1:1 with clj-kondo, with the latter supporting more things. So I would just fgo clj-kondo and not bother with joker
Only clj-kondo is fast enough to be editor integrated and run on every code change. For eastwood and kibit, I'd run them less often, like pre-commit, or at build, etc.
Thanks!
I'm trying to solve a problem where I want to set a series of default values in a map only if the map is missing values. I can't create the map ahead of time but rather have to do this post map creation. Currently I have a function that takes a map, key, value and checks if the map has that key, and if it doesn't returns an updated map with the default value set (otherwise returns the same map). I am threading this my original map to multiple calls of this function like so:
(defn set-default [m key value]
(if (m key)
m
(assoc m key value)))
(-> my-map
(set-default :kw1 "default1")
(set-default :kw2 "default2")
... etc)
I am sure there's a better way to do this. Any suggestions where to start?
Edit: merge
really should do the trick, right...yeah, merge my-map into one with the defaults
Hi, I am using clj-http to get some image resources as follows:
(http/get url {:as :byte-array})
The server responses with 308 Permanent redirect. By looking into clj-http documentation, it seems to me that the request should be automatically redirected and I should get 200 response for the redirect. Instead, I am getting 308 response map. What am I doing wrong?Are you on most recent version?
did you try the :redirect-strategy :lax
?
doesnât having the seq call in the if statement (rather just once at the beginning) just add an additional seq call per iteration? I believe I added the example referenced above based on someoneâs advice in this slack. I thought it might have even been @noisesmith âs idea
IIRC the seq of a vector is cached
sounds good. I doubt it makes much of a practical difference. Just wondering for my own edification. the example with the seq call on the outside appears more than once as an example in the docs
oh - looking at the source it might not be cached
oh right, that makes sense as if it was cached, you might have extra seqs hanging around in memory after iterating over a vector that is kept around after the iteration.
next returns a seq (or nil if empty) and doesnât calling seq on a seq just return itself? I think the âextraâ seq calls would have a minimal impact
(ins)user=> (def v [1 2 3])
#'user/v
(ins)user=> (identical? (seq v) (seq v))
false
the overhead of seq'ing it is low, but it's not cached like I thought it wasah, I was thinking about the second iteration where it will have already been converted to a seq by next
:
(let [x (next (seq [1 2 3]))]
(identical? x (seq x)))
;; true
Yes, updated just now.
Actually maybe not, let me check.
Ok, so updating from 3.10.3 to 3.11.0 fixed the problem.
Ya, I did, but it should not matter since I am using GET request, this changes the behaviour for POST requests. But updating to the latest version fixed the problem.
agree. just thinking of things to try to investigate
thanks a lot for ideas đ