# Calling a more generic method with Parametric Types

**URL:** <https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537>\
**Category:** General Usage\
**Tags:** question, agents, reflection\
**Created:** [October 29, 2023, 2:13pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537 "2023-10-29T14:13:07Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![rmellema](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmellema/32/202796_2.png) [@rmellema](https://discourse.julialang.org/u/rmellema)\
**Post date:** [October 29, 2023, 2:13pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/1 "2023-10-29T14:13:07Z")

</div>

Hey all,

For some context, I want to do some work with the Agents.jl package in which I want to try to keep track of the movement of the agents (so I can draw their paths and manipulate them later). The easiest way I think I can make this work is by writing a more specific agent type, with a specific `move_agent!` method, and then use invoke to call the more generic `move_agent!` methods that Agents.jl provides. The problem that I’m running into though is that I cannot get the invoke call which I think has to do with the way that parametric types work in Julia.

What I have right now is that I wrote a method with signature `move_agent!(::T, ::Agents.ValidPos, ::ABM{S, T}) where {S <: ContinuousSpace, T <: AbstractTurtle}`. Now I want to call the method with signature `move_agent!(::A, ::Agents.ValidPos, ::ABM{<:ContinuousSpace{D}, A}) where {D, A <: AbstractAgent}`. Now we have that `Turtle <: AbstractTurtle <: AbstractAgent`, but the problem (I think) is that `ABM{ContinuousSpace{2}, AbstractTurtle} <: ABM{ContinuousSpace{2}, AbstractAgent}` does not hold. This causes `invoke` to give the error “argument type error”. However, if I make the type in the `invoke` call more general it becomes ambiguous.

Is there a solution out of this problem, am I just using `invoke` wrong, or is what I’m trying to do just impossible given the current type system and constraints? Copying the current definition of `move_agent!(::A, ::Agents.ValidPos, ::ABM{<:ContinuousSpace{D}, A}) where {D, A <: AbstractAgent}` into `move_agent!(::T, ::Agents.ValidPos, ::ABM{S, T}) where {S <: ContinuousSpace, T <: AbstractTurtle}` will of course work but a less brittle solution would have my preference.

The code that I have at the moment only depends on Agents.jl being installed and is:

```julia
using Agents

abstract type AbstractTurtle <: AbstractAgent end

@agent Turtle ContinuousAgent{2} AbstractTurtle begin end

function Agents.move_agent!(turtle::T, pos::Agents.ValidPos, model::ABM{S, T}) where {T <: AbstractTurtle, S <: ContinuousSpace}
    start = turtle.pos
    @invoke move_agent!(turtle::AbstractAgent, pos, model::ABM{S, AbstractAgent})
    println("Turtle started at $start, ended at $(turtle.pos)")
end

model = AgentBasedModel(Turtle, ContinuousSpace((50, 50)))
agent = add_agent!(model, (1, 0))
move_agent!(agent, (1,1), model)

```

Julia version: 1.9.3  
Agents.jl version: 5.17.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:** [October 29, 2023, 3:40pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/2 "2023-10-29T15:40:53Z")

</div>

I think the followung may help you.

```julia
julia> Array{1, Int64} <: Array{1, Real}
false

julia> Array{1, Int64} <: Array{1, <: Real}
true

```

---

<div class="post-metadata">

**Author:** ![rmellema](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmellema/32/202796_2.png) [@rmellema](https://discourse.julialang.org/u/rmellema)\
**Post date:** [October 29, 2023, 3:48pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/3 "2023-10-29T15:48:09Z")

</div>

Thanks for the response! However, if I change the invoke line to `move_agent!(turtle::AbstractAgent, pos::Agents.ValidPos, model::ABM{S, <: AbstractAgent})` it says the line is too ambiguous again 😕 So that doesn’t really help.

However I did not know you could do that this way. Thats neat.

---

<div class="post-metadata">

**Author:** ![rmellema](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmellema/32/202796_2.png) [@rmellema](https://discourse.julialang.org/u/rmellema)\
**Post date:** [October 29, 2023, 4:20pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/4 "2023-10-29T16:20:12Z")

</div>

This is actually really weird to me, since it still lists `move_agent!(::T, ::Agents.ValidPos, ::ABM{S, T}) where {S <: ContinuousSpace, T <: AbstractTurtle}` as an option, even though it should treat the first argument as an `AbstractAgent`, and it lists `move_agent!(::A, ::Agents.ValidPos, ::ABM{<:AbstractSpace, A}) where A <: AbstractAgent` as an option even though there is a more specific alternative for that.

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [October 29, 2023, 4:42pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/5 "2023-10-29T16:42:57Z")

</div>

Does this work for you?

```julia
using Agents

abstract type AbstractTurtle <: AbstractAgent end

@agent Turtle ContinuousAgent{2} AbstractTurtle begin end

function move_agent!(turtle, pos, model)
    start = turtle.pos
    Agents.move_agent!(turtle, pos, model)
    println("Turtle started at $start, ended at $(turtle.pos)")
end

model = AgentBasedModel(Turtle, ContinuousSpace((50, 50)))
agent = add_agent!(model, (1, 0))
move_agent!(agent, (1,1), model)

```

last line prints

`Turtle started at (16.746942825197834, 38.018146848045035), ended at (1.0, 1.0)`

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [October 29, 2023, 4:47pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/6 "2023-10-29T16:47:01Z")

</div>

> [@rmellema](#):
>
> For some context, I want to do some work with the Agents.jl package in which I want to try to keep track of the movement of the agents (so I can draw their paths and manipulate them later). The easiest way I think I can make this work is by writing a more specific agent type, with a specific `move_agent!` method, and then use invoke to call the more generic `move_agent!` methods that Agents.jl provides.

I can think of a simpler way that doesn’t involve chaing the behavior one of the core functions of the library. Simply add a field `path::Vector{NTuple{2, Int}}` to your agent type. Create a **new function** `my_move!` which does two and only two things:

```julia
push!(agent.path, agent.pos)
move_agent!(...)

```

Instead of calling your overloaded `move_agent!`, call the `my_move!` instead.

---

<div class="post-metadata">

**Author:** ![rmellema](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rmellema/32/202796_2.png) [@rmellema](https://discourse.julialang.org/u/rmellema)\
**Post date:** [October 29, 2023, 4:57pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/7 "2023-10-29T16:57:02Z")

</div>

This would work, but like the suggestion made my @Datseris that would mean I cannot use any of the other agent move functions anymore, which means if I want to I would have to reimplement those on top of this function. And at that point I might as well just copy the code from `move_agent!(::A, ::Agents.ValidPos, ::ABM{<:ContinuousSpace{D}, A}) where {D, A <: AbstractAgent}`.

Like if I’m reimplementing parts of Agents.jl anyway, might as well pick the bit that is less effort.

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [October 29, 2023, 4:59pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/8 "2023-10-29T16:59:19Z")

</div>

Ah ok now I think I’m following, for example you want to use the `walk!` function with your new `move_agent!` function and so you need some sort of overloading

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [October 29, 2023, 5:46pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/9 "2023-10-29T17:46:59Z")

</div>

ok I guess I understood what is the problem and I have a fix: simply use the `dev` version of Agents which should be released soon.

In that the signature is

`move_agent!(agent::AbstractAgent, pos::ValidPos, model::ABM{<:ContinuousSpace})`

while on 5.17 it is

`move_agent!(agent::A, pos::ValidPos, model::ABM{<:ContinuousSpace{D},A}) where {D, A<:AbstractAgent}`

I think that the signature in 5.17 conflicts with the one you want to use since `A` in that definition is a subtype of `AbstractAgent`, I think it is harder in that case to disambiguate it, but It’s not clear to me why (I think that `A` becomes the concrete version and that’s why your method can’t call it), Maybe someone more knowledgeable can give more details.

In the `dev` version this works:

```julia
using Agents

abstract type AbstractTurtle <: AbstractAgent end

@agent Turtle ContinuousAgent{2} AbstractTurtle begin end

function Agents.move_agent!(turtle::Turtle, pos::Agents.ValidPos, model::ABM{S}) where {S <: ContinuousSpace}
    start = turtle.pos
    invoke(Agents.move_agent!, Tuple{AbstractAgent, Agents.ValidPos, ABM}, turtle, pos, model)
    println("Turtle started at $start, ended at $(turtle.pos)")
end

model = AgentBasedModel(Turtle, ContinuousSpace((50, 50)))
agent = add_agent!(model, (1, 0))
move_agent!(agent, (1,1), model)
walk!(agent, (2.0, 2.0), model) # this uses your version

```

This means that we probably need to update `Agents.jl` using wherever possible the first type of signature 🙂

---

<div class="post-metadata">

**Author:** ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)\
**Post date:** [October 29, 2023, 6:58pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/10 "2023-10-29T18:58:33Z")

</div>

But it seems weird also to me that still I can’t disambiguate those version in 5.17 since e.g.

```julia
f(x::T) where {T<:Integer} = println("First version")
g(x) = f(x)
function f(x::Int)
    println("Second Version")
    invoke(f, Tuple{Integer}, x)
end
g(1)

```

works returning

```julia
Second version
First Version

```

but instead something which seems equivalent to me in Agents.jl 5.17 doesn’t

```julia
using Agents

@agent Turtle ContinuousAgent{2} begin end

function Agents.move_agent!(turtle::Turtle, pos, model)
    start = turtle.pos
    invoke(Agents.move_agent!, Tuple{AbstractAgent, Any, Any}, turtle, pos, model)
    println("Turtle started at $start, ended at $(turtle.pos)")
end

model = AgentBasedModel(Turtle, ContinuousSpace((50, 50)))
agent = add_agent!(model, (1, 0))
move_agent!(agent, (1,1), model)

```

this throws

```julia
julia> move_agent!(agent, (1,1), model)
ERROR: MethodError: move_agent!(::Turtle, ::Tuple{Int64, Int64}, ::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, Turtle, typeof(Agents.Schedulers.fastest), Nothing, Random.TaskLocalRNG}) is ambiguous.

Candidates:
  move_agent!(agent::A, pos::Union{Int64, Tuple{Int64, Int64, Float64}, Tuple{Vararg{Int64, N}}, Tuple{Vararg{var"#s5", M}} where var"#s5"<:AbstractFloat} where {N, M}, model::AgentBasedModel{<:ContinuousSpace{D}, A}) where {D, A<:AbstractAgent}
    @ Agents ~/.julia/packages/Agents/xtlGn/src/spaces/continuous.jl:131
  move_agent!(agent::A, pos::Union{Int64, Tuple{Int64, Int64, Float64}, Tuple{Vararg{Int64, N}}, Tuple{Vararg{var"#s5", M}} where var"#s5"<:AbstractFloat} where {N, M}, model::AgentBasedModel{<:Agents.AbstractSpace, A}) where A<:AbstractAgent
    @ Agents ~/.julia/packages/Agents/xtlGn/src/core/space_interaction_API.jl:125
  move_agent!(turtle::Turtle, pos, model)
    @ Main REPL[7]:1

Possible fix, define
  move_agent!(::Turtle, ::Union{Int64, Tuple{Int64, Int64, Float64}, Tuple{Vararg{Int64, N}}, Tuple{Vararg{var"#s5", M}} where var"#s5"<:AbstractFloat} where {N, M}, ::AgentBasedModel{<:ContinuousSpace{D}, A}) where {D, A<:AbstractAgent}

```

which is weird

---

<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:** [October 29, 2023, 7:27pm UTC](https://discourse.julialang.org/t/calling-a-more-generic-method-with-parametric-types/105537/11 "2023-10-29T19:27:08Z")

</div>

Could you sbow us the entire error please?

Ambiguity means you have two methods that are of equal specificity and Julia cannot decide which to pick.
