# Custom implicit type conversion

**URL:** <https://discourse.julialang.org/t/custom-implicit-type-conversion/117315>\
**Category:** General Usage\
**Tags:** type, convert\
**Created:** [July 22, 2024, 1:42am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315 "2024-07-22T01:42:01Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 22, 2024, 1:42am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/1 "2024-07-22T01:42:01Z")

</div>

I would like to specify my own implicit type conversion and according to [Conversion and Promotion · The Julia Language](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#Defining-New-Conversions) I should be able to do something like:

```julia-repl
julia> struct AA end

julia> Base.convert(::Type{Float64}, x::AA) = 1.0

julia> sin(AA())
ERROR: MethodError: no method matching sin(::AA)

Closest candidates are:
  sin(::Float16)
   @ Base math.jl:1558
  sin(::Irrational{:π})
   @ Base mathconstants.jl:126
  sin(::BigFloat)
   @ Base mpfr.jl:801
  ...

Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

julia> versioninfo()
Julia Version 1.10.4
Commit 48d4fd48430 (2024-06-04 10:41 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 16 × 12th Gen Intel(R) Core(TM) i5-12600K
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, alderlake)
Threads: 1 default, 0 interactive, 1 GC (on 16 virtual cores)
Environment:
  JULIA_PKG_USE_CLI_GIT = true

julia> 

```

Why is `AA()` not implicitly converted to `Float64`?

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [July 22, 2024, 1:57am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/2 "2024-07-22T01:57:00Z")

</div>

to use promotion and conversion you need two more ingredients:

- `AA <: Number`
- defined `promote_rule`

```julia
julia> struct AA <: Number end

julia> Base.convert(::Type{Float64}, x::AA) = 1.0

julia> Base.promote_rule(::Type{Float64}, ::Type{AA}) = Float64

julia> AA() + 2.0
3.0

```

It still doesn’t give you `sin(AA())` because no method of `sin()` takes generic `Number`

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 22, 2024, 6:56am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/3 "2024-07-22T06:56:56Z")

</div>

Conversion and promotion only automatically occur in some methods or syntax, and the method dispatched by `sin(::AA)` does not do that, in fact it does not exist. Technically it’s not inherently automatic either because it’s implemented somewhere, which lets you implement your own methods with automatic conversions or promotions. Off the top of my head, conversions are attempted automatically in base Julia when assigning to a type-annotated variable or field, returning from a method with an annotated return type, and pushing into an array.

```julia
julia> y::Float64 = AA(); y
1.0

julia> ys = Float64[]; push!(ys, AA())
1-element Vector{Float64}:
 1.0

```

If you instead subtyped `AA<:Real`, then you do reach a method that converts a type, though not with `convert`:

```julia
julia> begin
       struct AA<:Real end
       Base.float(::AA) = 1.0
       sin(AA()) # dispatches to sin(x::Real)
       end
0.8414709848078965

```

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 23, 2024, 1:29am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/4 "2024-07-23T01:29:04Z")

</div>

Thank you @jling and @Benny , your answers and a reread of the manual suggest to me that Julia will not implicitly call `convert` in order to find a matching method. My example above was just an MWE to help me understand `convert`, what I really want to do is something like:

```julia
struct MyType <: Function end

(::MyType)(x) = println("Hello $x")
Base.convert(::Type{MyType}, x::Function) = MyType()

dosomething(x::MyType, y) = println(x(y), " says hello")

dosomething(8) do x
    x^2
end

```

which I now can see does not work because the new `convert` method is not implicitly called. The main goal here is to be able to use `do` syntax without changing (or adding methods to) `dosomething`. Is there something akin to `convert` that can be used to achieve this?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 23, 2024, 2:25am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/5 "2024-07-23T02:25:57Z")

</div>

