Enhanced type guard syntax]

Richard A. O'Keefe ok@REDACTED
Mon Sep 22 07:38:20 CEST 2003


Ulf_Wiger <ulf.wiger@REDACTED> wrote:

	The following code snippet from wings_deform.erl:
	
	twist_fun(y, {Cx,_,Cz}) ->
	    fun(U, Min, {X0,Y,Z0})
	       when float(U), float(Min), float(X0), float(Y), float(Z0) ->
		    Angle = U*(Y-Min),
		    Cos = math:cos(Angle),
		    Sin = math:sin(Angle),
		    X = X0 - Cx,
		    Z = Z0 - Cz,
		    {X*Cos+Z*Sin+Cx,Y,Z*Cos-X*Sin+Cz}
	    end;
	
I have to ask "what are those float/1 tests doing there at all?"
Surely the normal use of guards is to select between alternatives?
There is no alternative here.
All that these guards are doing is turning _meaningful_ calls
(like F(0,0,{0,0,0}) into errors.  What _would_ be useful from an
error-detection point of view would be to check that Cx and Cz are
numbers, but that is _not_ done.

Surely the best way to simplify that code is

	twist_fun(y, {Cx,_,Cz}) ->
	    % maybe check Cx, Cz are numbers before it's too late?
	    fun(U, Min, {X0,Y,Z0}) ->
		Angle = U*(Y-Min),
		Cos = math:cos(Angle),
		Sin = math:sin(Angle),
		X = X0 - Cx,
		Z = Z0 - Cz,
		{X*Cos+Z*Sin+Cx,Y,Z*Cos-X*Sin+Cz}
	    end;

	In this particular code (I believe), type guards are important
	from a performance perspective, since floating point operations
	are performed more efficiently if the compiler can _know_ that
	the input parameters are really floats.
	
It's not clear to me that tweaking performance in very minor ways
at the price of turning meaningful calls into errors is a good way for
Erlang to go.  Note that with the original code the compiler does NOT
know that Cx and Cz are floats (or even that they are numbers) so that
the last three lines of calculations have to be done in a general way
anyway, and that the cost is likely to be dominated by the calls to
math:cos/1 and math:sin/1.

Is there a way you can get the speed benefit of knowing that something
is a float _without_ going out of your way to lock out meaningful calls
and _without_ any language changes?  Yes:

	twist_fun(y, {Cx,_,Cz}) ->
	    Cxf = float(Cx),    % check numeric & convert now
	    Czf = float(Cz),    % check numeric & convert now
	    fun(U, Min, {X0,Y,Z0}) ->
	        Angle = float(U)*(float(Y) - float(Min)),
		Cos = math:cos(Angle),
		Sin = math:sin(Angle),
		X = float(X0) - Cxf,
		Z = float(Z0) - Czf,
		{X*Cos+Z*Sin+Cxf, Y, Z*Cos-X*Sin+Czf}
	    end;
	
Now the compiler does know what is going on, _and_ other numeric
types can be passed in.  There's no extra cost for arguments that _are_
floats, because the check had to be done anyway.

	Also, it's consistent with the bit syntax, so the X/Type
	syntax has already been added to the language.
	
I hadn't really regarded those things as types but as formats;
possibly because I thought of them as related to Perl's "pack" and
"unpack".



More information about the erlang-questions mailing list