3 ms·
> if something is expected then return either nil or some {:ok false :message "..."} value (and {:ok true :value ...} for success) Maps used in this way are un
by roenxi 2y ago
> if something is expected then return either nil or some {:ok false :message "..."} value (and {:ok true :value ...} for success)
Maps used in this way are uncomfortable. You end up with a function (foo x y z) and in practice you don't know how may values it is about to return. Technically one, but that one might be a map with who-knows-what in it.
There is a general API problem here of how to handle operations which really require multiple communication channels to report back with. I'm not sure if there is a good way to handle it, but complex objects as return value isn't very satisfying. Although in practice I find it works great in exceptions because the map is secretly just a string that is about to be logged somewhere and discarded.
- MarkMarine 2y agoMaps used in this way are clojure idiom. https://softwareengineering.stackexchange.com/questions/272302/use-map-instead-of-class-to-represent-data-rich-hickey https://softwareengineering.stackexchange.com/questions/2723...
- worthless-trash 2y ago> but that one might be a map with who-knows-what in it. I think thatthe beauty of maps, you can take the parts you need and discard the rest (I am suddenly reminded of my old shifu).
- IceDane 2y agoIn a dynamically typed language, you can't even be sure you have the parts you need.
- Capricorn2481 2y agoYou check the keys.
- diggan 2y agoIf you'd say "you can't even be sure you have the parts you have", it kind of makes sense, but the way we (Clojure devs) commonly use the REPL-in-editor kind of makes that moot. But as it stands, I'm not sure I understand what you mean?
- worthless-trash 2y agoI.. know which parts I need, thats a very abstract thought pattern, what do you mean ?
- socksy 2y agoJust because it’s dynamically typed, doesn’t mean there aren’t types. Additionally, usage of runtime type enforcement such as malli schemas and core.spec are commonplace.
- MathMonkeyMan 2y agoNot idiomatic, but it looks like [core.match][1] could be used to "if error" unpack the map: (let [result (do-thing x y z)] (match [result] [{:ok false :message msg}] {:ok false :message (format "do-thing failed: %s" msg)} [{:ok true, :value value}] (continue-computation-with value))) meh [1]: https://github.com/clojure/core.match/wiki/Overview https://github.com/clojure/core.match/wiki/Overview
- roenxi 2y agoWhat is the advantage there that justifies bringing in another dependency? You've already got a let for binding parts of result and could use cond from clojure.core instead of match. It'd be effectively identical.
- MathMonkeyMan 2y agoThe point of `match` is you can combine the destructuring with the conditional. It's also why Rich Hickey dislikes it.
- dustingetz 2y agoi don’t think core.match has worked out in practice for many prod projects for subtle reasons, there’s a je ne sais quoi about it that seems not quite right
- kokada 2y agoDo you have a list of those reasons? I find it curious that I really enjoy `match` in both Scala and Python. While it can be argued that Scala is a completely different beast than Clojure, Python is much closer (in the sense that both are dynamic typed languages).
- socksy 2y agoUnfortunately (IMO), `core.match` in Clojure is a macro provided by a library you have to install, rather than a builtin function as in Scala or Python. It’s a really cool demonstration of the power of lisps, since macros basically let you edit the code at compile time, rather than at runtime, and match is maybe the most extreme example of this as it’s really compiling down the match statement into completely different code, rather than treating it like a cond statement (more info on the match algorithm here: https://github.com/clojure/core.match/wiki/Understanding-the-algorithm https://github.com/clojure/core.match/wiki/Understanding-the...). However, when using macros there’s always some trade-offs — for example, you usually can’t treat them as a first class function, passing them around (although I’m not sure if that’s true for core.match tbf). Additionally, they can be confusing to debug because the code that you’re writing doesn’t match the code that’s actually being run… stack traces can be particularly weird. Finally, by not being a builtin, it doesn’t really feel blessed as a language feature, and if it’s a choice between case, condp, or cond, I’m going to reach for one of those before core.match, simply because I don’t have to add a new library, I can be sure that I’ll understand the stack traces, and most importantly I can be sure that others will understand the code better. The fact that destructuring is built into Clojure means that you can get sort of half of the use cases for match in the first place, so it ends up being quite rare when you’ll actually reach for it. I’m not sure exactly what Dustin was referring to exactly, but those are my gripes with it. I think it’s a shame, because `match` is a more powerful construct every time I’ve encountered it in other languages, and it’s one of the few things I really miss as a built in part of Clojure.
- eduction 2y agoNot really, you just have to check your return values, and it is trivial to write a macro to make this more convenient (if-success, success->, etc). Checking return values for errs is a common idiom even in languages without this affordance.