<div dir="ltr">A discussion at work prompted me to think a bit more about this, so I thought I should share the following reasoning for posterity:<div><br></div><div>First, as a general principle we don't want patterns to carry a surprising cost, especially in the case they don't match. For example, it's possible to match on "abc"++Rest, but not on Prefix++"abc" or Prefix++"abc"++Rest, since that would imply a linear search through an arbitrary-length string. The same goes for binary patterns: you can't have a field of unknown length, except when it's the whole remainder of the binary.</div><div><br></div><div>For exceptions, the cost of building the symbolic form of the stack trace lies in 1) allocating and filling in list cells and tuples on the heap, and 2) traversing tables to map the program pointers on the actual stack to find the corresponding source file and line number. This is definitely non-negligible. When an exception happens, only the raw pointers from the topmost N stack frames are saved, which is pretty cheap, but if someone wants to look at this, they will need to get the symbolic form that get_stacktrace() returns (and which will in the future be available in the optional Trace variable of the exception pattern). If an exception happens but nobody looks at the stack trace information, only a single small binary is created and becomes garbage again.</div><div><br></div><div>The problem is not when the pattern matches, and you catch the exception. In that case you were probably willing to take the performance hit anyway, if you were matching on stack trace info. The problem is when someone introduces a seemingly innocent try...catch...end that is rarely expected to actually match, in a control flow where exceptions do happen a lot as part of the general execution - perhaps using throw for nonlocal returns out of deep recursion. If you have code like this, being called a lot, perhaps in a tight loop:</div><div><br></div><div>    find(Key) -></div><div>        try search(Key)</div><div>        catch</div><div>            throw:aborted -> []</div><div>        end.</div><div><br></div><div>that seems perfectly fine, right? Just a wrapper.  Maybe you use it in a call to flatmap() and you want empty lists instead of exceptions when nothing is  found. But now someone else tweaks the search() function (or you have made the search function a parameter, and someone passes a new fun) so that it does the following:</div><div><br></div><div>    search(Key) -></div><div>        try</div><div>            ...  % main body of search</div><div>        catch</div><div>            _Class:_Term:[{foobar,f,2,_} | _] -> []</div><div>        end.</div><div><br></div><div>This is also straightforward looking. Just a monkey-patch to fix a known error case returning an empty list instead. Expected to occur very rarely.</div><div><br></div><div>But what happens now, is that every time the called code throws 'aborted' - which might be often - the inner try/catch will be expanding the exception to its symbolic form, so that you can check whether it comes from a particular module/function. Even if this never actually matches, you will take the penalty of traversing the tables and allocating data on the heap. When the check has been made and the clause didn't match, the exception will be re-thrown to the outer try/catch which handles it instead as expected. The only observed difference is suddenly increased cpu usage and garbage creation.</div><div><br></div><div>We (famously) had a bug in our code some years back that made the system very unstable, garbage collecting a lot and being generally unresponsive. It was hard to diagnose, but it was just because of this anti-pattern, albeit implemented by calling get_stacktrace, ignoring the result and then rethrowing the exception explicitly. Making it even easier to accidentally cause this sort of problems by writing a simple little pattern in a catch-clause would be a worse thing than artificially restricting the expressiveness of catch-patterns.</div><div><br></div><div>That's why the Trace pattern should only be allowed to be an unbound variable.</div><div><br></div><div class="gmail_extra"><div><div class="gmail_signature" data-smartmail="gmail_signature">        /Richard</div></div>
<br><div class="gmail_quote">2017-11-27 15:31 GMT+01:00 Jesper Louis Andersen <span dir="ltr"><<a href="mailto:jesper.louis.andersen@gmail.com" target="_blank">jesper.louis.andersen@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><span class="">On Mon, Nov 27, 2017 at 3:22 PM Björn Gustavsson <<a href="mailto:bjorn@erlang.org" target="_blank">bjorn@erlang.org</a>> wrote:<br></span><div class="gmail_quote"><span class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Sat, Nov 25, 2017 at 3:17 PM, Jesper Louis Andersen<br>
<<a href="mailto:jesper.louis.andersen@gmail.com" target="_blank">jesper.louis.andersen@gmail.<wbr>com</a>> wrote:<br>
[...]<br>
><br>
> My major gripe with it is the fact that you cannot pattern match on the<br>
> stack trace.<br>
<br>
Yes, I don't like that inconsistency myself, but I<br>
think that the alternatives are worse.<br>
<br></blockquote><div> </div></span><div>Yes, I think so too. In a typed language, you would probably declare an abstract type for the stack and not provide any kind of matching pattern for it. This would force people to handle the stack by printing, and there would be no matching on it at all.</div><div><br></div><div>Mimicking this behavior in Erlang is probably the sane behavior in this case.</div><div><br></div>I also like Richard's point: matching on the stack will eventually get you into trouble.<br></div></div>
<br>______________________________<wbr>_________________<br>
erlang-questions mailing list<br>
<a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
<a href="http://erlang.org/mailman/listinfo/erlang-questions" rel="noreferrer" target="_blank">http://erlang.org/mailman/<wbr>listinfo/erlang-questions</a><br>
<br></blockquote></div><br></div></div>