<div dir="ltr"><div dir="ltr">Andres,<div><br></div><div>(trying again... hit the wrong key)</div><div><br></div></div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Nov 5, 2019 at 1:15 AM Andres Valloud via Cuis-dev <<a href="mailto:cuis-dev@lists.cuis.st">cuis-dev@lists.cuis.st</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">I'm more familiar with how a message such as #receive would be <br>
implemented in some socket class, than how applications tend to write <br>
code that uses that functionality.  So, suppose you had a method like this:<br>
<br>
doTheHttpThing<br>
<br>
        | data |<br>
        self prepareToDoTheHttpThing.<br>
        data := [self socket receive] on: SocketError do: [:ex | ^nil].<br>
        data preprocess filter blah blah.<br>
        ^data<br>
<br></blockquote><div><br></div><div>Many times, you're not dealing with a single http request, but a series of interrelated requests that represent a conversation.  This is somewhat subjective in terms of how one answers the question 'when does it make sense to break this out into it's own method?' but I might end up with something like:</div><div><br></div><div>doTheHttpThing<br>    | intermediateData almostDoneData encounteredBlah |<br>    self prepareToDoTheHttpThing.</div><div>    encounteredBlah := false.<br>    intermediateData _ [ <br>    self httpGet: primaryUrl ]<br>        on: SomeAllowableError<br>        do: [ :ex |</div><div>            ex isExpected</div><div>                ifTrue: [</div><div>                    encounteredBlah := true.</div><div>                    self httpGet: alternateUrl ]</div><div>                ifFalse: [<br>                    "Something is probably wrong on the server, ignore it and move on"<br>                    ^ nil ] ].<br>    almostDoneData := intermediateData preprocess filter blah blah extractDataWeReallyCareAbout collect: [ :eaElement |<br>        [ self httpGet: (self makeSecondaryUrlWith: eaElement) ]<br>            on: SomeOtherAllowableError<br>            do: [ :ex |<br>                ex meh<br>                    ifTrue: [ <br>                        "Yeah, this happens from time to time.  Maybe we should handle it better, maybe not.  For now, whatever, the data is *probably* mostly good..."<br>                        someBadResultToken ]<br>                    ifFalse: [<br>                         encounteredBlah<br>                             ifTrue: [ "OK, this is related to issue #1234 and we got  garbage data back.  Can't do anything but file a bug report to the service and hope they fix it... so far they haven't.  Dump the data and move on..."</div><div>                                 self processingLog: 'Note #1234'.<br>                                 ^ nil ]<br>                             ifFalse: [ "This should not have happened..."<br>                                 ex pass ]]]].<br></div><div>    almostDoneData ... <and so on></div></div><div class="gmail_quote"><br></div><div class="gmail_quote">This may go on a couple levels deeper, and may be wrapped by session management etc, but you get the idea: a sequence of related requests that form a one-off conversation.</div><div class="gmail_quote"><br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
So now the problem that the on:do: is dealing with is what happens when <br>
the socket disconnects from under the client code.  Then #receive fails, <br>
there is no recourse, and the answer should be nil.  One could do the <br>
wrap around like this:<br>
<br>
        data := [self socket receive] on: SocketError do: [:ex | nil].<br>
        data isNil ifTrue: [^nil].<br>
<br>
and I'd be tempted to write it that way now because who knows that ^nil <br>
does today.  But suppose that no, that one would rather not have extra <br>
statements.  Then one might want something like [:ex | ex methodReturn: <br>
nil] instead.<br>
<br>
In this case, why couldn't that be written like this?<br>
<br>
        | data |<br>
        ^[<br>
                self prepareToDoTheHttpThing.<br>
                data := self socket receive.<br>
                data preprocess filter blah blah.<br>
                data<br>
        ] on: SocketError do: [:ex | nil]<br></blockquote><div><br></div><div>Sure, I can't think of a case where you *must* use a non-local return in an exception handler.  It's an intent/stylistic/convenience thing.  It's similar to how it used to be important to some to optimize functions for tail call optimization.  Potentially a big performance win... but (re-)structuring your code for it could be a pain.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Now there's no non-local return as far as exceptions are concerned, and <br>
