Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend @agent macro to allow defining default values with Base.@kwdef #709

Closed
fbanning opened this issue Nov 17, 2022 · 8 comments
Closed
Assignees
Labels
agent-construction About making agents enhancement New feature or request

Comments

@fbanning
Copy link
Member

fbanning commented Nov 17, 2022

I make heavy use of Base.@kwdef when creating my ABMs because it

  1. makes it clear which field's value is set during agent creation,
  2. allows to set default values for each field, and
  3. allows to skip those fields with existing default values.

A simple example:

julia> Base.@kwdef mutable struct Worker <: AbstractAgent
           id::Int
           age::Int = 16
           moneyz::Float64
       end
Worker

julia> Worker(id = 1, moneyz = 10)
Worker(1, 16, 10.0)

The new @agent macro, which we want to switch to completely at some point in the future, does not allow this:

julia> Base.@kwdef @agent Worker2 NoSpaceAgent begin
           age::Int = 16
           moneyz::Float64
       end
ERROR: Invalid usage of @kwdef
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:35
  [2] var"@kwdef"(__source__::LineNumberNode, __module__::Module, expr::Any)
    @ Base ./util.jl:478
  [3] eval
    @ ./boot.jl:368 [inlined]
  [4] eval
    @ ./Base.jl:65 [inlined]
  [5] repleval(m::Module, code::Expr, #unused#::String)
    @ VSCodeServer ~/.vscode/extensions/julialang.language-julia-1.38.2/scripts/packages/VSCodeServer/src/repl.jl:222
  [6] (::VSCodeServer.var"#107#109"{Module, Expr, REPL.LineEditREPL, REPL.LineEdit.Prompt})()
    @ VSCodeServer ~/.vscode/extensions/julialang.language-julia-1.38.2/scripts/packages/VSCodeServer/src/repl.jl:186
  [7] with_logstate(f::Function, logstate::Any)
    @ Base.CoreLogging ./logging.jl:511
  [8] with_logger
    @ ./logging.jl:623 [inlined]
  [9] (::VSCodeServer.var"#106#108"{Module, Expr, REPL.LineEditREPL, REPL.LineEdit.Prompt})()
    @ VSCodeServer ~/.vscode/extensions/julialang.language-julia-1.38.2/scripts/packages/VSCodeServer/src/repl.jl:187
 [10] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [11] invokelatest(::Any)
    @ Base ./essentials.jl:726
 [12] macro expansion
    @ ~/.vscode/extensions/julialang.language-julia-1.38.2/scripts/packages/VSCodeServer/src/eval.jl:34 [inlined]
 [13] (::VSCodeServer.var"#61#62")()
    @ VSCodeServer ./task.jl:484

This is naturally the case because Main.@kwdef doesn't know what to do with an @agent macro instead of a struct. It should be possible to add this functionality to the @agent macro though. While extending it might be possible, I think it makes more sense to create a new macro called @kwagent to allow for the expected behaviour.

Is anyone (@Datseris @AayushSabharwal @Libbum) against such an extension? If not, I'll try to implement it.

@fbanning fbanning self-assigned this Nov 17, 2022
@fbanning fbanning added bug Something isn't working enhancement New feature or request labels Nov 17, 2022
@AayushSabharwal
Copy link
Collaborator

Instead of adding a new macro, could we make it so that Base.@kwdef @agent or @agent Base.@kwdef just works?

@AayushSabharwal
Copy link
Collaborator

Additionally, this prompted some thought.
The macro is a shortcut and an easy way for us to control what goes in the user's agent that we need for bookkeeping, but it's arguably not the most "Julian" way of inheriting properties from another struct. Could we perhaps do it better, without a macro? I have 3 ideas (off the top of my head, from best to worst in my opinion):

  • All the data that would be inherited from @agent lives in the spaces/ABM struct. If the user wants the data or a part of it, they can access it through a function. E.g. position(agent, model)
  • The user just adds a metadata field to their struct, whose type depends on the space and can be whatever we want. They also always have a id::Int on their agent anyway. E.g. a meta::GridSpaceData{N} where GridSpaceData{N} is a struct containing a pos field, or maybe just an alias for NTuple{N,Int}. The user accesses the position through position(agent), so we can change GridSpaceData as we want.
  • All agents have to implement a series of functions (position(agent), position!(agent, new_pos)...). This is boilerplatey, but also the most typical way I've seen object-oriented-esque inheritance in Julia.

I'm entirely willing to accept that none of these three solutions are satisfactory and the one we have is the easiest and most ergonomic way of doing things, but also thought it best to at least put the ideas out there.

@Datseris
Copy link
Member

Datseris commented Dec 1, 2022

but it's arguably not the most "Julian" way of inheriting properties from another struct

There is no Julian way to do this because this is not supported from the base language. Im fne with making @agent work with @kwdef

@Datseris
Copy link
Member

Datseris commented Dec 1, 2022

the object orinted way is by far and wide the most intuitive one. It has the smallest learning curve, and requires the least amiunt of effot from the users. it also requires the least amount of effor from us the developers and the least amount of testing. this position function would need to be tested for every space otherwise.

In conclusion, i don't agree of changing one of the most fundamental designs of Agents.jl, just because base julia doesn't bother to support field inheritance yet. it will at some point in the future. Also, this would be so super breaking I am not sure how many users would like it.

@Datseris
Copy link
Member

Datseris commented Dec 1, 2022

im almost a fanatic of multiple dispatch. it is the most suitable paradigm for programming. but there are always cases where something else is better. Here for me it is a clear case of the object oriented way being just objectively better.

@AayushSabharwal
Copy link
Collaborator

Yeah that makes sense. I don't have any problem with the @agent setup as it is, just wanted to make sure there isn't another option we might be overlooking now that there's a discussion on it.

@Tortar
Copy link
Member

Tortar commented Jul 14, 2023

I have a working solution, it seems like adding Base.@kwdef to the quote definining the mutable struct just works!

@Tortar
Copy link
Member

Tortar commented Jul 14, 2023

so I would extend the @agent macro allowing for the functionality if nobody is against this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
agent-construction About making agents enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants