# A macro for removing calls to Base.convert: @noconvert

**URL:** <https://discourse.julialang.org/t/a-macro-for-removing-calls-to-base-convert-noconvert/116651>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [July 5, 2024, 9:50am UTC](https://discourse.julialang.org/t/a-macro-for-removing-calls-to-base-convert-noconvert/116651 "2024-07-05T09:50:16Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 5, 2024, 9:50am UTC](https://discourse.julialang.org/t/a-macro-for-removing-calls-to-base-convert-noconvert/116651/1 "2024-07-05T09:50:16Z")

</div>

From the prior discussion [on type annotations](https://discourse.julialang.org/t/on-type-annotations/116305) I was wondering what a macro that removed all calls to `Base.convert` would look like.

Here’s my prototype via Cassette.jl:

```julia
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-repl
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.~~

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [July 9, 2024, 8:58am UTC](https://discourse.julialang.org/t/a-macro-for-removing-calls-to-base-convert-noconvert/116651/2 "2024-07-09T08:58:06Z")

</div>

here is a function for returning x only if the x is of a given type

```julia
function cvt(::Type{T}, x) where T
      if isa(x, T)
           x
      else
           throw(DomainError("expected x::$T, received $x::$(typeof(x))"))
      end
end

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 9, 2024, 9:41am UTC](https://discourse.julialang.org/t/a-macro-for-removing-calls-to-base-convert-noconvert/116651/3 "2024-07-09T09:41:13Z")

</div>

This implementation below work as well and there is not a need for a custom error.

```julia-repl
julia> function cvt(::Type{T}, x) where T
           return x::T
       end
cvt (generic function with 1 method)

julia> cvt(Int, 5)
5

julia> cvt(Float64, 5)
ERROR: TypeError: in typeassert, expected Float64, got a value of type Int64
Stacktrace:
 [1] cvt(::Type{Float64}, x::Int64)
   @ Main .\REPL[30]:2
 [2] top-level scope
   @ REPL[32]:1

```
