# Struct with Multiple Dispatch

Hello,

I’m new to Julia and am watching the Juliacon 2020 video series. I have a question about the use of a mutable struct in this video here.

From what I am seeing, I think he defines a mutable struct with some fields, then defines two methods for that struct. So If I wanted to do something similar I could do

``````a = 3
b = 5
``````
``````# Example of multiple dispatch with a mutable struct
begin
mutable struct MyStruc
C::Float64
a::Int64
end
# Multiple dispatch of struct
MyStruc(C::Float64; aValue=a) = MyStruc(C,aValue)
MyStruc(C::Int64; bValue=b) = MyStruc(C,bValue)
end
``````

That seems to work. But the syntax is strange to me, doing `MyStruc = MyStruc`. Am I correct there are two methods for this struct? I’m using Pluto and I don’t see “MyStruct with two methods” above the cell with the struct defined (unlike when I define a function with multiple methods and it does say the number of corresponding methods above the cell).

I also tried the following

``````# Example of multiple dispatch with a mutable struct
begin
mutable struct MyStruc
C::Float64
a::Int64
end
# Multiple dispatch of struct
MyStruc(C::Float64; aValue=a) = MyStruc(C,aValue)
MyStruc(C::Int64, a::Float64) = MyStruc(C,a)
end
``````

But when I try to use the second method like this

``````TestStruct = MyStruc(3, 101.1)
``````

I get a StackOverflowError. Not sure why though - would appreciate any comments and insight on this.

``````mutable struct MyStruc
C::Float64
a::Int64
end
``````

is enough.

``````julia> TestStruct = MyStruc(3.1, 101)
MyStruc(3.1, 101)

julia> TestStruct = MyStruc(3, 101)
MyStruc(3.0, 101)
``````

but:

``````julia> TestStruct = MyStruc(3, 101.1)
ERROR: InexactError: Int64(101.1)
Stacktrace: ...
``````

to avoid this you could define a method (multiple dispatch):

`MyStruc(C::Int64, a::Float64) = MyStruc(C, Int(round(a)))`

then you get:

``````julia> TestStruct = MyStruc(3, 101.1)
MyStruc(3.0, 101)
``````

[quote=“TI36XPro, post:1, topic:53118”]
Am I correct there are two methods for this struct?

No, there are four. You can check with the `methods` function:

``````julia> methods(MyStruc)
# 4 methods for type constructor:
 MyStruc(C::Float64; aValue) in Main at REPL:2
 MyStruc(C::Float64, a::Int64) in Main at REPL:2
 MyStruc(C::Int64; bValue) in Main at REPL:1
 MyStruc(C, a) in Main at REPL:2
``````

 and  are the methods you defined, and  and  are the default methods that are always defined when you create a new struct (unless you define inner constructors).

Thank you for the reply. Why can’t I do

``````MyStruc(C::Int64, a::Float64) = MyStruc(C, a)
MyStruc(-3, 5.4)
``````

I’m confused because in my original declaration I have `C::Float64` and it didn’t seem to mind defining a new method with that as an `Int64`. But if I try it the other way around, with `a` where it is initially an `Int64` in my original declaration and then later on I try to define a new method with it as a `Float64`, it gives me a stackoverflow error and I have to use round as you show, to work correctly.

Because of what it means to go from `Int` to `Float` and vice versa?

It doesn’t have anything to do with the order of the definition. The problem is that in your second example, you define the method `MyStruc(C::Int64, a::Float64)` that always calls itself. Thus, you get a stack overflow (basically, the method ends up calling itself over and over again, so it can never return). In your first example, your methods have keyword arguments and call methods without keyword arguments, so none of the methods call themselves.

This does call only itself since `5.4 isa Float64` and it matches only your method definition above but not your `struct`, which has an `Int64` as 2nd element. Therefore the stack overflow. It says you actually, which method it calls:

