[erlang-questions] Ideas for a new Erlang

Robert Virding rvirding@REDACTED
Sat Jun 28 00:35:08 CEST 2008


Richard is absolutely right here. Guards were always seen as part of
pattern matching, the bits which you could not easily write in the
patterns. They have always been a sequence of *tests* which either
succeed of fail, there has never been a concept of error in guards. In
this sense they have never been proper expressions, even restricted
ones. Boolean operations were added to expand guards and tests the
changed to more resemble expressions, for example tuple(T) =>
is_tuple(T), but guard tests are not expressions, they are *tests*.

It might have been the wrong to expand the tests with boolean
operations as it means that it is more difficult to see the
difference, and hence to understand it.

But they are not expressions, they never have been, they are tests!

Robert

P.S. I have seen attempts, not in Erlang, to include the "guard" bit
in the patterns and they were *very* hard to read.

On 26/06/2008, Richard A. O'Keefe <ok@REDACTED> wrote:
> GUARDS
> ======
>
>
> p6. "The complex selective receive primitive of old Erlang
>       has forced the Erlang designers to introduce a more
>       restricted type of expressions called _guards_."
>
> This is fiction, and it's fiction with a major error in it.
>
> Guards are *NOT* a restricted type of expression.
>
> This was much clearer in old Erlang.
> Sadly, it has been obscured by the disastrous design bungle
> of giving guard predicates aliases that can be used in
> expressions and by allowing 'or' and 'and' -- which should
> be avoided AT ALL TIMES AND IN ALL PLACES -- in both contexts.
>
> It would be really really useful if there were a compiler
> option to complain loudly whenever a guard predicate's alias
> is used in an expression.
>
> The *syntax* of guards in Erlang is similar to expressions,
> but the *semantics* is very different.  Consider:
>
> 	Guard			Expression
> 	succeeds		returns 'true'
> 	fails			returns 'false'
> 				returns something else
> 				raises an exception
>
> Some forms that would raise an exception as an expression
> fail as guards.  Some forms that would raise an exception
> as an expression succeed as guards.  The order of evaluation
> of expressions is defined.  The order of evaluation of
> guards is not defined.  If you take a guard and treat it as
> an expression, expecting it to always answer 'true' or 'false',
> you are in for a nasty surprise:
>
>      some guards that fail may raise an exception
>      some guards that succeed may raise an exception
>      some guards that succeed may return a value other than 'true'.
>
> The whole point of section 3.4 is
> 	"SINCE guards are restricted expressions,
> 	 they ought to be unrestricted expressions"
> but since the assumption is flat-out false, the conclusion is
> unwarranted.
>
> The limitations of guards have NO intrinsic connection with
> receive, although they turn out to be very pleasant for receive.
> Erlang guards have their roots in concurrent logic programming
> languages such as Strand88, GHC, FCP, and Parlog.
>
> Whenever I see people demanding that guards should be complexified,
> I find myself wondering "do I know something about readability and
> maintainability that they don't, or is it the other way around?"
> The way I see it, if your selection process is complex enough that
> you *want* to write complex guards with user defined functions,
> then what you *need* is either
>   - abstract patterns, or
>   - a rewrite.
>
> Let me offer an example I saw today, in
> http://www.infoq.com/articles/erlang-dsl
>
> It doesn't involve a user defined guard, but it does show what
> happens when you have a "keep-guards-simple" mindset.
>
> Here's the original code.  Its purpose is to return a function
> that sends a message if something has a value within a limit.
>
> 	to_function(Pid, Action, Quantity, Ticker, Operator, Limit) ->
> 	    fun (Ticker_, Price) ->
> 		if
> 		    Ticker =:= Ticker_ andalso
> 		    ( ( Price < Limit andalso Operator =:= less    ) orelse
> 		      ( Price > Limit andalso Operator =:= greater )
> 		    ) ->
> 			Pid ! {Action, Quantity, Ticker} % place an order
> 		  ; true ->
> 			erlang:display("no rule applied")
> 		end
> 	   end.
>
> I looked at that and, well, it didn't make me smile.
> That complex guard *had* to go.  A few seconds later:
>
> 	to_function(Pid, Action, Quantity, Ticker, less, Limit) ->
> 	    fun (T, Price) when T =:= Ticker, Price < Limit ->
> 		    Pid ! {Action, Quantity, Ticker} % place an order
> 	      ; (_, _) ->
> 		    erlang:display("no rule applied")
> 	    end;
> 	to_function(Pid, Action, Quantity, Ticker, greater, Limit) ->
> 	    fun (T, Price) when T =:= Ticker, Price > Limit ->
> 		    Pid ! {Action, Quantity, Ticker} % place an order
> 	      ; (_, _) ->
> 		    erlang:display("no rule applied")
> 	    end.
>
> In the original context, this now gives us the benefit of a 'compile
> time'
> failure if Operator is not 'less' or 'greater', rather than a 'run time'
> failure.  But it gets better.  This is about buying or selling stocks,
> and it so happens that we always want to buy when the price is LOW,
> and sell when the price is HIGH.  So now we can write
>
> 	to_function(Pid, buy, Quantity, Ticker, less, Limit) ->
> 	    fun (T, Price) when T =:= Ticker, Price < Limit
> 		->  Pid ! {buy, Quantity, Ticker}
> 	      ; (_, _)
> 		->  erlang:display("buy rule did not apply")
> 	    end;
> 	to_function(Pid, sell, Quantity, Ticker, greater, Limit) ->
> 	    fun (T, Price) when T =:= Ticker, Price > Limit
> 		->  Pid ! {sell, Quantity, Ticker}
> 	      ; (_, _)
> 		->  erlang:display("sell rule did not apply")
> 	    end.
>
> Now we can *see* that the returned function will buy if the price is
> low or sell when the price is high, but not vice versa.  This was not
> possible with the original version.
>
> What I've done here is to trade size against readability.  The next
> step would be to replace Pid by Broker, but that's a different
> readability issue.



More information about the erlang-questions mailing list