As discussed in SV12 13.8, parameterized functions can be declared as static methods of abstract classes.
This is a much more powerful coding style than traditional Verilog functions, but the function calls look ugly and redundant, hence error-prone and difficult for a human to read. A typical style will be to specialize the class with a type parameter for each argument, because most particulars, such as widths, can be derived from the types.
... C#(type(a+b),type(c+d))::f(a+b,c+d) ...
So don’t call the function directly, but instead use a let expression. (See 11.13.)
package pkg; virtual class C ... endclass let f(arg1,arg2) = C#(type(arg1),type(arg2))::f(arg1,arg2); endpackage ... pkg::f(a+b,c+d) ...
Package the abstract class with let declarations for each of its parameterized functions. At a call site, a let expression will be indistinguishable from a traditional Verilog function call.
If you update the package definition to use an entirely different class behind the scenes, with a different class name, different function names, and different parameters, then the consumers of the package won’t need to change even a character of code.