Fork me on GitHub
Denis G00:12:57

interesting read nice to see your @borkdude thoughts there as well 🙂


@denisgrebennicov I think developers are either "static typing" people or they aren't. I've been a commercial developer for over 30 years and I spent about a decade working with C/C++ which don't have much of a type system, then about a decade working with Java/Groovy/Scala which do, and now close to a decade working with Clojure. Along the way, I've done a lot of work on source code analysis systems (starting with a type inference engine written in Prolog back in my PhD days). With all that experience, I can honestly say I'm not in the "static typing" camp these days.

Denis G00:12:41

@seancorfield interesting theory. I need to think about that for a while What about optional typing like Typescript? Is it like best of both worlds?


clojure.spec -based solutions tend to be a nice middle ground. You get strong API contracts, but no monads, generics, or similar rabbit holes. The same cannot be said of Typescript. Likewise many gradual/optional solutions (like Ruby Sorbet) are seemingly modelled after either Java or Haskell; that IMO is not particularly suitable for dynlangs (if I wanted Haskell I would choose Haskell in the first place)

Denis G00:12:38

doesn’t clojure.spec provide runtime checks only?


IMO in practice you do get a type system: in form of documentation, errors, coercions, etc And it achieves some things generally impossible in a static system, so it's a decent tradeoff

Denis G00:12:09

@U45T93RA6 any video of practical clojure.spec you could recommend I have some questions, but wanna do my homework first 😅


I think I learned spec gradually (how fit! ;p) Many resources, many approaches. I think someone even got to make music with spec? which I authored might be an interesting read. Its api resembles typed languages, with similar benefits (uniquely: performance). When focusing on :pre/:post, normally the assumption is that you have a traditional testing pyramid to exercise those defns' assertions. Not everyone thinks like that, defending instead to go all generative.

Denis G01:12:06

thanks 🙂

Denis G00:12:32

my “problem” with dynamic typing is error handling. I don’t think exceptions are good, because they are not defined on the level of method signature. your best bet is having javadoc or something above the method declaration. And I am not the only one. This is why the GoLang poor man error handling went viral. Because all ex-Java developers that were left frustrated with their apps crashing in production because of some things they didn’t anticipate, don’t have this problem anymore. e.g. from production got some data from the request, inserted some state into the hashmap, hashmap already has some value associated with this key → app crashes and throws exception. this is why I like Result (aka Either) type PS. retriable exceptions/errors inspired from common lisp are dope though. When I first heard of it, it blew my mind (apparently you call it algebraic effects nowadays) PPS. I guess it’s more of a rant over exceptions, but Clojure uses exceptions, so


Part of the problem is that "exceptions" is a pretty broad notion (programmer faults, business-facing validation errors, IO errors, ...). So having just 1 recipe can be a source of complexity

Denis G01:12:51

sure. however exceptions are the standard way of dealing with all kind of errors and afaiu clojure is inheriting this model


isn't at least some of that out of necessity for java interop? at least for jvm clojure


@ Denis: hmmm, my writing wasn't clear. Let me re-state: Part of the problem is that "error handling" is a pretty broad notion (programmer faults, business-facing validation errors, IO errors, ...).


With which I intended to say: I'm quite happy with the status quo (;pre for programmer faults; different shapes of data for validation, exceptions for IO) Exceptions should be exceptional, I think it's common wisdom ('"don't use exceptions for control flow"). So I don't worry a lot about their absence in method signatures; I hope to prevent hitting them through testing + QA + good engineering overall


@denisgrebennicov Exceptions should only be used for "exceptional" situations -- that you can't easily recover from. Error handling -- for "expected" errors -- should not use exceptions. I agree that some code, esp. some Java code, misuses exceptions based on that principle.

Denis G01:12:36

@seancorfield then how does one do it in Clojure. have a map with key :err in it? whab about ->> macro? having to check if :err key is there in order not to continue processing?


That's an interesting question. I think there's no universal answer: there are many ways to approach a "data processing pipeline" (->> is one) - concurrently / not? - abort in case of intermediate failures? Or salvage the 'good' results? - split the codebase between pure and impure parts, or follow something more imperative? The syntactic noise of having :err in the middle of a ->> might be superficial in comparison


Agree with @U45T93RA6 that there's no universal answer here. You pick what works best for the use case you're solving. Sometimes returning nil for the error case is enough and Clojure makes it easy to write code that is conditional on nil-punning. Sometimes a map containing a specific error key works and, again, conditional code based on the presence/absence of a particular key is relatively easy to write and fairly idiomatic.

👍 4
☝️ 5

A lot also depends on how much error information you need to convey to upstream consumers and how/if you are going to handle an error result in the flow of code @denisgrebennicov


(this is of course also one of the big challenges with Clojure: there is no One True Way(tm) for anything -- it's not Python, it's more like Ruby in that regard I guess)


@denisgrebennicov "Is [optional/gradual typing] like best of both worlds?" -- Perhaps. But idiomatic Clojure is extremely hard to annotate with static-style typing in any reasonable manner. I spent quite a while looking at using core.typed with parts of our code. I started with Prismatic's Schema (run time checking), then switched to core.typed (static checking), then Schema again, then core.typed again. And then I gave up -- core.typed was too hard to use and I found myself refactoring code purely to satisfy the type checker, and it ended up being less idiomatic, and harder to read; Schema didn't really add much benefit, for me, over my regular testing -- and it required good test coverage anyway to exercise the Schema checking at runtime (and I found Schema annotations just plain noisy in the code, making the code harder to read).


By contrast, Spec takes a rather different approach and can be used in many different ways, both in production code for data validation, and in testing for assertions on function arguments, function behavior (return values), data shapes... as well as generating example data and full-on generative testing.


I wrote about our usage of Spec a few months ago

Denis G01:12:21

@seancorfield hmmm. Interesting experience. I will read your blog post. Thanks for the link 🙂


I'm trying to understand this quote by Alan Perlis: "Recursion is the root of computation since it trades description for time." Do you know what he meant by this?

hindol20:12:22 Some more praise for LISP, in case you haven't had enough. All thanks to the Alan Perlis quote above, which sent me down the rabbit hole, :man-shrugging:

👍 8

Sorry about that 🙂


I'd heard of it but never really knew what it was. Lisp is so powerful that problems which are technical issues in other programming languages are social issues in Lisp.