Strict typing and restricting types in a method's signature

one of the problems beginners like me run into is that if we supply an unsupported type, some obscure error is raised from deep inside the function, claiming that some function does not have some method.

as an experiment, i wanted to alleviate this problem, and cause my functions to test and fail early if the supplied types does not support what i want from them. i tried this:

function myfunc(a)
  if !method_exists(feature, (typeof(a),))
    error("$(typeof(a)) does not have feature")
  end
  ...

i was hoping that Julia could magically eliminate the entire code during compile. but unfortunately it is not the case, some quite heavy code gets injected. a much shorter code, but still some runtime check, and also can’t control the error message:

function myfunc(a)
  @which feature(a)
  ...

maybe there is a neat way to move this check to compile time and thus enabling efficient contract enforcing and early detection of errors.

1 Like

Maybe check this again.

Could you use generated functions?
Not sure if this is recommended, or indeed if it will work.

@generated function myfunc{T}(a::T)
    if method_exists(feature, (T,))
        :(_myfunc(a))
    else
        :(error("$T does not have feature"))
    end
end

function _myfunc(a)
    println("_myfunc called with $(typeof(a))")
end

feature(x::Number) = x*x


myfunc(2)
myfunc("cat")

4 Likes

Yes, a generated function is the right tool here.

I personally am of the opinion that this is better done with a static checker than a dynamic check, even if the dynamic check is actually a runtime check. Unfortunately, Lint.jl is not sophisticated enough to do these checks yet. I would like to work on that feature if/when I get some time.

i came up with this ugliness, purely for the sake of learning

@generated function require(method, args...)
  t = Base.to_tuple_type(args)
  t = Tuple{method, t.parameters...}
  if ccall(:jl_method_exists, Cint, (Any, Any), method.name.mt, t) == 0
    :(error("$method is required"))
  end
end

function myfunc(a)
  require(feature, a)
  ...
end

i had to lift the body of the method_exist function from reflections.jl, because none of the exported functions take a function type as opposed to a function, understandably. it works as intended. i wonder if i butchered julia badly here, or it is actually something reasonable.

1 Like