What is the better way to define generic routines?
A simple routine which returns the smallest normal number could be defined two ways.
This:
inline proc leastNormal(type T) param where T == real(32) do return 0x1p-126:int(32);
inline proc leastNormal(type T) param where T == real(64) do return 0x1p-1022:int(64);
inline proc leastNormal(param x : ?Real) param do return leastNormal(Real);
inline proc leastNormal(const x : ?Real) param do return leastNormal(Real);
Or
inline proc leastNormal(type T) param where T == real(32) do return 0x1p-126:int(32);
inline proc leastNormal(type T) param where T == real(64) do return 0x1p-1022:int(64);
inline proc leastNormal(param x : real(?w)) param do return leastNormal(real(w));
inline proc leastNormal(const x : real(?w)) param do return leastNormal(real(w));
What is better or more optimal?
Hi Damian —
Since you use the phrase "more optimal", let me start by saying that you shouldn't see any performance difference between these approaches. I'd call the difference more a matter of style and generality (so maybe "more optimal in terms of clarity / precision").
For a routine that only wanted to support these real(w)
signatures, I prefer the second of these two choices since it puts a tighter constraint on the x
argument, requiring it to be a real
. In particular, note that your first approach would permit a call like leastNormal(42)
which would cause it to try and call leastNormal(int)
which will lead to a resolution error since there is no such overload.
Of course, if you wanted to have the routine support additional types, then the first form might be preferable, where you might want to add an additional where
clause to permit only the types of x
that you want to support to make it through. But since you named that type query Real
, I'm guessing that's not the case here.
As a few other style notes, consider:
-
To avoid the verbosity of a where
-clause, you could change the first two overloads in either approach into a constraint on the type argument:
inline proc leastNormal(type T: real(32)) param do return 0x1p-126:int(32);
inline proc leastNormal(type T: real(64)) param do return 0x1p-1022:int(64);
-
Since the behavior of the param
and const
overload are identical (in that both return a param
and do so using the same logic), you could do away with the param
overload which doesn't add any significant value over the const
version. Specifically, param
actuals can be passed to non-param
formals. The typical reason for supporting routines that accept param
and non-param
actuals is when the former returns a param
and the latter does not.
-
For that matter, since param
routines are computed at compile-time, the argument intent of const
isn't really necessary or meaningful. So for brevity, you could just drop that intent.
-
Similarly, since param
procedures are computed at compile-time, the inline
keyword shouldn't really have any benefit here.
As a result of all those comments, this is probably how I would write this family of overloads (ATO):
proc leastNormal(type T: real(32)) param do return 0x1p-126:int(32);
proc leastNormal(type T: real(64)) param do return 0x1p-1022:int(64);
proc leastNormal(x: real(?w)) param do return leastNormal(real(w));
Let us know if this response raises any additional questions for you,
-Brad
Thanks. You have answered all my original questions and more.
1 Like