Fork me on GitHub
#beginners
<
2021-03-05
>
Adrian Imanuel05:03:21

i wonder how to make a "MiXeD CaSe" , upper : for odd, lower : for even?

valerauko05:03:46

Is there a way to get reflection / boxed math warnings excluding dependencies? Putting the globals in project.clj includes dependencies, which is a lot of noise. set!ing them in the main ns seems to be too late and I get no warnings

phronmophobic05:03:51

are you working at the repl or running from the command line?

phronmophobic05:03:18

at the repl, I can usually just set! in the main namespace and reload the buffer

phronmophobic06:03:16

at the command line, I would probably just do something like:

clj <my run args...> 2>&1 | grep 'Reflection warning' | grep "<filename I care about>" > warnings.txt

valerauko06:03:06

I'm doing the grep way right now. I was hoping for something more... uh, refined?

phronmophobic06:03:25

afaik, warnings from warn on reflection can only be read from stderr, so any option will have to read from stderr. you could do something like https://github.com/clojure/clojure/blob/cbb3fdf787a00d3c1443794b97ed7fe4bef8e888/test/clojure/test_helper.clj#L123 if you wanted to programmatically gather warnings. It's possible that there's a tool or IDE that already does that, but I just common tools like grep ¯\(ツ)

seancorfield06:03:01

FWIW, I put (set! *warn-on-reflection* true) in every source namespace, after the ns form as a matter of course these days.

seancorfield06:03:18

(I often put it in test namespaces too)

seancorfield06:03:55

@adrianimanuel take a look at cycle and think of it used on the functions you want to apply to elements of your string

seancorfield06:03:17

Like so:

