1.  There's no standard way to return "predictably" bad result.

Given that the examples you provided are from typed languages and Clojure isn’t 
typed, what would satisfy you here? A “recommendation” from Cognitect that such 
success+result/failure+error-info situations use certain specific keys in a 
hash map for the overall return value? A new clojure.<namespace> that includes 
a spec for such a map and helpers for the ok/error cases?

I’m genuinely curious as to what you (and other folks) would like to see added 
to Clojure to satisfy this “need”.


  1.  The standard library throws exceptions even when the error might be 
conveyed as the result of the call.

First off, a nitpick: Clojure doesn’t have a standard library. There’s “the 
namespaces that ship in the org.clojure/clojure dependency” and then there’s 
“the various Contrib libraries”. The Clojure/core folks control the former but 
could only make recommendations regarding the latter (although, if a core 
change added a “standard” way to report s+r/f+e-i, then maybe Contrib authors 
would migrate to that – but see caveat below).

Second, as I indicated in my previous response, I think that code that throws 
exceptions for a perfectly predictable error situation is wrong – in most 
cases. Clojure.core/read is an interesting example because, by default, if it 
hits EOF, it throws an exception but it allows you to override that behavior 
and, instead, return a value at end of file. Normally, I’d say that default 
behavior is wrong: EOF is a predictable failure mode – but this is a case where 
it is most convenient to have read return the value that is read and there is 
no universal value it can return to indicate EOF since it can read and return 
any valid Clojure value (including nil or any namespace-qualified 
keyword/symbol etc). This is a case where the simplest possible API is to have 
read always return (just) the value it read on the happy path but to throw an 
exception if EOF is encountered during reading. It’s simpler because Clojure is 
a dynamic language without types like Either, Result, etc. Your choices are 
either force all users of read to deal with s+r/f+e-I map values and decoding 
or to force all uses of read to deal with exceptions.

I’m curious as to what you’d have clojure.core/read do here? (and feel free to 
point me at other Clojure functions that throw exceptions where you think 
returning s+r/f+e-i would be “better” – happy to look at several different 
cases!)

Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

________________________________
From: clojure@googlegroups.com <clojure@googlegroups.com> on behalf of Oleksii 
Kachaiev <kacha...@gmail.com>
Sent: Friday, October 26, 2018 3:02:54 PM
To: Clojure
Subject: Re: An Error spec?

Sean, that's what I'm actually talking about.

1. There's no standard way to return "predictably" bad result. I don't know 
what you meant with "documented way through its return value". There's no 
Either with Left/Right (Scala, Haskell), no Result with Ok/Error (Rust), 
there's no "second value with an error" (Go) etc etc etc. You have to come up 
with your own way to distinguish bad and good result. And they will be 
different for different libraries. And that's right the problem I've stated in 
my message. No unification means manually work in each use case.

2. The standard library throws exceptions even when the error might be conveyed 
as the result of the call. Probably because of #1 - there's no way to express 
that.. Well... except exceptions. So, we're running circles here.

BR,

On Saturday, October 27, 2018 at 12:48:27 AM UTC+3, Sean Corfield wrote:
For any library – for any function – there are always two classes of unhappy 
path:


  1.  Expected, known failure modes.
  2.  Unexpected, exceptional failure modes.

The former should not use exceptions. The library/function should signal the 
error in a documented way through its return value. Calling code should check 
the return value to see if the library/function failed in one of the expected, 
known, documented ways it is known to be possible to fail in, and respond 
accordingly.

The latter can (and should) use exceptions. An exception says “I got into a 
state I can’t handle because I wasn’t expecting to get there!” and maybe the 
caller can handle that and maybe it can’t. Library/function authors can help 
callers here by:


  1.  Providing a clear but succinct message for the exception,
  2.  And providing as much potentially useful detail in the ex-data as 
possible.

Does Java (and its standard library) overuse exceptions? Yes, absolutely. It 
throws exceptions for all sorts of completely predictable failure modes. We 
don’t need (or want) to be Java.

Clojure provides perfectly good features to support both the expected and the 
unexpected failure modes and, in particular, provides an excellent way to 
convey information about the point of failure even when our code doesn’t know 
how to recover.

As Alex says, there may be value in providing a spec in your library for the 
sort of ex-data you provide around exceptions. You’ll already be in “regular 
Clojure land” as far as functions that return values that may indicate success 
or expected, known failure modes.

Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

