<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40"><head><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=Generator content="Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:"Malgun Gothic";
        panose-1:2 11 5 3 2 0 0 2 0 4;}
@font-face
        {font-family:"\@Malgun Gothic";}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:#954F72;
        text-decoration:underline;}
.MsoChpDefault
        {mso-style-type:export-only;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style></head><body lang=EN-US link=blue vlink="#954F72"><div class=WordSection1><p class=MsoNormal>Personally, I have been using ‘safe_’ notation as ‘must_’. </p><p class=MsoNormal>For ‘may_’, I just use regular function name. For example, safe_format_message vs format_message.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>However, those ‘safe_’ functions are private functions in the module since it looks ugly. If some of the functions are required to do strict error checking then I put wrapper around ‘safe_’ function.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I wasn’t that much of favor on generic exception handler because sometimes I prefer let program to crash before GA the code. </p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Sent from <a href="https://go.microsoft.com/fwlink/?LinkId=550986">Mail</a> for Windows 10</p><p class=MsoNormal><o:p> </o:p></p><div style='mso-element:para-border-div;border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in'><p class=MsoNormal style='border:none;padding:0in'><b>From: </b><a href="mailto:essen@ninenines.eu">Loïc Hoguin</a><br><b>Sent: </b>Wednesday, September 27, 2017 3:46 AM<br><b>To: </b><a href="mailto:erlang@gmail.com">Joe Armstrong</a>; <a href="mailto:erlang-questions@erlang.org">Erlang</a><br><b>Subject: </b>Re: [erlang-questions] Must and May convention</p></div><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>On 09/27/2017 11:08 AM, Joe Armstrong wrote:</p><p class=MsoNormal>> For several years I've been using a convention in my hobby</p><p class=MsoNormal>> projects. It's what I call the must-may convention.</p><p class=MsoNormal>> </p><p class=MsoNormal>> I'm wondering if it should be widely used.</p><p class=MsoNormal>> </p><p class=MsoNormal>> What is it?</p><p class=MsoNormal>> </p><p class=MsoNormal>> There are two commonly used conventions for handling bad arguments to</p><p class=MsoNormal>> a function. We can return {ok, Val} or {error, Reason} or we can</p><p class=MsoNormal>> return a value if the arguments are correct, and raise an exception</p><p class=MsoNormal>> otherwise.</p><p class=MsoNormal>> </p><p class=MsoNormal>> The problem is that when I read code and see a function like</p><p class=MsoNormal>> 'foo:bar(a,12)' I have no idea if it obeys one of these conventions or</p><p class=MsoNormal>> does something completely different. I have to read the code to find</p><p class=MsoNormal>> out.</p><p class=MsoNormal>> </p><p class=MsoNormal>> My convention is to prefix the function name with 'must_' or 'may_'</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I've been debating this in my head for a long time. I came to the </p><p class=MsoNormal>conclusion that 99% of the time I do not want to handle errors. </p><p class=MsoNormal>Therefore 99% of the functions should not return an error.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>What happens for the 1% of the time where I do want to handle an error </p><p class=MsoNormal>and the function doesn't allow it? Well, I catch the exception. And </p><p class=MsoNormal>that's why I started using more meaningful exceptions for these cases. </p><p class=MsoNormal>For example, Cowboy 2.0 has the following kind of code when it fails to </p><p class=MsoNormal>validate input:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>     try</p><p class=MsoNormal>         cow_qs:parse_qs(Qs)</p><p class=MsoNormal>     catch _:_ <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>         erlang:raise(exit, {request_error, qs,</p><p class=MsoNormal>             'Malformed query string; application/x-www-form-urlencoded </p><p class=MsoNormal>expected.'</p><p class=MsoNormal>         }, erlang:get_stacktrace())</p><p class=MsoNormal>     end.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>99% of the time I don't care about it because Cowboy will properly </p><p class=MsoNormal>notice it's an input error and will return a 400 automatically (instead </p><p class=MsoNormal>of 500 for other crashes). It still contains the full details of the </p><p class=MsoNormal>error should I wish to debug it, and if it is necessary to provide more </p><p class=MsoNormal>details to the user I can catch it and do something with it.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>(The exception probably won't make it as a documented feature in 2.0 due </p><p class=MsoNormal>to lack of time but I will rectify this in future releases.)</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>This strategy also helps with writing clearer code because I don't need </p><p class=MsoNormal>to have nested case statements, I can just have one try/catch with </p><p class=MsoNormal>multiple catch clauses to identify the errors I do want to catch, and </p><p class=MsoNormal>let the others go through.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>     try</p><p class=MsoNormal>         Qs = cowboy_req:parse_qs(Req),</p><p class=MsoNormal>         Cookies = cowboy_req:parse_cookies(Req),</p><p class=MsoNormal>         doit(Qs, Cookies)</p><p class=MsoNormal>     catch</p><p class=MsoNormal>         exit:{request_error, qs, _} -></p><p class=MsoNormal>             bad_qs(Req);</p><p class=MsoNormal>         exit:{request_error, {header, <<"cookie">>}, _} -></p><p class=MsoNormal>             bad_cookie(Req)</p><p class=MsoNormal>     end</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Write for the happy path and handle all errors I care about in the same </p><p class=MsoNormal>place. Goodbye nested cases for error handling!</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I have also been using exceptions in a "different" way for parsing </p><p class=MsoNormal>Asciidoc files. Asciidoc input is *always* correct, there can not be a </p><p class=MsoNormal>malformed Asciidoc file (as far as parsing is concerned). When input </p><p class=MsoNormal>looks wrong it's a paragraph.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>I can therefore in that case simply write functions for parsing each </p><p class=MsoNormal>possible elements, and try them one by one on the input until I find a </p><p class=MsoNormal>parsing function that doesn't crash. If it doesn't crash, then that </p><p class=MsoNormal>means I found the type of block for the input. If it crashes, I try the </p><p class=MsoNormal>next type of block.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>So I have a function like this defining the block types:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>block(St) <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>     skip(fun empty_line/1, St),</p><p class=MsoNormal>     oneof([</p><p class=MsoNormal>         fun eof/1,</p><p class=MsoNormal>         %% Section titles.</p><p class=MsoNormal>         fun section_title/1,</p><p class=MsoNormal>         fun long_section_title/1,</p><p class=MsoNormal>         %% Block macros.</p><p class=MsoNormal>         fun block_id/1,</p><p class=MsoNormal>         fun comment_line/1,</p><p class=MsoNormal>         fun block_macro/1,</p><p class=MsoNormal>         %% Lists.</p><p class=MsoNormal>         fun bulleted_list/1,</p><p class=MsoNormal>         fun numbered_list/1,</p><p class=MsoNormal>...</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>And then one of those parse functions would be like this for example:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>comment_line(St) <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>     «"//", C, Comment0/bits» = read_line(St),</p><p class=MsoNormal>     true = ?IS_WS(C),</p><p class=MsoNormal>     Comment = trim(Comment0),</p><p class=MsoNormal>     %% Good!</p><p class=MsoNormal>     {comment_line, #{}, Comment, ann(St)}.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>If it crashes, then it's not a comment line!</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>The oneof function is of course defined like this:</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>oneof([], St) <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>     throw({error, St}); %% @todo</p><p class=MsoNormal>oneof([Parse|Tail], St=#state{reader=ReaderPid}) <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>     Ln = asciideck_line_reader:get_position(ReaderPid),</p><p class=MsoNormal>     try</p><p class=MsoNormal>         Parse(St)</p><p class=MsoNormal>     catch _:_ <span lang=KO style='font-family:"Malgun Gothic",sans-serif'>→</span></p><p class=MsoNormal>         asciideck_line_reader:set_position(ReaderPid, Ln),</p><p class=MsoNormal>         oneof(Tail, St)</p><p class=MsoNormal>     end.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>This allows me to do some parsec-like parsing by abusing exceptions. But </p><p class=MsoNormal>the great thing about it is that I don't need to worry about error </p><p class=MsoNormal>handling here again, I just try calling parse functions until one </p><p class=MsoNormal>doesn't crash.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>So to go back to the topic at hand, I would say forget about the </p><p class=MsoNormal>distinction between must and may, and truly embrace "happy path" </p><p class=MsoNormal>programming and make smart use of exceptions. Deal with errors in one </p><p class=MsoNormal>place instead of having nested cases/many functions. There are of course </p><p class=MsoNormal>other ways to do this, but only exceptions let you do this both in the </p><p class=MsoNormal>local process and in a separate process, depending on your needs.</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>(I will now expect horrified replies from purists. Do not disappoint.)</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>Cheers,</p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal>-- </p><p class=MsoNormal>Loïc Hoguin</p><p class=MsoNormal>https://ninenines.eu</p><p class=MsoNormal>_______________________________________________</p><p class=MsoNormal>erlang-questions mailing list</p><p class=MsoNormal>erlang-questions@erlang.org</p><p class=MsoNormal>http://erlang.org/mailman/listinfo/erlang-questions</p><p class=MsoNormal><o:p> </o:p></p></div></body></html>