Constants in generic code

I am wondering if there is a recommended way to deal with constants in generic code (ie supporting user types).

Example adding a rational f(x) = x+2/3

  • Implementation 1:
f(x::T) where T = x  + T(2)/T(3)
  • Implementation 2:
f(x::T) where T = x  .+ 2//3
  • Implementation 3:
function f(x)
    T = typeof(x) 
    x  + T(2)/T(3)
end

AFAIK implementation 1 and 3 are equivalent.
Implementation 1 and 2 should have similar efficiency if the compiler can perform constant propagation.
I would say implementation 2 is better since it works with arrays whereas implementation 1 and 3 do not.

Also for irrational. eg adding 2pi f(x) = x+2pi

Implementation 1:

  • Implementation 1:
f(x::T) where T = x  + 2*T(pi)
  • Implementation 2:
f(x::T) where T = x  + 2*(pi*one(T))

This does not work with arrays.

  • Implementation 3:
f(x::T) where T = (x  .+ pi) .+ pi

works with array but not very nice to look at.

Those are the criterions I can think of to decide on best approach:

  • precision should match user type (BigFloat, Float64,…)
  • ability to support extended types such as DualNumbers, ForwardDiff, Vectors and Arrays…
  • efficiency
  • code readability

Thank you

I think either the first or the third implementation is fine. Also, broadcasting over arrays should be an orthogonal issue, and I would not define the same method to both broadcast and work on scalars, unless there is a compelling reason.

Also, for some scalars which are composed from integers via operations that propagate type, it may be enough to just convert the first element, eg

f(x::T) where T = x  + T(2)/3

Cf the style guide.

Similarly,

f(x::T) where T = x  + 2*T(pi)

would be my choice.

2 Likes