This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-25
Channels
- # alda (7)
- # aleph (10)
- # announcements (3)
- # babashka (103)
- # beginners (54)
- # calva (62)
- # clerk (2)
- # clj-yaml (27)
- # cljs-dev (1)
- # clojure (61)
- # clojure-europe (64)
- # clojure-nl (3)
- # clojure-norway (34)
- # clojure-sweden (4)
- # clojure-uk (4)
- # conjure (9)
- # cursive (1)
- # data-science (3)
- # fulcro (20)
- # gratitude (1)
- # hyperfiddle (54)
- # lsp (9)
- # malli (7)
- # meander (4)
- # membrane (17)
- # off-topic (23)
- # releases (3)
- # sci (1)
- # shadow-cljs (5)
- # sql (1)
- # tree-sitter (8)
- # vim (6)
What's not to love about Javascript, right? BTW - this project is pure JS - there's not even Babel involved, so no source map or whatever other thing that can be masking the stacktraces... EDIT - just to be 100% clear - these lines never ran in the code - I added some logs to check, and nothing was printed.
I'm confused. The error is a sql error. Your stacktrace seems to show the correct path. On line 36, you call this.query passing in some sql. That makes its way to your sql library and throws an error because of a not null constraint. That's what the stacktrace says. You are pointing to the string of the sql on line 37, but the string isn't erroring, the function call is. Everything looks right to me
Looking better now, it seems you're right. The thing for me is that the call is supposed to be async, but it seems that if the query fails, it throws the error synchronously... Well, now I have a bug to fix, because I'm not rolling back these transactions :(
Still, I'm calling the function on that line, so it should at least show me that line, I guess?
Unclear what this.query
is, but if it returns a promise (or is an async function, which is the same thing), then the right part of that function might be getting executed at some later time.
Same exact thing you can have in Clojure with lazy collections or core.async
.
It does show you that line if you look closer at the stack trace. Also shows you that the call is happening async (there is a promise in the stack trace) I definitely recommend trying to use async await in js. It makes a lot of things much much easier. But yeah async code is really hard. It is really easy to blame our tools when we encounter those tough issues. But for all the flaws js has, it is really good at async code. If something is confusing, it might mean you just have to dive deeper and understand js a bit more.
> I definitely recommend trying to use async await in js. It makes a lot of things much much easier.
Yep, that's what I am using - the function that calls this one uses await
In the end, I took a little bit more time to hook up devtools, and now things are way better 🙂
The interceptor
pattern has been a godsend in terms of localising async errors.
(of course it's easier in cljs
😉 )
I came across this quote on HN: > type inference is a poor man's model checking Source: https://news.ycombinator.com/item?id=37633243 As a community that has a pragmatic but sometimes also a little ambivalent relationship with types, correctness etc. this might be interesting to think or talk about. Is this an argument against types as a form of asserting static correctness?
Not sure, but I've come to feel types are only part of the solution some of the time, whereas a spec is more complete and often includes a type, among other things.
But not to be overlooked, there is a history of irony in the literature of "a poor man's" (whatever). See https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent.
In my many years of C++ & Java, the overwhelming use of types allowed people to select the right type from auto-complete to make the code compile. Then they would spend the rest of the day / week making it the code actually do what it was supposed to, by understanding the values contained within those types. Types were often a crux that would limit understanding in the race for expediency. This expediency driven by managers and executives with unrealistic goals (often promising things without asking the team), or companies not clearly defining / understanding their own value proposition (or changing it daily) To me, Type inference does make the experience of coding a solution a more enjoyable and effective experience. I also think it helps avoid over typing a solution, especially with inheritance, ultimately leading to code bases too complex to maintain effectively With dynamic typing there is an opportunity to focus more on what the code should actually do. Personally I find this helps attain a deeper understanding of what the code should be doing, so I can deliver software that does what is needed. In the larger scheme of things, I think static or dynamic type system is quite a minor discussion point (especially in the era of JIT compilation). Dynamic typing is a no-brainer for me, it's simpler and I achieve more. I like the separation of a specification library, e.g. clojure.spec or Malli, as they can be applied pragmatically. Although again specifications are only a small part of understanding and defining a solution that delivers value.
Thing about the type system in Clojure is "YAGNI". In the vast universe of bugs a simple type system (like Java's) solves such a tiny percentage of bugs it's not even worth the effort (and added verbosity) of typing out the type signature. Let's take some code
(defn compute-new-balance ^float [^float balance ^float amount ^float fee ^float exchange-rate]
...)
What percentage of important/worthwhile bugs has adding type signatures achieved? Basically near 0%.In the Clojure world, the ability of Jepsen to http://jepsen.io/analyses untested claims of correctness in distributed systems (many of which are built with the help of static types) serves as very strong evidence for the claim that runtime behavior of a system cannot be guaranteed by compile-time properties. I recommend Hillel Wayne's newsletter if you're new to the idea of model checking. He communicates the https://buttondown.email/hillelwayne/archive/the-best-model-checker-is-your-head/ behind it quite well.
^ static typing is largely about a particular IDE driven development experience. auto-complete, fix all the red lines where types mismatch, compiler won’t go until you do -- often aligned with testing and deployment as the only ways to check what the code is doing while running. dynamic typing + periodic spot evaluation, whether w/repl driven dev in clojure or eg hot reloading in js or eval-ing lines or blocks of python is IMO a faster way to iterate towards the desired behavior of a system. I think the market proved this out from ~2005-2015 or so, when js, python, ruby and even php ate enterprisey Java and C#’s lunch in the ‘new product creation’ world. then those companies ended up w/big code bases that wouldn’t fit in any of their devs’ heads, and the need to treat developers as fungible, which is where the static typing + OO world shined, now we get to live with typescript as the norm for js lib dev and mypy/static typing all over the place in python. proponents talk about static typing like it’s the only way to achieve a certain kind of correctness — but it either helps not at all or only very, very trivially in that space — you can get to just fine w/dynamic langs w/the use of formal methods, static analysis, etc. it’s good to be careful about not treating all static typing as equal, though. my recurring joke is that rust is the best argument for static typing (rust’s kind) and against all other static typed langs (where the static types give you literally 0-1 of the benefits you get from rust’s type system (memory safety, tricky concurrency, purity and functional programming constraints, really fast code…)
The post itself is a type error, because I expect all "X is a poor man's Y" statements to have a deeper hidden meaning
I consider types to be a form of documentation. They're true purpose in modern languages is to provide quick information to the developer on what they're working with. But the cost is the time spent to manage the types. To me, it really comes down to the amount of time it takes to use a function properly. With REPL driven development, you need to take some time to try functions in the repl to fully understand them, and if this takes more time than the time taken to manage types, then types are better. If it takes less time, then types are unnecessary. Type Inference is supposed to reduce the amount of time spent on managing types, by letting your editor do the work for you. If Clojure's error messages were clear and easy to use to trace problems to the source, I would wholeheartedly argue that typing is unnecessary, but so long as I still see IFn
in a Clojure error message, I can't say that with confidence.
> With REPL driven development, you need to take some time to try functions in the repl to fully understand them, and if this takes more time than the time taken to manage types, then types are better. If it takes less time, then types are unnecessary.
this assumes that the time spent in the REPL is equivalent to the time spent adding types with respect to learning about what a function does and how to use it. which IMO is the whole point — it doesn’t, the same time spent at the REPL tells you significantly more. To quote donald knuth: “Beware of bugs in the above code; I have only proved it correct, not tried it.”
It’s also amusing to me that you mention IFn
a type (albeit a fairly generic one, which I’m sure is why you bring it up). One of the pains of the Java typing system historically is that eg interface casting for classes is limited in what it can guarantee at compile time to exclude runtime errors.
static types are a tool to cope with complexity in a shared codebase. there are other ways to cope with this complexity, and clojure uses some of those. these coping strategies don't need to eliminate all bugs in order to be useful.