I am writing some generic code to allow basic operations on functions. It might be simplest if I give an example:
julia> import Base: +,^
julia> +(f1::Function,f2::Function) = x -> f1(x)+f2(x)
julia> ^(f1::Function, n::Int) = x -> f1(x)^n
julia> unity = sin^2 + cos^2
julia> unity(123.456)
1.0
So far, so good. Now, say I notice that I want the second argument to be always positive, since raising a trig function to a -ve power implies an inverse function -- a totally different scenario. Further, I probably won't even need a huge value for it, so say I decide to limit it to UInt16.
However,
julia> ^(f1::Function, n::UInt16) = x -> f1(x)^n
^ (generic function with 67 methods)
julia> unity = (sin^2) + (cos^2)
ERROR: MethodError: no method matching ^(::typeof(sin), ::Int64)
Closest candidates are:
^(::Float16, ::Integer) at math.jl:885
^(::Regex, ::Integer) at regex.jl:712
^(::Missing, ::Integer) at missing.jl:155
...
Stacktrace:
[1] macro expansion at ./none:0 [inlined]
[2] literal_pow(::typeof(^), ::typeof(sin), ::Val{2}) at ./none:0
[3] top-level scope at REPL[4]:1
I don't understand the MethodError. Why is:
ERROR: MethodError: no method matching ^(::typeof(sin), ::Int64)
somehow expecting an Int64? and more importantly, how do I override it, to get the behaviour I want?
(PS. iow that the system handles the types appropriately, and the API user isn't obliged to use spurious casts, the target is an intuitive and predictable mathematical description language in the style of Sussman, of Scheme fame.)
Thanks!
2 isa Int (not a UInt16). Values don't have multiple types.
The problem is that when given an integer constant, Julia's compiler looks at the format of the number and assigns it a type based on the format. Numbers in base 10 default to Int (signed Int64) and numbers such as 0x123 default to Unsigned integer types with the number of digits used in the constant used to assign the size of the unsigned type: 0x5 and 0x05 UInt8, 0x005 and 0x0005 UInt16, and so forth. So:
julia> fpow(f, n::UInt16) = x -> f(x)^n
fpow (generic function with 1 method)
julia> sinpow(x, n) = fpow(sin, n)(x)
sinpow (generic function with 1 method)
julia> sinpow(pi, 5)
ERROR: MethodError: no method matching fpow(::typeof(sin), ::Int64)
Closest candidates are:
fpow(::Any, ::UInt16) at REPL[13]:1
Stacktrace:
[1] sinpow(x::Irrational{:π}, n::Int64)
@ Main .\REPL[14]:1
[2] top-level scope
@ REPL[15]:1
julia> sinpow(pi, 0x5)
ERROR: MethodError: no method matching fpow(::typeof(sin), ::UInt8)
Closest candidates are:
fpow(::Any, ::UInt16) at REPL[13]:1
Stacktrace:
[1] sinpow(x::Irrational{:π}, n::UInt8)
@ Main .\REPL[14]:1
[2] top-level scope
@ REPL[16]:1
julia> sinpow(pi, 0x005)
0.0
But Julia will change a signed base 16 constant to unsigned (2s complement) without complaint. Did you want this to work? It does:
julia> sinpow(pi, -0x005)
0.0
I would therefore suggest that, to avoid unneeded error messages, you use Integer in the function declaration and @assert() to require n >= 0:
function fpow(f, n::Integer)
@assert n >= 0
return x -> f(x)^n
end
or
fpow(f, n::Integer) = (n >= 0 || error("exponent cannot be < 0"); x -> f(x)^n)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With