This article is a discussion of if statements in the archaeological Erlang language.
grammar
First, how do we think about "if" semantics in Erlang? In the book "Programming in Erlang" written by Armstrong, it is clear that "case and if expressions" are put together in the same section, and the case is introduced first, and the if is introduced later. When introducing if, the given syntax example is as follows:
if
Guard1 ->
Expr_seq1;
Guard2 ->
Expr_seq2;
...
end
It can be clearly seen from this sentence pattern that the entire if statement is more like a case statement, which executes different clauses through a Guard. The if statement will execute each level. If the result is true, the subexpression will be executed and the end will be executed. If it is false, it will be matched downward until a level is true.
One thing to note here is that the entire statement must ensure that at least one level is true, otherwise the entire statement will throw an exception. Such a statement would be an exception under certain conditions:
if
A > 0 ->
do_this()
end
Unless you intentionally want the error to happen. Of course, an engineer familiar with coding knows that when any hidden intent is confused with possible mistakes, the entire piece of code becomes difficult to read and understand, and even if it is clearly marked with comments, it will be difficult to iterate over later evolutions. The recommended practice, and practice in various projects, is to use the atomic true in the last level, which is guaranteed to match the last subexpression. This is like the last default of case in Java.
if
Guard1 ->
Expr_seq1;
Guard2 ->
Expr_seq2;
...
true ->
Expr_default
end
decision table?
Looking at various Erlang documentation, examples in stackoverflow, and blogs, case will be recommended instead of if. Here is an archived email that clearly illustrates the status of if in the eyes of developers: http://erlang.org/pipermail/erlang-questions/2014-September/080827.html (also cited in the Erlang Coding Specification to illustrate the use case) .
Brief background: Richard A. O'Keefe (a researcher at the University of Otago who has published a number of computational linguistics research papers directly by name) supports the "smart" use of if-statements. The subject of the email he gave was "Praise for the if in Erlang". The description cites a paper that demonstrates the idea that "structured flowcharts are better than pseudocode", and traditional pseudocode is similar:
IF GREEN
THEN
IF CRISPY
THEN
STEAM
ELSE
CHOP
ENDIF
ELSE
FRY
IF LEAFY
THEN
IF HARD
THEN
GRILL
ELSE
BOIL
ENDIF
ELSE
BAKE
ENDIF
ENDIF
Logical confusion caused by common nested ifs. In Erlang, by cleverly using Erlang syntax, this piece of logic can be changed into the following pattern:
if Green, Crispy -> steam()
; Green, not Crispy -> chop()
; not Green, Leafy, Hard -> fry(), grill()
; not Green, Leafy, not Hard -> fry(), boil()
; not Green, not Leafy -> fry(), bake()
end
In essence, it uses semicolons, commas, and whitespace in Erlang to create a visual "decision table" that clearly indicates the conditions corresponding to each branch.
But does this type of writing make your nerves feel some kind of "wonderful trick"? Intuitively, our code should avoid all such tricky but difficult to understand/maintain code, unless this code is essential. Important performance optimization node. Moreover, in today's mature development of compilers, even what you think of as "performance optimization" will often become unrecognizable when compiling, and it must be tested by performance. Often, such optimizations you do can't compare to the compiler at all. optimization done.
back to simplicity
Anthony Ramine first pointed out the troubles caused by this strange indentation to programmers in his reply email.
Secondly, the mixed use of semicolons and commas is difficult to notice in this way, and even typos are difficult to be automatically detected, such as the following example he constructed (here the comma in the third branch is changed to a semicolon):
if Green, Crispy -> steam()
; Green, not Crispy -> chop()
; not Green; Leafy, Hard -> fry(), grill()
; not Green, Leafy, not Hard -> fry(), boil()
; not Green, not Leafy -> fry(), bake()
end
You can see that this kind of problem happens frequently in Erlang development: https://github.com/rebar/rebar/commit/2c4d7d1d9bdc2a11d3f485f844500bf4c2aa77a2 .
A comment on if in Erlang even goes to this point: https://stackoverflow.com/questions/4327860/erlang-equivalent-to-if-else
"I have found that if you are relying on guards or case statements, you are probably 'doing it wrong' most of the time in Erlang."
To this end, Anthony prefers to remove the level clause in the if statement, or even stop trying the if clause.
Incidentally, in this example, vegetables that are not green, have no leaves, and are not firm will report an error for not being able to cook.
Coding Standards
In the coding standard we refer to, combined with your development experience, we also put forward the requirement to use less/no if statement.
To transform the if statement in the code, we can use case (which is easier to understand like other languages), and pattern matching is the best choice:
-module(no_if).
-export([bad/1, better/1, good/1]).
bad(Connection) ->
{Transport, Version} = other_place:get_http_params(),
if
Transport =/= cowboy_spdy, Version =:= 'HTTP/1.1' ->
[{<<"connection">>, utils:atom_to_connection(Connection)}];
true ->
[]
end.
better(Connection) ->
{Transport, Version} = other_place:get_http_params(),
case {Transport, Version} of
{cowboy_spdy, 'HTTP/1.1'} ->
[{<<"connection">>, utils:atom_to_connection(Connection)}];
{_, _} ->
[]
end.
good(Connection) ->
{Transport, Version} = other_place:get_http_params(),
connection_headers(Transport, Version, Connection).
connection_headers(cowboy_spdy, 'HTTP/1.1', Connection) ->
[{<<"connection">>, utils:atom_to_connection(Connection)}];
connection_headers(_, _, _) ->
[].
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。