beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
seancorfield 2021-05-26T00:10:12.430400Z

@francesco.losciale Itโ€™s so that you donโ€™t leak connections if you consume the result lazily.

seancorfield 2021-05-26T00:11:18.431500Z

That was the bug it was fixing. Previously, if you lazily consumed just the first portion of the result (from either find-maps or find-seq), then the connection was never explicitly freed up (because you didnโ€™t reach the end of the sequence).

seancorfield 2021-05-26T00:11:39.431900Z

(itโ€™s why laziness + side-effects = trouble in most cases)

gibi 2021-05-26T07:10:39.462800Z

interesting, thanks @seancorfield

Joni Hiltunen 2021-05-26T04:24:04.433300Z

can someone help me understand what's wrong with my spec for ::main-script because it accepts invalid scripts... see gist for example, L33 shows the definition and L38-39 how I'm testing it https://gist.github.com/Sose/97dac8f1785d87d282d20e97fe1d41eb#file-spec-cljs-L32

seancorfield 2021-05-26T04:35:35.434100Z

@djonih [start & rest] is going to bind start to [:start 1 2 3] and that's valid for ::start right?

Joni Hiltunen 2021-05-26T04:36:36.434800Z

yes..

phronmophobic 2021-05-26T04:37:01.435100Z

also, rather than just a function predicate, you may want to use s/cat https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html#clojure.spec.alpha/cat

Joni Hiltunen 2021-05-26T04:40:30.436Z

@seancorfield but shouldn't rest bind to ([:call :circle "invalid"]) and that shouldn't be a valid ::script and i'm using (s/and)?

phronmophobic 2021-05-26T04:41:58.436100Z

what does s/conform report?

Joni Hiltunen 2021-05-26T04:44:52.436900Z

kilppari.spec> test-sc
;; => [[:start 1 2 3] [:call :circle "invalid"]]
kilppari.spec> (s/conform ::script (rest test-sc))
;; => :cljs.spec.alpha/invalid
kilppari.spec> (s/conform ::main-script test-sc)
;; => [[:start 1 2 3] [:call :circle "invalid"]]

lassemaatta 2021-05-26T04:45:12.437400Z

should that s/and in the predicate be just a plain and?

Joni Hiltunen 2021-05-26T04:45:51.437900Z

umm right I think so... I guess I don't really understand the difference between those two

Joni Hiltunen 2021-05-26T04:48:24.438500Z

changing it to a normal andseems to fix my problem.. Now I only have to figure out what s/and actually does ๐Ÿ˜„ thank you

lassemaatta 2021-05-26T04:55:35.443600Z

I'm no expert on the matter, but my understanding is that s/and is one way to compose specs, where the given value must match all predicates. Typical example is something like (s/and int? #(< 10 %)) , which accepts integers over 10. It has a bunch of details related to how the values propagate from one predicate to the next, and how it works when generating values, which I won't go into. But I think the relevant part here is that s/and returns a new spec, not a truthy/falsy value like and.

Joni Hiltunen 2021-05-26T04:58:10.444700Z

ahh.. I think I understand

lassemaatta 2021-05-26T04:58:49.445400Z

but as Adrian suggested, you might want to checkout s/cat for speccing a sequence. One day you'll want to generate sample data from the specs and find out that spec can't generate values that match your custom predicate.

Joni Hiltunen 2021-05-26T05:09:17.446200Z

thanks for the suggestion, I guess using s/cat that would be