________________________________
From: clo...@googlegroups.com<javascript:> 
<clo...@googlegroups.com<javascript:>> on behalf of Oleksii Kachaiev 
<kach...@gmail.com<javascript:>>
Sent: Friday, October 26, 2018 2:28:35 PM
To: Clojure
Subject: Re: An Error spec?

I've got the idea that you're not going to including anything like this into 
core library. I just want to clarify because I'm actually a bit confused here, 
and I think I'm not the only one. We promote doing functional programming, 
staying declarative when possible, using data with small pure testable 
functions as much as we can... and at the same time declaring the "official" 
way of handling errors using exceptions, which are side-effects by their 
nature, even tho' they play really poorly with:

* laziness (which is a default behavior for most operations with most 
collections in the language)

* multi-threaded code (especially in case of "opaque" jumps between 
threads/executors when using core language concurrency primitives, or even 
trying to emulate async event loop, i.e. with core.async)

* macros (often macroexpand screws up the only feature we love about 
exceptions: traces, making them pretty much useless)

I thought that the design approach of using data and staying declarative should 
also be applied to errors handling. And a contract declared for a function 
should reflect not only "the happy path" but all potential cases. We see a lot 
of languages put some mechanics into the core library or language design (i.e. 
Scala, Rust, Haskell, Go etc) because errors and errors handling is a very 
significant part of our programs that we just cannot ignore. You can like or 
dislike them, you can always come up with something very specific for your 
application or library. But the key idea here is that core functionality is a 
rule of thumb for libraries & ecosystem in general. So, when I do pick up 
library I can assume by default the way errors are handled. Most probably my 
code and libraries that I'm already using would play nicely with each other. 
Which is not the case in Clojure. As a creator of a few Clojure libraries, I 
always have to design upfront what my library will do with errors, either with 
my own implementation or embracing/adopting one of the existing libraries to 
handle control flows. And each time I'm struggling with the choice because I 
know perfectly well that a) most other libraries in the ecosystem would not be 
aligned with it automatically, b) most applications/users will be forced to 
learn how to deal with the control flow in this specific case. More libraries 
you use = more cases of how errors are handled here and there = more time you 
need to teach them to talk to each other. It seems to me that the "write about 
your exceptions in the documentation and pray all users of your code will read 
that carefully and think through really hard" approach (which is the state of 
the art right now) makes ecosystem of the language & libraries more fragile and 
more fragmented than it might be.

Thanks,

On Friday, October 26, 2018 at 10:42:13 PM UTC+3, Sean Corfield wrote:

I would likely only spec the status 200 OK responses. We use 400-series status 
values when we send back an error. You might consider that to be the 
“exception” of the HTTP world 😊



We actually do have a documented format for 400-series responses but pretty 
much any part can be omitted so callers might occasionally not be able to 
ascertain a reason beyond “it failed”…



Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood



________________________________
From: clo...@googlegroups.com <clo...@googlegroups.com> on behalf of Didier 
<did...@gmail.com>
Sent: Friday, October 26, 2018 11:35:20 AM
To: Clojure
Subject: RE: An Error spec?

Sean, if you were to Spec your API responses, what would you do for your error 
response?

This is my issue. I operate in a distributed environment. If I produce a set of 
data, but one field failed to compute properly, maybe a downstream system was 
down, maybe some information I was given to compute was corrupted, or missing, 
etc. And say this producing service has no user facing component, failing it is 
not logical. So I need to publish the partial result, and the error field 
should indicate an error. In my case it publishes a document entry in a nosql 
datastore, and events about it.

Now, some other system will eventually consume that document, to display it to 
the user. When it does, it must appropriately handle the fact that some fields 
were in error.

My documents are fully specced. So that consuming services can easily know 
their shapes and structure, so they can be sure to support them fully.

In such scenario, exceptions aren't useful, but only because Java exceptions 
are crap at serialization. So I need to do the same thing you are, marshal my 
exception into an error and serialize that into my document. Then I spec the 
field appropriately.

Now, I feel a lot of people using Spec would have such a use case, as its a 
logical tool to model data at your boundaries, and so I felt it might make 
sense to offer a spec macro for it.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com<javascript:>
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com<javascript:>
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+u...@googlegroups.com<javascript:>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to 
clojure+unsubscr...@googlegroups.com<mailto:clojure+unsubscr...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to