> [@GHTaarn](#):
>
> without changing (or adding methods to) `dosomething`. Is there something akin to `convert` that can be used to achieve this?

That’s impossible. Every call is dispatched to a most fitting method, and if that method doesn’t exist or doesn’t properly implement the conversion you need, then you’re just stuck with manual `convert`s in serial calls, like `dosomething(convert(MyType, x->x^2), 8)`. `do` syntax in particular needs `_(::Function, _)` or `_(::Any, _)` methods.

If you can add a method, this can be done with a forwarding method, like:

```julia
dosomething(x::Function, y) = dosomething(convert(MyType, x), y)

```

In fact, `MyType` already subtypes `Function` so the 2 methods can be merged and still work for your example:

```julia
dosomething2(x::Function, y) = println(convert(MyType, x)(y), " says hello")

```

though I’d prefer to separate responsibilities. Unlike most types, `Function`-annotated arguments are not specialized over as a heuristic, so you’ll want to make a parametric method to opt into specialization, search the performance tips for examples.  
P.S. `(::MyType)(x)` currently returns `nothing`, so `println(x(y), " says hello")` will print `nothing says hello`. My gut feeling is that’s not your intention.

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 24, 2024, 1:29am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/6 "2024-07-24T01:29:07Z")

</div>

Thank you for your insights, at least it is useful to know that there is no immediately obvious slick way of handling this. I will just have to either accept that straight forward `do` syntax is not possible or redesign the framework. I did think of a little hack that allows me to use `do` syntax although less elegantly:

```julia
enable_do_syntax(f1::Function, f2::Function, args...; kwargs...) = f2(convert(MyType, f1), args...; kwargs...)

enable_do_syntax(dosomething, 8) do x
    x^2
end

```

