From the prior discussion on type annotations I was wondering what a macro that removed all calls to Base.convert
would look like.
Here’s my prototype via Cassette.jl:
using Cassette
Cassette.@context NoConvertCtx;
Cassette.overdub(context::NoConvertCtx, ::typeof(Base.convert), _, x) = x
macro noconvert(e)
e.head == :function || error("Expression for @noconvert must be a full `function` definition")
s = gensym("noconvert")
return_type = Any
if e.args[1].head == :(::)
return_type = e.args[1].args[2]
e.args[1] = e.args[1].args[1]
end
if e.args[1].head == :call
fname = e.args[1].args[1]
fargs = e.args[1].args[2:end]
end
e2 = deepcopy(e)
e2.args[2] = :(Cassette.overdub(Ctx(), $s)::$return_type)
for arg in fargs
push!(e2.args[2].args[1].args, arg isa Expr ? arg.args[1] : arg)
end
if e.args[1].head == :call
e.args[1].args[1] = s
end
quote
let
global $fname
$e
$e2
end
end
end
julia> function foo(x::Number)
_x::Int = x
return _x
end
foo (generic function with 2 methods)
julia> foo(1)
1
julia> foo(2.0)
2
julia> foo(2.5)
ERROR: InexactError: Int64(2.5)
julia> @noconvert function foo(x::Number)
_x::Int = x
return _x
end
foo (generic function with 2 methods)
julia> foo(3)
3
julia> foo(4.0)
ERROR: TypeError: in typeassert, expected Int64, got a value of type Float64
julia> foo(4.5)
ERROR: TypeError: in typeassert, expected Int64, got a value of type Float64
The calls to foo(4.0)
and foo(4.5)
both fail since the argument is a Float64
and conversion to Int
is no longer done.
Can you improve upon this macro? We need to generalize this to other function definition forms or perhaps even other code blocks. Cassette.jl is also a bit overkill I think, but the interface is so easy.
Also, I need to generalize for more arguments.