This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # aleph (1)
- # announcements (1)
- # aws (11)
- # babashka (117)
- # beginners (34)
- # calva (13)
- # cider (3)
- # clj-commons (8)
- # clj-kondo (24)
- # clj-yaml (36)
- # cljsrn (45)
- # clojure (50)
- # clojure-australia (5)
- # clojure-europe (239)
- # clojure-nl (3)
- # clojure-norway (3)
- # clojure-spec (16)
- # clojurescript (25)
- # core-typed (20)
- # cursive (41)
- # datahike (1)
- # datalevin (1)
- # datomic (17)
- # fulcro (27)
- # hyperfiddle (35)
- # introduce-yourself (1)
- # jobs (4)
- # lsp (20)
- # malli (8)
- # meander (8)
- # nbb (1)
- # off-topic (31)
- # parinfer (9)
- # pathom (3)
- # portal (2)
- # re-frame (20)
- # react (2)
- # reagent (8)
- # releases (1)
- # remote-jobs (4)
- # scittle (2)
- # shadow-cljs (8)
- # slack-help (4)
- # sql (30)
- # squint (3)
- # tools-deps (34)
- # xtdb (21)
Working my way through a tutorial, I came across an empty anonymous function
(fn ). It was tucked into the end of an
or form, presumably to provide a default result if the first part of the
or didn't return anything. Playing around with
(fn ) in the repl, I see that it just returns
nil. That's all pretty clear to me, but I'm wondering if there's a reason to prefer that over something perhaps a little more explicit, such as
(constantly nil) ?
(constantly nil) is definitely more explicit about what is returned, but they might not actually care about the returned result of the function, but maybe invoking for side effects, where
(fn ) is a function that does no side effects
I was thinking of arities.
paused-repl=> ((fn )) nil paused-repl=> ((fn ) :a) Execution error (ArityException) at metabase.api.dashboard/eval193577 (REPL:1503). Wrong number of args (1) passed to: metabase.api.dashboard/eval193577/fn--193578 paused-repl=> ((constantly nil) :a :b :c :d :e) nil
(fn )is more explicit about what arguments it can accept: none.
(constantly nil)can take arbitrarily many arguments.
(constantly nil) is also that, but from that view point which one is more explicitly that is less clear
for places that invoke no arg functions and don't care about the return value, I've been known to just use
+ which is not clear at all, in any way
haha. i love the
+ trick but i would have to run
git blame to figure out the intention if i wasn’t familiar with the idiom
Is it fair to say that if there’s no way you’ll be exploiting hierarchies, there’s little advantage to a multimethod over a fn with equivalent conditional code?
so if someone consumes my code he can extend the multimethod but would have an ugly time wrapping the equivalent fn
Hi everyone, I have been playing with clojure for a little while now and it’s really cool, but there is on topic I am very unsure about it, what’s the typical way to do error handling in clojure? Is just exceptions as in Java or is there any library that most of the community uses, I just would like general tips on error handling as I am having a hard time figuring out the best way to do.
I played arround with https://github.com/funcool/cats, but I have to say I am not quite sure yet how to handle the results that some functions give me.
But you will also see code that returns
nil for a failure since
nil-punning is idiomatic too.
I assumed exceptions are too side effectful and Clojure usually tries to have pure functions (where possible)
You will sometimes see code that returns a hash map with either error-related keys or result-related keys. It depends on how your domain works.
We have an example of this at work where we have several pipelines of photo processing code and they pass a hash map through the entire pipeline with
:valid true indicating the data is still valid and the processing step should continue, or
:valid false indicating an error has been detected and subsequent steps should not perform their work.
On the JVM, you'll interact with a lot of core Java library code that throws exceptions so you can't avoid them in many cases.
In addition, Clojure has
ex-info to create information-carrying exceptions that you can throw (and the
catch can call
ex-data to get that information back).
In general, I try to draw a mental line between "expected" failures and "unexpected" failures. For the former, these are errors that you could handle via
nil or an error/result hash map for example. For the latter, I'd consider those good candidates for exceptions.
For exceptions, I also try to either handle those very locally or just let them bubble all the way to the top (for a generic exception handler to deal with).
For errors, those might bubble up the call chain a bit but something is expected to handle those (and possibly retry an operation, or perform an alternative operation).
Unfortunately, some exceptions from Java libraries indicate "expected" failures that may indicate a retry is possible or that you are just meant to catch as part of normal data processing.
One issue I see with returning nil to represent an “expected” error is that sometimes a function can have more than 1 type of “expected” error to check, but in this case I guess I could use a error/result hash map like you explained
Thank you for the detailed explanation 😀 it was very helpful, I definitely have a better overview now.
Adding to @U04V70XH6: when choosing to bubble up that category of unexpected failures all the way up and handle them there, the purity of the pure functions through which those exceptions pass is virtually unaffected. Just like some OS related memory error that terminates your script doesn't make those functions less pure, I feel something can be said for viewing them as pure as well even though some exceptions passes through to be handled at the top. I'm trying to say that with this strategy consistently in place, from the POV of your pure functions you can pretty much ignore those exceptions in your reasoning about those functions. Hope that make sense and it is clear 🙂. Also, if people disagree I would love to hear and learn
yeah that actually makes sense, it’s just that exceptions somehow have a bad reputation (at least from what I heard), so somehow it just feels wrong to have exceptions bubbling up but because they way clojure handle return values, in the end it does not matter at all, you can still reason as if the functions are really pure
I'm curious where and what you've heard about exceptions being "bad"? I suspect people's feelings about exceptions are shaped by the languages they've used previously...
When learning about functional programming, I also read a lot of trash talking of exceptions, so I think I know the feeling @U027MB6K8RM has. But I think you're right that it's all about how they are used and what your language offers and prescribes as idiomatic.
I mostly heard that errors should be just values and not whole classes/objects like exceptions are, but in Clojure they are actually treated as values if I understood correctly.
clojure mostly throws java exceptions. https://insideclojure.org/2018/12/17/errors/ clojure errors seem to be pretty new. spec added a lot of error messages as well, which is also pretty new.
I think you're misunderstanding that article. Those errors have been around a long time. Clojure 1.10 just improved them in terms of the information provided.
And mostly that's about tool chain support, not the actual errors themselves (for example, nREPL-based tooling continued to display those errors the way Clojure 1.9 and earlier did).
All of the "errors" in that article are actually exceptions -- thrown by the compiler (or by the runtime, for the latter part of the article).