``````julia> TestStruct = MyStruc(3, 5.4)
ERROR: StackOverflowError:
Stacktrace:
 MyStruc(::Int64, ::Float64) at ./REPL:1 (repeats 79984 times)
``````

you see it does not call your struct definition.

Thank you both - I think that makes a little more sense now.

One last question - you both said I was creating a method that called itself. Isn’t that what is happening here too? No keyword arguments are used to distinguish the method from itself like before. Yet, this does work. Because the presence of the `Int` function?

``````MyStruc(C::Float64, a::Float64) = MyStruc(C, Int(round(a)))
``````

yes, that then matches your `struct` definition better with an `Int64` at the second place. So that gets dispatched as you probably want.

``````julia> MyStruc(C::Float64, a::Float64) = MyStruc(C, Int(round(a)))
MyStruc

julia> TestStruct = MyStruc(3, 101.1)
MyStruc(3.0, 101)
``````

This does not call itself because it has the signature `MyStruc(::Float64, ::Float64)` and converts the second float to Int, so it calls `MyStruc(::Float64, ::Int)` then. This is a different method of the same function. See:

Thank you all for explaining this to me and answering my questions.

One last thing. Am I technically creating multiple dispatches of a function called `MyStruc` by doing `MyStruc(C::Float64; aValue=a) = MyStruc(C,aValue)` and it just so happens I’ve also already defined a new type called `MyStruc`? I could just as easily have done the following?

``````begin
mutable struct MyStruc
C::Float64
a::Int64
end
# Multiple dispatch of struct
MyFunction(C::Float64; aValue=a) = MyStruc(C,aValue)
MyFunction(C::Int64; bValue=b) = MyStruc(C,bValue)
end
``````

If that’s the case - I’m not really creating multiple dispatches of a struct here am I? Sorry if I am overthinking things here.

I think it a bit misleading if you speak of “Multiple dispatch of struct”. There is multiple function dispatch. I’m starting with a fresh REPL here. I you define your `struct MyStruc`, you implicitly define a `function MyStruc` with two methods:

``````julia> mutable struct MyStruc
C::Float64
a::Int64
end

julia> methods(MyStruc)
# 2 methods for type constructor:
 MyStruc(C::Float64, a::Int64) in Main at REPL:2
 MyStruc(C, a) in Main at REPL:2
``````

but it does not do what you want, since

``````julia> MyStruc(123, 4.56)
ERROR: InexactError: Int64(4.56)
Stacktrace: ...
``````

Therefore you define another method like

``````julia> MyStruc(C::Int64, a::Float64) = MyStruc(C, Int(round(a)))
MyStruc

julia> methods(MyStruc)
# 3 methods for type constructor:
 MyStruc(C::Float64, a::Int64) in Main at REPL:2
 MyStruc(C::Int64, a::Float64) in Main at REPL:1
 MyStruc(C, a) in Main at REPL:2

julia> MyStruc(123, 4.56)
MyStruc(123.0, 5)
``````

In your case then you create a 2nd function, `MyFunction` with two methods, dispatching on `Float64` and `Int64`:

``````julia> MyFunction(C::Float64; aValue) = MyStruc(C,aValue)
MyFunction (generic function with 2 methods)

julia> MyFunction(C::Int64; bValue) = MyStruc(C,bValue)
MyFunction (generic function with 2 methods)

julia> methods(MyFunction)
# 2 methods for generic function "MyFunction":
 MyFunction(C::Int64; bValue) in Main at REPL:1
 MyFunction(C::Float64; aValue) in Main at REPL:1
``````

The problem with the 2nd parameter is not resolved by that since without the 3rd method for `MyStruc` above you get the same `InexactError` if you do `MyFunction(123, bValue=4.56)`. Therefore `MyFunction` does not help.

BTW: `MyFunction(C::Float64; aValue=a)` assumes `a` being a global variable or a constant present in the current scope.

1 Like