Repository: JuliaDiff/FiniteDifferences.jl (julia)

Issue:
Argument limited to Functions
FiniteDifferences does not support objects that can be called without being a subtype of `Function`. It's a bit weird since Julia does not support multiple inheritances. Can the restriction `<: Function` be removed? If not it would be good to discuss the issue/workaround in the README.

```julia
using Interpolations
f = linear_interpolation(1:10, 1:10)
central_fdm(5, 1)(x -> f(x), 1.5)
# 1.000000000000018
central_fdm(5, 1)(f, 1.5)
#ERROR: MethodError: no method matching (::FiniteDifferences.AdaptedFiniteDifferenceMethod{5, 1, FiniteDifferences.UnadaptedFiniteDifferenceMethod{7, 5}})(::Interpolations.Extrapolation{Float64, 1, ScaledInterpolation{Float64, 1, Interpolations.BSplineInterpolation{Float64, 1, Vector{Float64}, BSpline{Linear{Throw{OnGrid}}}, Tuple{Base.OneTo{Int64}}}, BSpline{Linear{Throw{OnGrid}}}, Tuple{UnitRange{Int64}}}, BSpline{Linear{Throw{OnGrid}}}, Throw{Nothing}}, ::Float64)
#Closest candidates are:
#  (::FiniteDifferences.AdaptedFiniteDifferenceMethod)(::TF, ::Real) where TF<:Function
#   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/zZFW0/src/methods.jl:191
#  (::FiniteDifferences.AdaptedFiniteDifferenceMethod{P, Q})(::TF, ::Real, ::Real) where {P, Q, TF<:Function}
#   @ FiniteDifferences ~/.julia/packages/FiniteDifferences/zZFW0/src/methods.jl:238
```

Pull request description:
Fixes #224.

Patch:
diff --git a/src/methods.jl b/src/methods.jl
index a5885d7..6783530 100644
--- a/src/methods.jl
+++ b/src/methods.jl
@@ -153,13 +153,13 @@ function FiniteDifferenceMethod(grid::AbstractVector{Int}, q::Int; kw_args...)
 end
 
 """
-    (m::FiniteDifferenceMethod)(f::Function, x::T) where T<:AbstractFloat
+    (m::FiniteDifferenceMethod)(f, x::T) where T<:AbstractFloat
 
 Estimate the derivative of `f` at `x` using the finite differencing method `m` and an
 automatically determined step size.
 
 # Arguments
-- `f::Function`: Function to estimate derivative of.
+- `f`: Function to estimate derivative of.
 - `x::T`: Input to estimate derivative at.
 
 # Returns
@@ -188,12 +188,12 @@ julia> FiniteDifferences.estimate_step(fdm, sin, 1.0)  # Computes step size and
 # We loop over all concrete subtypes of `FiniteDifferenceMethod` for Julia v1.0 compatibility.
 for T in (UnadaptedFiniteDifferenceMethod, AdaptedFiniteDifferenceMethod)
     @eval begin
-        function (m::$T)(f::TF, x::Real) where TF<:Function
+        function (m::$T)(f::TF, x::Real) where TF
             x = float(x)  # Assume that converting to float is desired, if it isn't already.
             step = first(estimate_step(m, f, x))
             return m(f, x, step)
         end
-        function (m::$T{P,0})(f::TF, x::Real) where {P,TF<:Function}
+        function (m::$T{P,0})(f::TF, x::Real) where {P,TF}
             # The automatic step size calculation fails if `Q == 0`, so handle that edge
             # case.
             return f(x)
@@ -202,13 +202,13 @@ for T in (UnadaptedFiniteDifferenceMethod, AdaptedFiniteDifferenceMethod)
 end
 
 """
-    (m::FiniteDifferenceMethod)(f::Function, x::T, step::Real) where T<:AbstractFloat
+    (m::FiniteDifferenceMethod)(f, x::T, step::Real) where T<:AbstractFloat
 
 Estimate the derivative of `f` at `x` using the finite differencing method `m` and a given
 step size.
 
 # Arguments
-- `f::Function`: Function to estimate derivative of.
+- `f`: Function to estimate derivative of.
 - `x::T`: Input to estimate derivative at.
 - `step::Real`: Step size.
 
@@ -235,7 +235,7 @@ julia> fdm(sin, 1, 1e-3) - cos(1)  # Check the error.
 # We loop over all concrete subtypes of `FiniteDifferenceMethod` for 1.0 compatibility.
 for T in (UnadaptedFiniteDifferenceMethod, AdaptedFiniteDifferenceMethod)
     @eval begin