by the way the code is faster in all cases because non-local return is <br>
expensive.<br></blockquote><div><br></div><div>That would be premature optimization.  When you're dealing with network requests (which often take on the order of tens to hundreds of milliseconds) and putting together/processing the results (which can take even longer), the cost of non-local returns is not even rounding error.  But your larger point is taken and ties in to something else below...</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Once I wrote a multi-process web spider, and I remember writing the code <br>
so that the actual networking interaction happened in a very small place <br>
that could be controlled easily with patterns such as the above.  Is the <br>
problem that application code doesn't always factor that nicely to allow <br>
that implementation strategy?  I do not have enough of a sample to tell.<br></blockquote><div><br></div><div>The short answer is yes.  The main difference is that handling an isolated http request is a nicely compartmentalized, widely used, bit of functionality.  Application logic tends not to be... and there's a lot of it winding down a bunch of essential, but rarely followed, paths.  That code often ends up utilizing #on:do: in very situation-specific ways.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> For example, I have a periodic task which involves ~10k http requests.  <br>
> A couple hundred of those will fail each run for various reasons: <br>
> network errors, server down, server errors, page errors etc.  There are <br>
> exception handlers at different levels of connection/request handling <br>
> that decide if the error is something that needs to be specifically <br>
> handled / retried or if terminating the request (or perhaps it was <br>
> terminated on us) and returning some specific value to the sender is the <br>
> appropriate result.  These are almost always non-local returns since <br>
> we're failing right in the middle of processing a request which is no <br>
> longer valid.<br>
<br>
I suspect this is one of those cases where the code just doesn't factor <br>
nicely, right?<br></blockquote><div><br></div><div>Right, or it might not be worth factoring.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> 3) FFI code and dealing with the 'outside world' more generally.  <br>
> Similar situation as 2: something fails either in an expected place or <br>
> in an expected way.  The current method can't continue but the sender <br>
> can.  Examples: a non-critical log file or database can't be found or <br>
> has a problem... (maybe) log something to transcript and return from the <br>
> method in question.  A backup file can't be copied to a secondary backup <br>
> location... we probably aren't connected to the network so ignore it and <br>
> try again the next time we get called.  I have several of these: fatal <br>
> to the current method but the sender doesn't care.<br>
> <br>
> The thing all of these examples have in common is that they are all <br>
> essentially resumable, fatal exceptions... that need to resume somewhere <br>
> else.<br>
<br>
Maybe more like critical exceptions that are not fatal simply because of <br>
the context in which they occur.<br></blockquote><div><br></div><div>Well usually it's critical and fatal to the code that raised it (i.e. since if failed back to us) and the decision I see the non-local return as making and signaling is 'yes, it is fatal to us as well... but the sender should be able to proceed.'  To me that's an important bit: signaling to the exception framework how the exception was resolved.</div><div><br></div><div>Related to your earlier point about non-local returns being expensive: it is entirely possible that for some applications, they impact performance significantly.  In those cases, I could see being able to instrument the exception framework to provide statistics about how exceptions are resolved could be useful and even help identify where you should focus your optimization efforts.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
You know, it just occurred to me that having such exceptions implement a <br>
#defaultAction method isn't really useful, either.  Suppose you could <br>
arrange the code so that the non-fatal yet critical exceptions were <br>
modeled by a particular class.  Instead of a million exception handlers <br>
everywhere doing something like<br>
<br>
        [...] on: NonFatalIOError do: [:ex | ex methodReturn: nil]<br>
<br>
one would like #defaultAction to do that.  However, #defaultAction <br>
cannot do that now because a non-local return in the #defaultAction <br>
method of the exception does not do what is required.<br>
<br>
If exceptions implemented #methodReturn: instead, then it might be <br>
possible for #defaultAction to be written like this:<br>
<br>
NonFatalIOError>>defaultAction<br>
<br>
        self methodReturn: nil<br>
<br>
This is not perfect yet, and one still needs to mark the return point <br>
with on:do:, but maybe there's a way to write even less code here.<br></blockquote><div><br></div><div>Interesting idea.  We'd have to play around with an implementation in the wild to get a feel for how that would actually play out.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Andres.<br><br></blockquote><div><br></div><div>Thanks,</div><div>Phil </div></div></div>