(s/def ::main-script (s/cat :start ::start
                            :script (s/* ::instruction)))

seancorfield 2021-05-26T05:21:16.446300Z

So the first one is invalid and the second one is valid.

phronmophobic 2021-05-26T05:22:02.447600Z

I forgot that s/conform isnโ€™t super helpful in this scenario.

seancorfield 2021-05-26T05:22:13.448Z

s/and โ€œflowsโ€ the result of the first predicate into the second predicate. and just checks both predicates are true.

seancorfield 2021-05-26T05:23:58.449Z

Since you donโ€™t have an actual predicate being applied, you donโ€™t get what you expect (you have expressions that just return Boolean).

seancorfield 2021-05-26T05:24:35.449600Z

(and, yes, s/cat is what you want to specify and sequence of Specs over a sequence of values)

seancorfield 2021-05-26T05:25:21.449700Z

s/conform is telling the truth though ๐Ÿ™‚

lassemaatta 2021-05-26T05:30:53.451700Z

and I imagine that because s/and returns a spec it's value is truthy -> (s/def ::foo (fn [v] (s/and .. .. ..))) is essentially (s/def ::foo (constantly true)) ?

Joni Hiltunen 2021-05-26T05:33:13.453100Z

I got interested in trying to generate sample data for fun but it's failing on stuff like (s/def ::start (s/tuple #(= :start %) number? number? number?))because of the anonymous function... I tried to look but I don't see if there's actually a spec way for saying "something is equal to this"?

seancorfield 2021-05-26T05:34:01.453800Z

You can use a set as an equality predicate: #{:start}

seancorfield 2021-05-26T05:34:05.454Z

That should generate.

seancorfield 2021-05-26T05:35:13.454200Z

(! 532)-> clj -A:test
Downloading: org/clojure/test.check/maven-metadata.xml from central
Downloading: org/clojure/test.check/maven-metadata.xml from sonatype
Clojure 1.10.3
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::start (s/tuple #{:start} number? number? number?))
:user/start
user=> (s/exercise ::start)
([[:start 1.0 0 -1] [:start 1.0 0 -1]] [[:start 1.0 -1 0] [:start 1.0 -1 0]] [[:start 0 -1 0] [:start 0 -1 0]] [[:start -2.25 -2 -3] [:start -2.25 -2 -3]] [[:start -0.5 -1 -0.5] [:start -0.5 -1 -0.5]] [[:start 0.5625 14 -15] [:start 0.5625 14 -15]] [[:start 1 1.453125 2] [:start 1 1.453125 2]] [[:start -1 1 60] [:start -1 1 60]] [[:start 7 0.34375 2] [:start 7 0.34375 2]] [[:start -6 -1 2] [:start -6 -1 2]])

seancorfield 2021-05-26T05:35:28.454600Z

(the :test alias brings in test.check)

Joni Hiltunen 2021-05-26T05:37:46.455300Z

yep that works, fun.. doesn't work for ::instruction though, and not sure I understand the error

kilppari.spec> (gen/generate (s/gen ::instruction))

Execution error (Error) at (<cljs repl>:1).
Vector's key for assoc must be a number.
;; => :repl/exception!

phronmophobic 2021-05-26T05:41:14.455500Z

Indeed! s/conform can sometimes be useful for telling you how a spec is conformed. But not in this case.

seancorfield 2021-05-26T05:42:34.457200Z

I suspect (since Iโ€™m not terribly familiar with multi-spec) that you need to define ::instruction as (s/and (s/cat keyword? ...) (s/multi-spec ...)) although Iโ€™d have to think about what that first ... was

seancorfield 2021-05-26T05:42:58.457700Z

I donโ€™t know that multi-spec is going to generate on its own.

Joni Hiltunen 2021-05-26T05:43:22.458100Z

Ah. So I'd have to look into writing a generator for it I guess?

seancorfield 2021-05-26T05:43:59.458800Z

Depends. If your Spec for what an instruction sequence could be was tighter, it might generate out of the box.

seancorfield 2021-05-26T05:44:47.459600Z

Maybe (s/cat ::command ...) if you made ::command a literal set of possible keywords (since a set will always generate).

seancorfield 2021-05-26T05:45:32.460Z

Your ::command can only be a limited number of things (keywords).

seancorfield 2021-05-26T05:46:26.461Z

But the general idea is that you define an s/and Spec so that the first predicate will generate and the remaining predicates will filter (and hopefully succeed).

Joni Hiltunen 2021-05-26T05:47:34.461700Z

Hm.. right, I'll see if I can make it work

seancorfield 2021-05-26T05:48:13.462300Z

(but itโ€™s worth pointing out that Spec isnโ€™t designed to be a parser)

Joni Hiltunen 2021-05-26T07:55:54.464Z

I got it to generate something after bunch of messing around and trying to google things... The namespaces seem to matter too and I don't undestand them too well I guess... I'm not even sure what actually fixed it in the end ๐Ÿ˜„ <https://gist.github.com/Sose/32aa813eea3004ecf7c3176b8b7c5a68>

Joni Hiltunen 2021-05-26T08:11:56.465500Z

(number? ##NaN) =&gt; true , this definitely surprised me ๐Ÿ˜„ "is 'not a number' a number?" "yes"

2021-05-26T08:13:34.465600Z

https://en.wikipedia.org/wiki/NaN

Joni Hiltunen 2021-05-26T10:02:34.466600Z

I wonder if there's a better way of doing this :thinking_face:

(def mymap {:a 1 :b 2 :c 3 :d 4})
(def mykeys [:a :c])
(def myvals (map mymap mykeys)) ; =&gt; (1 3)

(def myanswer (map vector mykeys myvals)) ;=&gt; ([:a 1] [:c 3])

lassemaatta 2021-05-26T10:04:10.467Z

looks a lot like https://clojuredocs.org/clojure.core/select-keys

Joni Hiltunen 2021-05-26T10:04:45.467200Z

ohh, thanks

Joni Hiltunen 2021-05-26T11:21:28.469700Z

I wonder if there's a good pattern or method for testing things that are very "iterative" (?) by nature... Like stepping an interpreter and checking some values about the environment after each step? This is pretty painful to write imo... I mean it's obviously possible to write something but I wonder if there's a great builtin solution for these kinds of cases https://gist.github.com/Sose/f4fb8ffe55e54d9856e1e96bcd3eb51b

lassemaatta 2021-05-26T12:09:05.470Z

I recently did something similar (in Java), where I needed to submit events to a state machine and check the state after each event: create a vector of pairs, where each pair consists of a) an input value (in your case scripts) and b) a predicate function, which checks the state machine. Then you can reduce over this vector by submitting a script to the turtle thing and verify its state using the predicate.

Joni Hiltunen 2021-05-26T12:13:14.470200Z

Yeah I ended up doing something like that.. Though I guess it could be made a little more general still.. Current 4th version https://gist.github.com/Sose/f4fb8ffe55e54d9856e1e96bcd3eb51b#file-test-cljs-L53

Joni Hiltunen 2021-05-26T12:14:20.470400Z

not sure that even works ๐Ÿ˜„

Joni Hiltunen 2021-05-26T12:17:44.470600Z

ahh I need to (dorun ...) the result of the final map for the tests to actually run

lassemaatta 2021-05-26T12:20:27.470800Z

perhaps something not quite unlike this

Joni Hiltunen 2021-05-26T12:21:40.471Z

Hmm, thank you

Joni Hiltunen 2021-05-26T13:39:14.474300Z

I think I actually like this version the most because if you pass a predicate function, you don't seem to get the "expected" and "actual" reports when tests fail. It just says "(pred xxx)" failed. Unless I'm doing something wrong ๐Ÿ˜„ But maybe the most general version isn't the best in this case https://gist.github.com/Sose/5c4440f85b317e9f322c8b4fdca7d28d

Joni Hiltunen 2021-05-26T13:41:42.474500Z

maybe it would be better to pass in kv pairs as maps and compare all of those so it could test multiple things at once

Joni Hiltunen 2021-05-26T13:45:43.474900Z

easy enough with clojure.data/diff it seems

2021-05-26T15:57:01.481600Z

I'm super stuck on a dependency issue, I'm hoping I can describe it a bit and maybe get some suggestions for where to go looking: I've installed Datahike in my app via tools.deps (i.e., using a deps.edn file as opposed to Boot or Lein). The app works perfectly when I'm running it with the clj utility, so no problems there. However, after I bundle the app into an uberjar using Depstar, all of a sudden I'm getting errors that Datahike can't be found on the classpath. It's a fairly simple setup, I've got src and resources listed as my paths in deps.edn, so I'm scratching my head as to why it works with clj but not when running the jar with java. I'm also unsure whether this is specifically a Datahike problem, but it seems unlikely.

dpsutton 2021-05-26T15:59:35.482200Z

can you share your deps edn file?

2021-05-26T16:08:08.482400Z

Sure thing!

2021-05-26T16:09:22.482600Z

I'm building the jar with this command: clojure -A:depstar -m hf.depstar.uberjar mailfile.jar And running it like this: java -cp mailfile.jar clojure.main -m backend.core

2021-05-26T16:11:29.482800Z

Welp ... turns out Datahike was the culprit after all. Version 0.3.6 gives me that classpath problem, but 0.3.2 doesn't. Thanks for taking a look anyway @dpsutton, appreciate the offer ๐Ÿ™‚

dpsutton 2021-05-26T16:19:11.483100Z

i can make a simple uberjar with 0.3.6 and run it. i'm not able to reproduce your issue

dpsutton 2021-05-26T16:23:57.483300Z

https://github.com/dpsutton/datahike-jar

2021-05-26T16:26:22.483600Z

another option: (map find (repeat mymap) mykeys)

2021-05-26T16:27:02.483800Z

that gives literally the same result you had (select keys-gives back a map with the same keys, which will have the right contents if you call seq on it, but maybe not in the expected order)

2021-05-26T16:33:07.484200Z

Huh, interesting. Downgrading fixed it for me, but if it hasn't affected you then there's something else going on

2021-05-26T16:34:13.485500Z

Looks like you're using AOT and a few other things which aren't in my aliases, I'll give that a shot and see if it makes a difference

dpsutton 2021-05-26T16:35:23.486900Z

i can remove them. i think the warning even says i didn't mark any namespaces so its ignoring it

2021-05-26T16:36:17.487300Z

Nah, it's fine. I actually just started getting that same classpath error I was seeing with Datahike with a different library, so that shows me that the issue isn't with my libs

2021-05-26T16:36:56.487500Z

Those alternative Depstar args are looking like they could put me on the right path

2021-05-26T16:38:57.487700Z

and you're using a much later version of Depstar too come to think of it, like 2 major versions later than what I was using, that could very well be it

2021-05-26T16:39:55.487900Z

๐Ÿ™Œ bingo

dpsutton 2021-05-26T16:43:50.488100Z

awesome!

2021-05-26T16:47:34.488300Z

really appreciate it, thanks @dpsutton ๐Ÿ™‚

Joni Hiltunen 2021-05-26T17:52:28.489600Z

ehmm I'm stuck on how to "get rid of" one layer of structure ๐Ÿ˜„ flatten isn't really what I want

kilppari.turtle&gt; (def v [1 [[:a 3] [:b 5]]])
;; =&gt; #'kilppari.turtle/v
kilppari.turtle&gt; (rest v)
;; =&gt; ([[:a 3] [:b 5]])
kilppari.turtle&gt; (drop 1 v)
;; =&gt; ([[:a 3] [:b 5]])

Joni Hiltunen 2021-05-26T17:53:27.489900Z

hm I guess destructuring but are there other options?

phronmophobic 2021-05-26T17:55:38.490200Z

what output are you looking for?

Joni Hiltunen 2021-05-26T17:56:21.490500Z

without the outer list layer.. [[:a 3] [:b 5]]

phronmophobic 2021-05-26T17:56:49.490900Z

I would probably do something like (-&gt; v rest first)

dpsutton 2021-05-26T17:56:58.491100Z

(second [1 [[:a 3] [:b 5]]])

dpsutton 2021-05-26T17:57:17.491600Z

there are two elements in that vector, 1 and the second one is the one you want

๐Ÿ‘ 1
Joni Hiltunen 2021-05-26T17:57:28.491900Z

ohh you're right ๐Ÿ˜„ thanks a lot

2021-05-26T18:07:22.494900Z

I have a script which interactively walks through items received from a backend. Currently I (ab)use lazy-cat to achieve that. I shouldn't rely on this because it's side-effects in lazy sequences. which is not cool. What would be recommended in this case? I put a simplified repl-ready example in the ๐Ÿงต

2021-05-26T18:07:45.495Z

2021-05-26T18:08:35.495200Z

(doall (get-items {})) would take roughly 1second, while (take 1 (get-items {})) will take 200ms. (the desired behaviour).

phronmophobic 2021-05-26T18:20:32.495400Z

It really depends on how you want to handle errors.

phronmophobic 2021-05-26T18:21:11.495600Z

If it's for a dev script that rarely fails and rerunning the script is acceptable, then the lazy version is ok.

phronmophobic 2021-05-26T18:22:00.495800Z

If it's for a website where random, unhandled errors cost money, then you probably want a different strategy

2021-05-26T18:27:33.496900Z

> If it's for a dev script that rarely fails and rerunning the script is acceptable, This is indeed the case. It's part of a script the team uses to query deployable artifacts from a registry. I was just wondering if there's something I should be careful about; not too familiar with lazy sequences.

phronmophobic 2021-05-26T18:34:09.497100Z

Some common gotchas when mixing lazy sequences and I/O: โ€ข you can't control chunk sizes. Even doing (take n my-items) may realize more than n and there's not a good, reliable way to control how many items are realized with lazy sequences โ€ข Since you can't control chunk sizes, you also can't control when or which thread is used to realize items. โ€ข Exceptions may be thrown wherever items may be realized โ€ข Since you can't control when items are realized, if you try to stream items, then resources like network connections may close or time out depending on when items are consumed. It looks like your example is grabbing chunks all at once, so this is less of a problem โ€ข Since you can't control when items are realized, you can't rely on dynamic vars for configuration (not usually a problem, but it can come up).

1
phronmophobic 2021-05-26T18:39:29.497400Z

Ymmv, but it seems like it might work reasonably well for a dev script. It seems like the type of thing where someone finds a TODO 6 years later that says "TODO: replace lazy-seq with a better alternative", but it's been working fine the whole time

๐Ÿ˜… 1
2021-05-26T18:40:36.497600Z

https://clojure.atlassian.net/browse/CLJ-2555

๐Ÿ™Œ 2
2021-05-26T19:32:47.499200Z

Thanks for the thourough explanation! I'll make sure to include that Todo so the prophesy may come true ;)

๐Ÿ˜† 1
Joni Hiltunen 2021-05-26T20:09:19.000800Z

Does anyone know if CIDER in Emacs can do something like "auto reload on save"? I got so used to that in ClojureScript that if I'm editing Clojure code, I sometimes forget to manually re-evaluate things and get confused why my changes are doing nothing

dpsutton 2021-05-26T20:34:43.001900Z

emacs has a general after-save-hook. You could make a function that checks if the buffer 's mode is clojure-mode, and cider is connected then call cider-ns-reload

2021-05-26T20:40:40.002800Z

Has anyone here been using http://fig.io? If so, I would love some help building a Leiningen autocomplete spec

2021-05-26T23:22:28.003200Z

https://twitter.com/roberthaisfield/status/1397694221553856512?s=21

๐Ÿง  2
๐ŸŽ‰ 3
2021-05-27T20:06:19.046900Z

of course you can always replace (fn [a b] (* a b)) with *

2021-05-27T20:06:33.047100Z

for factorial, you might want *' instead

2021-05-27T20:06:58.047300Z

(ins)user=&gt; (reduce *' (range 1 101))
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000N
(cmd)user=&gt; (reduce * (range 1 101))
Execution error (ArithmeticException) at user/eval314 (REPL:1).
integer overflow

2021-05-27T20:07:27.047500Z

a nice thing about clojure is that most of the time, with good code most of the code disappears

2021-05-27T20:31:33.047900Z

Huh Iโ€™ve never seen the ' after a multiplier. Is that a general syntax thing? Whatโ€™s going on there?

Oliver 2021-05-28T20:53:17.110900Z

https://clojuredocs.org/clojure.core/*'

2021-06-02T18:17:43.272100Z

@rob370 it's an old lisp idiom to name something foo' if it's an extended or enhanced version of foo, and to name it foo* if it's a less featureful intermediate form of foo in clojure it seems to only get used for operations that upgrade to bignum:

)user=&gt; (-&gt;&gt; (all-ns) (mapcat (comp keys ns-publics)) (map name) (filter #(re-find #"'$" %)))
("+'" "dec'" "inc'" "-'" "*'")

2021-06-02T18:18:29.272300Z

but I've definitely used x' in let blocks to mean "next value of x"