-        function (m::$T{P,Q})(f::TF, x::Real, step::Real) where {P,Q,TF<:Function}
+        function (m::$T{P,Q})(f::TF, x::Real, step::Real) where {P,Q,TF}
             x = float(x)  # Assume that converting to float is desired, if it isn't already.
             fs = _eval_function(m, f, x, step)
             return _compute_estimate(m, fs, x, step, m.coefs)
@@ -245,7 +245,7 @@ end
 
 function _eval_function(
     m::FiniteDifferenceMethod, f::TF, x::T, step::Real,
-) where {TF<:Function,T<:AbstractFloat}
+) where {TF,T<:AbstractFloat}
     return f.(x .+ T(step) .* m.grid)
 end
 
@@ -336,7 +336,7 @@ end
 """
     function estimate_step(
         m::FiniteDifferenceMethod,
-        f::Function,
+        f,
         x::T
     ) where T<:AbstractFloat
 
@@ -345,7 +345,7 @@ estimate of the derivative.
 
 # Arguments
 - `m::FiniteDifferenceMethod`: Finite difference method to estimate the step size for.
-- `f::Function`: Function to evaluate the derivative of.
+- `f`: Function to evaluate the derivative of.
 - `x::T`: Point to estimate the derivative at.
 
 # Returns
@@ -355,13 +355,13 @@ estimate of the derivative.
 """
 function estimate_step(
     m::UnadaptedFiniteDifferenceMethod, f::TF, x::T,
-) where {TF<:Function,T<:AbstractFloat}
+) where {TF,T<:AbstractFloat}
     step, acc = _compute_step_acc_default(m, x)
     return _limit_step(m, x, step, acc)
 end
 function estimate_step(
     m::AdaptedFiniteDifferenceMethod{P,Q}, f::TF, x::T,
-) where {P,Q,TF<:Function,T<:AbstractFloat}
+) where {P,Q,TF,T<:AbstractFloat}
     ∇f_magnitude, f_magnitude = _estimate_magnitudes(m.bound_estimator, f, x)
     if ∇f_magnitude == 0.0 || f_magnitude == 0.0
         step, acc = _compute_step_acc_default(m, x)
@@ -373,7 +373,7 @@ end
 
 function _estimate_magnitudes(
     m::FiniteDifferenceMethod{P,Q}, f::TF, x::T,
-) where {P,Q,TF<:Function,T<:AbstractFloat}
+) where {P,Q,TF,T<:AbstractFloat}
     step = first(estimate_step(m, f, x))
     fs = _eval_function(m, f, x, step)
     # Estimate magnitude of `∇f` in a neighbourhood of `x`.
@@ -551,7 +551,7 @@ end
 """
     extrapolate_fdm(
         m::FiniteDifferenceMethod,
-        f::Function,
+        f,
         x::Real,
         initial_step::Real=10,
         power::Int=1,
@@ -567,7 +567,7 @@ automatically sets `power = 2` if `m` is symmetric and `power = 1`. Moreover, it
 
 # Arguments
 - `m::FiniteDifferenceMethod`: Finite difference method to estimate the step size for.
-- `f::Function`: Function to evaluate the derivative of.
+- `f`: Function to evaluate the derivative of.
 - `x::Real`: Point to estimate the derivative at.
 - `initial_step::Real=10`: Initial step size.
 
@@ -576,7 +576,7 @@ automatically sets `power = 2` if `m` is symmetric and `power = 1`. Moreover, it
 """
 function extrapolate_fdm(
     m::FiniteDifferenceMethod,
-    f::Function,
+    f,
     x::Real,
     initial_step::Real=10;
     power::Int=1,


Test patch:
diff --git a/test/methods.jl b/test/methods.jl
index 3c8b132..af8e15b 100644
--- a/test/methods.jl
+++ b/test/methods.jl
@@ -1,3 +1,10 @@
+"""
+Wrapper used in test set “do not require f::Function” below, moved outside so that it
+works on Julia 1.0.
+"""
+struct NotAFunction end # not <: Function on purpose, cf #224
+(::NotAFunction)(x) = abs2(x)
+
 @testset "Methods" begin
     @testset "Correctness" begin
         # Finite difference methods to test.
@@ -162,4 +169,11 @@
             end
         end
     end
+
+    @testset "do not require f::Function" begin
+        x = 0.7
+        for f in [forward_fdm, central_fdm, backward_fdm]
+            f(5, 1)(NotAFunction(), x) ≈ 2 * x
+        end
+    end
 end