Const or param or No argument qualifier

I apologize in advance for my New Year brain fade.

proc fred(x : real(?w)) param ....

the same as the combination

proc fred(param x : real(?w)) param ...
proc fred(const x : real(?w)) param ...

Thanks - Damian

Hi Damian —

It's not exactly the same, but quite possibly what you want for this situation.

In more detail:

  • a real(?) argument has a default intent of const [in], so is equivalent to your second routine in the second code block. Such const [in] intents will accept any variable, constant, or param value of appropriate type, make a copy of it on the way in, and disallow local modifications to it. Therefore, you can call it with a param actual even though the argument isn't labeled as param.

  • a param argument (like the first procedure in your second code block) says to create a new, specialized instance of the routine for each unique value that's passed to it, and it will only accept param actuals. Therefore fred(1.0), fred(2.0), fred(3.0) will create three distinct routines, one corresponding to each of the values.

For cases that need to take distinct actions based on the argument's value at compile-time, a param intent is a necessity. For example, if you wanted to double the argument at compile time to return it as a param value you'd need to do this.

However, if you only want to reason about the static properties of the argument at compile-time (like the value of w, or twice the value of w, or the max of w and 16, or …), then the param intent is overkill and will likely bog down compilation by creating more unique instantiations of the routine than it requires.

Let us know if this isn't clear, or if you have follow-up questions,
-Brad

Understand.

I actually use the first form based on something you said ages ago but I could not find the reference to what you said and hence why I do it. As I am about to post an example of such code, I wanted to make sure I had the provenance of the reason why I use the simpler form.

I like to avoid overkill. Thanks - Damian

Consider the following:

//  IEEE 754 floating point representation parameters
//  - p : the precision (the digits in the significand)
//  - b - the bias (added to the exponent)
//  For a type of real(w), b == 2 ** (w - 1 - precison(real(w))),
//  but spelling it out explicitly yields less Lines of Code (LOC).
//  When we have to support real(16) and real(128), that approach
//  might come back and bite us but we will address that in time!
//  See 'const or param or No qualifier' note in Discourse

//  ---------------------------------------------------------

//  precision 'p' (p = 11 (or 113) for BINARY16 (or BINARY128))

proc precision(type T) param where T == real(32) do return 24:uint(32);
proc precision(type T) param where T == real(64) do return 53:uint(64);
proc precision(x : real(?w)) param do return precision(real(w));

Is it better (wiser/more effective/.....) to write the last line as

proc precision(x) param do return precision(x.type);

Am curious. Thanks.

Hi Damian —

For real(?) arguments, there shouldn't be any difference between the two. x.type is evaluated at compile-time for simple types like scalar values, as is real(w), so the bodies of the two procedures would both evaluate to the same equivalent at compile-time. The only difference I'm seeing between the two is that the latter will accept arguments of arbitrary type where the former should only accept real(?) arguments.

If the second were written as proc precision(x: real(?)) param do return precision(x.type); or proc precision(x) param where isReal(x) do return precision(x.type);, then I believe the two would be even more equivalent (other than how error messages are printed for non-real(?) arguments).

-Brad

1 Like