user=> (require '[clojure.string :as str])
nil
user=> (str/join (map (fn [f c] (f (str c))) (cycle [str/upper-case str/lower-case]) "mixed case!"))
"MiXeD CaSe!"
;; or:
user=> (str/join (map (fn [f c] (f c)) (cycle [#(Character/toUpperCase %) #(Character/toLowerCase %)]) "mixed case!"))
"MiXeD CaSe!"
user=>

clojure-spin 3
Adrian Imanuel06:03:45

thank you so much, I'll digest this code... unfamiliar with cycle yet

Adrian Imanuel06:03:02

now I just learn that we can use map like that as well 😲

seancorfield06:03:02

clojure.core is huge -- even after over a decade of using Clojure, I'm still finding new functions (or new ways to use old functions).

Hagenek09:03:00

This made my day a little better, such a fun way to solve this problem:grinning:

Joel Ambass09:03:00

Hey there, total Clojure noob here! I'm reading a book called "7 concurrency models in 7 weeks" where Clojures concurrency model is one example. Now there's one thing which isn't really explained that well and where I didn't really find a quick explanation about what it exactly does under the hood (probably I just don't know where to look at). The book states that: > As its name suggests, ensure ensures that the value of the ref it returns won’t be changed by another transaction. It’s worth comparing this solution to our earlier lock-based solutions. Not only is it significantly simpler, but because it’s lockless, it’s impossible for it to deadlock. Now I don't quite understand yet how this is achieved

hiredman09:03:15

you can imagine (ensure r) as being the same as (alter! r identity)

hiredman09:03:18

I wouldn't focus so much on ensure, it is optimization basically, if it didn't exist there other ways to avoid, uh, I guess it is read skew

raspasov09:03:30

@ambass.joel I always found that for explaining specific Clojure concepts like concurrency around refs/agents the old https://www.amazon.com/Clojure-Programming-Practical-Lisp-World/dp/1449394701 is hard to beat; while dated in certain aspects, I found the book sections about refs/agents quite excellent and they are still pretty much 100% valid (no significant changes in Clojure in those areas as far as I know)

danielneal09:03:42

Is there an alternative to merge that will ignore nils. I got (merge-with #(or %1 %2) {:a 1} {:a nil}) ;=> {:a 1} but wondering if there is something more idiomatic

Ben Sless09:03:53

(into to (remove (comp nil? val)) from)?

danielneal09:03:45

I guess, thanks

raspasov09:03:54

@ambass.joel another piece of practical advice around concurrency in Clojure is also “stick to (atom …) for vast majority of cases”; way simpler and usually there’s a way to solve your problem with just atoms;

Joel Ambass09:03:34

Jup that's also mentioned and I think it makes a lot of sense for 99% of the cases.

raspasov09:03:18

Yes, that’s correct. In 8 years of Clojure, I don’t think I’ve used (ensure …) even once 🙂 And I’ve written some decently optimized/concurrent code with it; I can relate to the curiosity just for learning’s sake though.

😁 3
Joel Ambass09:03:02

Thanks for your answeres! I have to admit that I'm (not yet) ready to look deeper into clojure as a language itself so I'm more interested on a conceptional level for now 🙂 So what I'm basically trying to understand is how ensure guarantees that other transactions don't change the ref ? Maybe there's also some missunderstanding of the guarantes ensure brings.

hiredman09:03:32

ensure doesn't really do that

hiredman09:03:51

the transaction does it, ensure just makes sure the given ref is part of the transaction

hiredman09:03:13

clojure does mvcc

hiredman09:03:03

meaning there are multiple versions of each ref, associated with each concurrent transaction, and one of those versions gets committed, and other transactions retry

hiredman09:03:54

alter! or ref-set! in a transaction mark a ref as part of that transaction, just reading the value of a ref doesn't

hiredman09:03:17

ensure is a way to read the value and mark it as part of the transaction

Joel Ambass09:03:18

Got it! That makes a lot of sense.

Joel Ambass09:03:00

I think the part that I was missing is that the other transactions would retry. Kinda stupid question but how does it determine if it needs to retry? I guess it needs to compare it's local state of the ref with the now global state or something like that? When does that happen? (Probably still some misunderstanding and/or mixing of thoughts on my side 🙂 ) EDIT: I guess it's the dosync function that does that right?

hiredman10:03:05

the dosync causes the code in its dynamic extent to run in a transaction

hiredman10:03:09

I believe basically each ref has a version number, and when a ref is modified for the first time in a give transaction, the ref and its version number are recorded in the transaction, and then when it is time to commit the versions numbers are compared to the current versions, and if the ref has been modified since, then the transaction is retried

Joel Ambass10:03:49

Ah ok so similiar to Postgres transaction ids in that sense. But does the dosync block actually syncronize (I think it doesn't right?) so how is it guaranteed that there are no race conditions?

hiredman10:03:30

I guess reading the value of a ref does make it part of the transaction in a limited sense, in that it causes the transaction the cache the value read and return in for subsequent reads, so all reads in a transaction get the same value

hiredman10:03:46

it is called a LockingTransaction because it is built on locks

hiredman10:03:44

the only mutable things are the refs, and you can only mutate the refs in transactions, which are real transactions (ACI but no D)

Joel Ambass10:03:57

So where does the locking/synchronization happen? Is the whole transaction synchronized or just when we ensure or alter the refs ?

hiredman10:03:11

none of those

hiredman10:03:44

the locking happens when a transaction commits, which is after the body has run

hiredman10:03:05

(technically refs all have a readwritelock, and the read lock may be held briefly while reading)

Joel Ambass10:03:21

So it gets the global state of all the ref s and compares them to their local state (with a semaphore around the checking of those refs)

hiredman10:03:57

it just checks the state of the refs involved in the transaction

Joel Ambass10:03:12

against what?

hiredman10:03:31

against themselves

hiredman10:03:12

if they haven't been modified since the body of transaction ran, the transaction can be committed, otherwise it needs to be retried

Joel Ambass10:03:20

Yes that I understand but how does it know that they have been modified? I guess they have their local cached copy of them (with a given transaction version number or something) but what does it use to see if somebody else have modified them?

hiredman10:03:44

the ref has a version number, not the transaction

Joel Ambass10:03:02

Yes sorry that's what I mean with that 🙂

hiredman10:03:04

so the first time a ref is touched in a transaction, it will store in the current transaction "my version is 5", and then at the commit point, if the ref says "my version is 6", then you need a retry

hiredman10:03:26

if it says "my version is 5" at the commit point, you can commit and increment the version on that ref

Joel Ambass10:03:11

Ok, that's what I basically meant with comparing the global state of the ref (current version number) with the local one (the one it had at the start of the transaction)

hiredman10:03:16

this is all based on my fuzzy recollections, at a certain point the answer to "but how does it do that?" is "it does the stuff in the code there" which is what I linked to

Joel Ambass10:03:43

Yeah no that's the level I wanted to get answers to. If I had a bit more knowledge and time to invest I would have tried to get that from the source but I'm really thankful for your time and explanation! It certainly helped a lot to understand the concept and important details of it better 🙂

sb10:03:14

if I have a lib which works normally (via shadow-cljs) https://www.npmjs.com/package/jsonwebtoken I would like to do a jwt sign.. with rs256 in clojurescript, my code is:

(def creds {:client_x509_cert_url "",
            :token_uri "",
            :type "service_account",
            :private_key_id "58f854354354354330b0fd3",
            :auth_provider_x509_cert_url "",
            :client_email "",
            :client_id "11765353534543507207",
            :project_id "ga-v4-auth-test",
            :private_key "-----BEGIN PRIVATE KEY-----
               MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCeHLXyO+bOEWyq
               bYNKVGMclwrMFq8IdKrUjdDg+1PT9OK4d3k2Xv7jpprjI2dSNpuLm26JIvcE0hF2
               smk4mOXKzrf79v+lPLM+5GKv6YBW7TlZfJBzIxtPMLhiZBMKNUaYZSQsldJiTISM
               ...............................................................
               dKmIUpzZ4Y1FnRVx6FCENN0JlzOaGa2PhGUZfLisXVwkWyXinyuElyaUGXo6BI64
               fazS98F6ZtU8GhElnKO43usa5bcQareDtan9N1m1S8g4Tr3x7daPbhiVAoGAU+AS
               eIBqm7zMxM3RE57+GH59o2SvVD94QpDn9GbHUs2IuKE4NTrrdp79YBphRVpyb6R8
               jeJfTr8FM3zLzUeBgRxuY9Jus6wpM5gXYzw+v8NCjwb9dsD0BSkLmRPXe1L6pwAr
               0c3lQkcB1sDwIxAIE1noM/6Ex+UdESLGHi0HXoECgYByffIreMTVaUAWhMWh0dBX
               /2eVMxOibNGtoiCWn6f3d8zKFJb4ut7NCC5fZHzUccGPyJ8hVFi1BWvJjmIIYM0M
               ZzeXhzkqttFA9uT5irrRD071tKjdF/hXkJ510RdjSVgOd7z/YmzPxR2mpOS2LGH0
               Mld1BOLpH/p+DkED5p3+WA==
               -----END PRIVATE KEY-----
               ",
            :auth_uri ""})

sb10:03:48

and when I run:

(jwt/sign (.stringify js/JSON (clj->js {:foo "bar"})) (-> creds :private_key) (clj->js {:algorithm "RS256"}))

sb10:03:41

I got error: `Execution error (TypeError) at (<cljs repl>:1). Cannot read property ‘2’ of null`

sb10:03:55

Any idea what is the problem?

sb10:03:42

eg. this works fine: (jwt/sign (clj->js {:foo "bar"}) "sssssss")

dharrigan10:03:04

Are you you want to be leaking a private key?

sb10:03:20

No, as you see I modified

sb10:03:48

Just I would like to show the structure of original creds

sb10:03:26

I don’t know what is the problem.. I can’t find out/ see. Any tip welcome! :)

sb10:03:54

Sorry! that was a false question.. I don’t know why didn’t run the first.. but now works: (jwt/sign (clj->js {:foo “bar”}) (:private_key creds) (clj->js (:algorithm “RS256"))) maybe a typo

Lucy Wang15:03:31

I have recently 4clojure really good, even after I have written quite some clojure code https://www.4clojure.com/ . Question: is there such well-organized piece-by-piece-puzzle project for frontend stuff like html & css?

dharrigan17:03:50

Not sure if there is anything like http://4clojure.org for frontend Clojure stuff, but you may get something from this set of tutorials by practicalli

dharrigan17:03:01

Maybe that might help?

dharrigan17:03:37

There's a set of tutorials and videos about constructing a simple web-based banking system.

dharrigan17:03:07

It's not complete as far as I can tell, but it may help to get you started

dharrigan17:03:39

This is also a good source of information about using figwheel-main

Carlo16:03:20

hey, I started using cider within a clj project. When I go to a newline between two forms, the second form is not correctly indented (I didn't have this problem in my cljs projects). Who's controlling that indentation?

Carlo17:03:11

for example, if I have:

(defn something [param]
  (let [data (:key param)] | (prn data)))
and I issue a newline where | is, I get:
(defn something [param]
  (let [data (:key param)]
(prn data)))

dpsutton17:03:09

so it works on my machine. but here's how i can diagnose things. i copied that, put my cursor where the pipe was and hit enter and it worked. then i undid that, and hit C-h c [enter] which asks emacs to tell me what its calling when i hit enter. that said newline. So i'm gonna read the documentation for newline with C-h f newline which has the following documentation > If ‘electric-indent-mode’ is enabled, this indents the final new line that it adds, and reindents the preceding line. To just insert a newline, use M-x electric-indent-just-newline. So now it seems it depends upon electric-indent-mode which is the thread i would look into now

dpsutton17:03:51

C-c v electric-indent-mode tells me this is on for me

dpsutton17:03:20

a bit of hint for perhaps some arcane commands here: C-h starts asking for help. C-h c is for "character", what happens when i press whatever. in this case it was the enter key. C-h f gets documentation about a function (`f`). I asked for documentation for what happens when i run the function triggered by the key i looked up. And then i checked out a variable with C-h v on electric-indent-mode and it told me some documentation about it and that it was enabled

dpsutton17:03:49

but from all that, i would call the function (elecric-indent-mode) and then see if that fixes it

Carlo17:03:48

thanks a lot for the details, @dpsutton! Now, when I hit a newline I'm actually calling lispy-newline-and-indent-plain, which in my case forwards to newline-and-indent , which, besides being advised to continue a comment, in general should behave like TAB. But also, TAB, which calls indent-for-tab-command, which should ultimately call indent-relative. But even indent-relative is just sending some tabs, not aligning it the way I would expect. Also, it still works in another cljs file, which made me think that it's an issue with how my project is configured.

dpsutton17:03:13

as far as i know none of this should really involve the project itself. there is a small bit that could ask nrepl for how to indent things since vars can have indention settings but i don't think that's what's going on here. all of this behavior doesn't take into account your project at all

Carlo17:03:53

also, strangely, it seems to be indenting properly other parts, and just break in this particular case

Carlo17:03:13

hah: the error seems to be in: clojure-indent-function as I get clojure-indent-function: Wrong type argument: number-or-marker-p, nil in my messages buffer. The cause might be that I started using emacs 28, and this broke something

dpsutton17:03:15

are you using paredit?

dpsutton17:03:33

ah. well you have some fun digging to do. and it sounds like you have some decent experience with emacs. so hard to tailor advice with the vast range of experience that might be in here: first day using emacs vs maintainer of a popular emacs library just new to Clojure or CIDER

Carlo17:03:01

right, I'll try to poke around, and in case probably just confirm that I don't have this problem in emacs27, and maybe open an issue. But thanks for the help! 🙂

👍 3
grazfather20:03:07

how should I structure this?

(defn get-current-branch
  []
  (let [output (->> (sh "git" "branch")
                    :out)]
    (if-let [branch (re-find #"\* (\w+)" output)]
      (second branch)
      (second (re-find #"\* \(HEAD detached at (\w+)\)" output))
      )))
Basically the second case of the if-let sucks

seancorfield20:03:33

I'd probably use a single regex with that extra string part being optional.

seancorfield20:03:39

(let [[_ _ branch] (re-find #"\* (\(HEAD detached at )?(\w+)" output)] branch) -- something like that?

seancorfield20:03:40

Although \w+ will not match - and I often have dashes in my branch names.

seancorfield20:03:02

dev=> (let [output "* master"] (let [[_ _ branch] (re-find #"\* (\(HEAD detached at )?(\w+)" output)] branch))
"master"
dev=> (let [output "* (HEAD detached at master)"] (let [[_ _ branch] (re-find #"\* (\(HEAD detached at )?(\w+)" output)] branch))
"master"
dev=> (let [output "* WS-12677-refactor-hubble-model"] (let [[_ _ branch] (re-find #"\* (\(HEAD detached at )?(\w+)" output)] branch))
"WS"
dev=> 

borkdude20:03:09

isn't he matching a SHA?

grazfather20:03:18

oh, and I didn’t think of destructuring instead of using secon

seancorfield20:03:21

Not from git branch.

grazfather20:03:31

@U04V15CAJ I am matching a branch or detached head, in which case i need to get the hash

grazfather20:03:39

i can expand the class to include dashes

phronmophobic20:03:47

depending on how much you know about how git works and how much you like parsing git commands, there's also https://www.eclipse.org/jgit/

seancorfield20:03:57

@U01KUQRDMLG Note that with the optional detached head stuff, you need to return the third item, not the second.

borkdude20:03:04

Right, I usually do it like this nowadays: [A-Za-z_-]

grazfather20:03:16

you can have dots in git branches, too 🤯

borkdude20:03:29

About jgit: even the clojure tools CLI is stepping away from that now by shelling out to git, so you're in good company :P

phronmophobic20:03:04

is that to avoid dependencies or is parsing git commands actually easier?

borkdude20:03:19

avoid dependencies, fix a list of issues

borkdude20:03:26

#tools-deps for more info

borkdude20:03:06

I think git itself has options to emit output that is easier to parse for some use cases

👍 3
sebastian andersen20:03:43

how come #{1 2 3 4} evaluates to #{1 4 3 2} but, #{1 2 3 4 5} evaluates to #{1 4 3 2 5} ?

seancorfield20:03:03

Sets are unordered.

seancorfield20:03:12

Hash maps are also unordered.

seancorfield20:03:14

If you walk along a set or a hash map (via printing or any sequence operation), you should pretty much assume you'll get the elements in a random order.

6
sebastian andersen20:03:18

Oh, okay, how come sets aren't ordered?

ghadi20:03:39

because sets have no order

☝️ 6
🎯 3
ghadi20:03:54

nor do maps

ghadi20:03:27

(despite other languages' implementation choices)

andy.fingerhut20:03:26

meaning, sets in mathematics have no order.

👍 9
pavlosmelissinos20:03:10

> Two sets are equal if and only if they have precisely the same elements. > the order in which the elements of a set are listed in roster notation is irrelevant, so {6, 11} is the same set as {11, 6}, and {2, 6, 4}, {4, 2, 6}, {4, 6, 2}, {6, 2, 4} or {6, 4, 2} all represent the same set https://en.wikipedia.org/wiki/Set_(mathematics)#Roster_notation

andy.fingerhut20:03:11

There are implementations in Clojure of sets and maps that do have various kinds of ordering guarantees, e.g. sorted by key/element given a comparator function (`sorted-set`, sorted-map built into Clojure), order by insertion, etc. if you really want one, but you often don't.

andy.fingerhut20:03:48

Some of those variants are linked from the Clojure cheat sheet, which can be useful for other reasons, too: http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-cdocs-summary.html

sebastian andersen20:03:48

But what is the point of sets in math then?

noisesmith14:03:25

they are specifically (and only) an object that is defined by what is (or equivalently what isn't) a member use them in clojure when the role a piece of data plays is to keep track of membership (for example when called as a function they act like a membership test)

(ins)user=> (def special #{1 2 3})
#'user/special
(ins)user=> (special 1)
1
(ins)user=> (special 5)
nil

andy.fingerhut20:03:10

Those others aren't quite as "first class" as the default unordered sets, e.g. if a library for reading a JSON file, XML, etc. can return a Clojure map, it depends upon who developed that library whether they thought to return one of these variants of sets/maps that preserve read order, or not.

andy.fingerhut20:03:33

To represent which of some domain of values has some property, versus which do not.

andy.fingerhut20:03:16

e.g. this set of symbols appears in this program, and any not in the set, do not appear in the program.

andy.fingerhut20:03:18

This set of records appears in this table of a relational database, any others do not.

andy.fingerhut20:03:52

As far as a map goes, consider a map used to describe properties of a person. Do you have brown eyes first, and black hair second, or vice versa? Answer: You have both simultaneously.

sebastian andersen20:03:23

Yeah, that's also how I Iooked at maps, they remind me a bit of js objects.

andy.fingerhut20:03:08

Some others who want to read, process, and write out modified JSON files that are similar to the input file might ask exactly the same kind of question about maps, if maps were used to represent the JSON data read, i.e. why don't they preserve read order by default? You can implement that in Clojure, but you might have to ensure that you use a data structure other than an unordered map to represent the JSON data you read, or else the write order could be completely different.

3
andy.fingerhut20:03:16

Should someone care about the order of keys in a JSON map? I have heard that some applications might actually do that, which other might say is a misuse of JSON. I don't know if there are any official answers to that question, or which applications might treat order as important there.

robertfw23:03:49

The official answer from the JSON spec is that objects are unordered

sebastian andersen20:03:04

But, I haven't yet experienced needing json objects to stay in order though. I guess it would be an issue if you decided to iterate over the keys, and expect specific pairs at some point, but you shouldn't do that, since that leads to easily breakable code.

andy.fingerhut20:03:05

So, think of a mathematical set as only the keys of a JSON map. There are many use cases for representing membership in some class of values, where order doesn't matter, and shouldn't.

sebastian andersen20:03:25

can you use a set to map a vector of maps thus filtering the maps by key to match the set?

andy.fingerhut20:03:43

I'm not sure I understand your question. In both mathematics and Clojure, the values that can be placed into a set can themselves be other sets, or maps, or numbers, etc. If that doesn't answer your question, please try again, maybe with an example.

andy.fingerhut20:03:43

In Clojure, the keys of a map can be sets, or other maps, or numbers, and many other kinds of values, which can sometimes be useful. I don't think JavaScript has such generality in its JS Objects as far as what keys it can have, but I'm no JavaScript expert.

sebastian andersen20:03:42

You sure can't do that with objects in JavaScript

pavlosmelissinos21:03:10

@andersendigital not really sure but maybe you mean this?

(let [vector-of-maps [{:a 1, :b 2} {:b 5, :c 1} {:a 1, :b 2, :c 3, :d 4}]
      a-set          #{:a :c}]
  (map #(select-keys % a-set) vector-of-maps))

=>  [{:a 1} {:c 1} {:c 3, :a 1}]

🙌 3
sebastian andersen21:03:32

@pavlos Yeah, that looks like what I was thinking.

clojure-spin 3
sebastian andersen21:03:19

Given this task: Write a function, `mapset`, that works like `map` except the return value is a set. Would you say this code is a correct answer?: ´´´clojure (defn mapset   [function collection]   (set (reduce (fn [final-collection item]                  (println final-collection)                  (into final-collection [(function item)]))                []                collection))) ´´´

phronmophobic21:03:20

Seems like a good start. A couple notes: • map is lazy while this implementation is eager • the initial value for reduce is provided a vector. Is there another initial value that might simplify things? • You're using into to add a single item to final-collection . Is there another function that might be more appropriate?

sebastian andersen21:03:32

I have yet to learn what it means to be lazy in clojure.

phronmophobic22:03:24

you can probably ignore that for now

sebastian andersen22:03:45

Oh okay, cool 😄

phronmophobic22:03:29

here's a quick overview if you're interested though, http://clojure-doc.org/articles/language/laziness.html

sebastian andersen22:03:09

Taking everything you said except for laziness into account, how does this feel?

(defn mapset
  [function collection]
  (set (reduce (fn [final-collection item]
                 (conj final-collection (function item)))
               [(first collection)]
               (rest collection))))

phronmophobic22:03:31

looking better. I wouldn't add the first item to the initial value since that will give you incorrect* results if you passed mapset an empty collection

phronmophobic22:03:52

The hint about the initial value was more about the fact that your goal is to return a set so there's probably a better initial value than an empty vector

sebastian andersen09:03:07

Oh, I feel like you're suggesting I use an empty set instead :thinking_face:

👍 3
grazfather21:03:26

Hm, is there an idiomatic way to run something before and after, but return the middle? something like with-open? Basically I am writing a script that will run a command on a certain git commit, but I want to checkout the commit where I started, so it would be like

(let [current (get-commit)
      _ (checkout commit)
       out (do-something)
      _ (checkout current)]
     out)

phronmophobic21:03:01

try try

(let [current (get-commit)]
  (try
    (checkout commit)
    (do-something)
    (finally
      (checkout current)))) 

grazfather21:03:08

Ah, nice 🙂 my next question was how to make sure i checkout current if there’s an issue

😁 3
Michael Stokley21:03:27

what are some general purpose debugging strategies that folks use to find errors in lazy sequences? normally i'd inspect the stack trace to see the line at which the error occurred, but if the seq is lazy, it only tells me the line at which the error was realized (i think).

Michael Stokley21:03:03

i also tried running through the code path and replacing all calls to map with mapv, thinking that would realize the error and help me hone in on it

Michael Stokley21:03:38

i've been warned about using concat because it can introduce the same kind of subtle, lazy bugs.

blak3mill3r23:03:21

In development and testing I tend to force the realization of a sequence so any bug that is only triggered later on in the sequence will be caught early. That isn't a magic-bullet of course, the tests aren't going to cover every possibility that you care about. I would say that what you're encountering is pretty fundamental to lazy sequences. However your stack trace should still be pointing out whatever code is called to generate the next element of the sequence (and that's the only thing that deserves to be called an "error in a lazy sequence"). Many times I have found that to be enough (understanding the local context surrounding an exception being thrown when generating more elements of a sequence).

🙏 3