> [@Benny](#):
>
> P.S. `(::MyType)(x)` currently returns `nothing`, so `println(x(y), " says hello")` will print `nothing says hello`. My gut feeling is that’s not your intention.

Yes, this was a very shaved down MWE, I did realise this, but decided that as a whole it illustrated what I was trying to achieve, namely to use `do` syntax on a specialised type.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [July 24, 2024, 8:56am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/7 "2024-07-24T08:56:11Z")

</div>

> [@GHTaarn](#):
>
> `do` syntax

IMO `do` syntax is unnecessary and ideally wouldn’t be part of the language. What’s wrong with assigning a function to a variable?

```julia
sq = x -> x^2
enable_do_syntax(sq, dosomething, 8)

```

… or …

```julia
function sq(x)
    x^2
end
enable_do_syntax(sq, dosomething, 8)

```

So using `do` syntax just makes code less readable and less debuggable.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 24, 2024, 11:22am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/8 "2024-07-24T11:22:27Z")

</div>

> [@GHTaarn](#):
>
> enable\_do\_syntax(dosomething, 8) do x

seems a little elaborate, and the name suggests something too general for such a specific conversion. If you’re not trying to call `dosomething` directly (because you also can’t add the obviously missing method), then just make a `do`-supporting derivative function forwarding to it:

```julia-auto
dosomething2(x::Function, y) = dosomething(convert(MyType, x), y)

```

but I suppose the implication is that there are more functions like `dosomething` with missing `_(::Function, _)` methods, hence a higher-order function to handle any of them. A more specific name would be in order, but honestly `makeMyType(dosomething, 8) do x` isn’t really preferable to a manual `convert` prior to a typical call to me.

> [@nsajko](#):
>
> What’s wrong with assigning a function to a variable?

The `do`-less examples assigned to a global variable that’ll persist, but a `let` block is a simple alternative:

```julia-auto
let sq = x->x^2
  dosomething2(sq, 8)
end

```

in fact if you allow yourself to annotate `MyType` on the assignment, it converts and you can just use the original function:

```julia-auto
let sq::MyType = x->x^2
  dosomething(sq, 8)
end

```

clarity which I would prefer in the case where `MyType` isn’t actually special and there’s a whole host of types to convert functions to.

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 25, 2024, 3:40am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/9 "2024-07-25T03:40:38Z")

</div>

Thank you both @nsajko and @Benny for your comments.

> [@nsajko](#):
>
> IMO `do` syntax is unnecessary and ideally wouldn’t be part of the language. What’s wrong with assigning a function to a variable?
> 
> > [@nsajko](#):
> >
> 
> So using `do` syntax just makes code less readable and less debuggable.

We clearly disagree on whether `do` syntax should be part of Julia, I feel that it looks very neat, but I will consider your suggestion, especially if `do` is handicapped with a call to `enable_do_syntax`.

> [@Benny](#):
>
> > [@GHTaarn](#):
> >
> > enable\_do\_syntax(dosomething, 8) do x
> 
> seems a little elaborate, and the name suggests something too general for such a specific conversion.

Hopefully I can come up with a better name.

> [@Benny](#):
>
> but I suppose the implication is that there are more functions like `dosomething` with missing `_(::Function, _)` methods, hence a higher-order function to handle any of them.

Exactly, there are many `dosomething`s and they will be spread out over multiple packages. [Initially](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/4) I wanted to keep this question simple, but now that we are in so deep, I feel that I should mention that there are many `MyTypes` as well, all with one abstract supertype which is what the `dosomething`s actually have as their first parameter.

> [@Benny](#):
>
> A more specific name would be in order, but honestly `makeMyType(dosomething, 8) do x` isn’t really preferable to a manual `convert` prior to a typical call to me.
> 
> > [@nsajko](#):
> >
> > What’s wrong with assigning a function to a variable?
> 
> The `do`-less examples assigned to a global variable that’ll persist, but a `let` block is a simple alternative:
> 
> ```julia-auto
> let sq = x->x^2
> dosomething2(sq, 8)
> end
> 
> ```
> 
> in fact if you allow yourself to annotate `MyType` on the assignment, it converts and you can just use the original function:
> 
> ```julia-auto
> let sq::MyType = x->x^2
> dosomething(sq, 8)
> end
> 
> ```
> 
> clarity which I would prefer in the case where `MyType` isn’t actually special and there’s a whole host of types to convert functions to.

For a multi-line anonymous function, I’m understanding that you are saying that you would prefer syntax like:

```julia
dosomething(convert(MyType, x -> begin
   println("starting...")
   for vv in some_global_variable
      if vv > length(vv)
         fsad....
         fdsa...
      elseif dsfdsa....
         fsdaf...
         fafad...
      else
         error("dsfasdfada")
      end
   end
end), 8)

```

or for the `let` version:

```julia
let sq = x -> begin
   println("starting...")
   for vv in some_global_variable
      if vv > length(vv)
         fsad....
         fdsa...
      elseif dsfdsa....
         fsdaf...
         fafad...
      else
         error("dsfasdfada")
      end
   end
   dosomething(convert(MyType, sq), 8)
end

```

It is especially in situations like this where I feel that `do` syntax looks neater which is why I want to preserve the option to use it.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 25, 2024, 7:55am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/10 "2024-07-25T07:55:43Z")

</div>

> [@GHTaarn](#):
>
> I’m understanding that you are saying that you would prefer syntax like:

I wasn’t talking about the syntax but specifying the type to convert the function to. You have confirmed that there are many such types, and manual specification makes far more sense than higher order boilerplate functions inlining the types e.g. `dosomething1`, `dosomething2`, etc.

It is conceivable for a macro to transform the neater `do` syntax to the code converting the input function prior to the higher-order call. I imagine:

```julia
@convertdo MyType dosomething(8) do x
  x^2
end

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 26, 2024, 1:31am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/11 "2024-07-26T01:31:36Z")

</div>

Not sure if this really works, I usually take way longer to get a macro working but:

```julia
julia> macro convertdo(type::Symbol, doblock::Expr)
         if doblock.head != :do throw(ArgumentError("@convertdo's 2nd argument must be do-block.")) end
         callexpr = doblock.args[1]
         anonfunc = doblock.args[2]
         insert!(callexpr.args, 2, :(convert($type, $anonfunc)) )
         callexpr
       end
@convertdo (macro with 1 method)

julia> @macroexpand @convertdo MyType dosomething(8) do x
         x^2
       end
:(Main.dosomething(Main.convert(Main.MyType, ((var"#1#x",)->begin
                  #= REPL[7]:2 =#
                  var"#1#x" ^ 2
              end)), 8))

julia> @convertdo MyType dosomething(8) do x
         x^2
       end
Hello 8
nothing says hello

```

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 26, 2024, 4:34am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/12 "2024-07-26T04:34:43Z")

</div>

Thank you, I have tried it on my own code and it has worked on everything except when keyword arguments are specified with `;`, e.g.:

```julia
dosomething(x::MyType, args...; kwargs...) = println(args..., kwargs)

@convertdo MyType dosomething([1 2;3 4], "EE"; hi="hello") do x
x^2
end

```

which produces an “invalid syntax” error. I don’t know enough about writing macros to find the bug, hopefully you or someone else can see what needs to be done.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 26, 2024, 5:25am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/13 "2024-07-26T05:25:38Z")

</div>

Thanks for the feedback, this is the sort of stuff that would be caught in a good test. From what I can tell, it’s because I just insert the converted function expression to the 2nd element, shifting the previous 2nd and following elements back, for the 1st argument of a call without `;`. For a call with `;`, the 2nd element instead holds the post-`;` arguments, and shifting into the 3rd element turns them into a mangled 2nd argument (see output of `@macroexpand`). I can make a check that gets rid of the mangling, hopefully that covers any calls because I don’t have the code to run tests:

```julia
julia> macro convertdo(type::Symbol, doblock::Expr)
         if doblock.head != :do throw(ArgumentError("@convertdo's 2nd argument must be do-block.")) end
         callexpr, anonfunc = doblock.args[1], doblock.args[2]
         arg1 = if (callexpr.args[2] isa Expr && callexpr.args[2].head == :parameters) 3 else 2 end
         insert!(callexpr.args, arg1, :(convert($type, $anonfunc)) )
         callexpr
       end
@convertdo (macro with 1 method)

```

---

<div class="post-metadata">

**Author:** ![GHTaarn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ghtaarn/32/216007_2.png) [@GHTaarn](https://discourse.julialang.org/u/GHTaarn)\
**Post date:** [July 27, 2024, 12:26am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/14 "2024-07-27T00:26:36Z")

</div>

Thank you, I have now tested this on quite a lot of examples (even a `@convertdo` inside a `@convertdo`) and it worked perfectly every time.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 27, 2024, 1:37am UTC](https://discourse.julialang.org/t/custom-implicit-type-conversion/117315/15 "2024-07-27T01:37:18Z")

</div>

Spotted a rookie macro mistake in my code. I only bothered to experiment a little within the `Main module`, so it doesn’t cover macro hygiene at all; basically, that’s whether the variables in the expression are considered to belong to the macro definition’s module or the macro call’s module. As it is, all the variables will be considered to belong to the macro definition module, but in this context, you likely want the code you write to belong to whereever you write it, and the only new symbol the macro introduces, `convert`, should belong to `Base` unconditionally. So, I’m putting the amended macro here, it’s a very small change:

```julia
julia> macro convertdo(type::Symbol, doblock::Expr)
         if doblock.head != :do throw(ArgumentError("@convertdo's 2nd argument must be do-block.")) end
         callexpr, anonfunc = doblock.args[1], doblock.args[2]
         arg1 = if (callexpr.args[2] isa Expr && callexpr.args[2].head == :parameters) 3 else 2 end
         insert!(callexpr.args, arg1, :(Base.convert($type, $anonfunc)) )
         esc(callexpr) # entire expression belongs to macro call module
       end
@convertdo (macro with 1 method)

julia> @macroexpand @convertdo MyType dosomething([1 2;3 4], "EE"; hi="hello") do x
       x^2
       end
:(dosomething(Base.convert(MyType, ((x,)->begin
                  #= REPL[4]:2 =#
                  x ^ 2
              end)), [1 2; 3 4], "EE"; hi = "hello"))

```

See how all the `Main._` from the first version’s `@macroexpand` vanishes, and the do-block’s argument stays as written instead of being changed? Those changes were the variables being resolved in the macro definition scope by default. If you ever need to use the macro outside its definition module, I expect this would fix the immediate bugs.
