# Help me find flaws in this pattern: spawning task from constructor

**URL:** <https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219>\
**Category:** General Usage\
**Tags:** question, agents, channels, actor-model\
**Created:** [July 29, 2023, 12:50am UTC](https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219 "2023-07-29T00:50:43Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [July 29, 2023, 12:50am UTC](https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219/1 "2023-07-29T00:50:44Z")

</div>

I hope the MWE is self-explanatory, but I’ll say a few words after the code block anyways:

```julia
struct Agent
    id::Int
    in::Channel
    out::Channel
    Agent(id, in, out) = new(id, in, out)
    function Agent(id, in, out, f::Function)
        x = new(id, in, out)
        Threads.@spawn f(x)
        return x
    end
end

# this is a test/MWE function, but imagine it's a complex agent with a complex state
function agent(x::Agent, f::Function)
    @info "Agent $(x.id) started"
    # lots of thread-safe / data-race free mutable state here
    state = 0
    for msg in x.in
        @info "Agent $(x.id) received $msg at state $state"
        put!(x.out, f(msg))
        state += 1
    end
end

behavior(x) = agent(x, a -> a + 1)
const AG = Agent(1, Channel{Int}(1), Channel{Int}(1), behavior)

put!(AG.in, 6)
sleep(1)
@info "out: $(take!(AG.out))"

readline()

```

The question mainly targets the tight coupling of the constructor (I can do the same with an outer constructor) with spawning a consumer/producer task.

The documentation is not very helpful in evaluating the sanity of such patterns.

I find the above quite nice on the ergonomics side of things - and I do not get any weird results to prohibit me from using it.

In a way, the object constructor also defines the object _behavior_. Now I can pass my object to other functions and create closures that can be used in the constructors of additional `Agent` instances.

In practice, by only changing the function passed to the constructor, I am basically chaining these functions in a thread-safe manner (since all the “state” mutation takes place _inside_ a channel).

Please note that my question is not referring to the potential danger of having those tasks floating around without any control/supervision (I could easily add a field `isalive::Ref{Bool}` to the `Agent` and then have a kill switch at hand that is readable from `agent` scope and also from the _outside world_). But let’s not go there for now.

---

<div class="post-metadata">

**Author:** ![fbanning](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fbanning/32/14972_2.png) [@fbanning](https://discourse.julialang.org/u/fbanning)\
**Post date:** [July 31, 2023, 9:32am UTC](https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219/2 "2023-07-31T09:32:43Z")

</div>

> [@algunion](#):
>
> I hope the MWE is self-explanatory

This is likely 100% on me but it really isn’t self-explanatory to me. Could you please elaborate a bit on what you’re aiming to do and how this code helps you with that? Maybe afterwards I can infer from that what your question is but tbh I would also prefer if you could write it out a bit more explicitly.

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [July 31, 2023, 11:35am UTC](https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219/3 "2023-07-31T11:35:22Z")

</div>

> [@fbanning](#):
>
> This is likely 100% on me but it really isn’t self-explanatory to me. Could you please elaborate a bit on what you’re aiming to do and how this code helps you with that? Maybe afterwards I can infer from that what your question is but tbh I would also prefer if you could write it out a bit more explicitly.

Everything that the MWE accomplishes can also be done by spawning the task after the instance is created. So it is not like _here is a little something that I want to achieve and is not possible to achieve in any other way_.

As I said in the OP: I consider my proposal as an ergonomic way to couple object creation with object behavior. Also, I think it reduces the complexity of the actor-model approach (especially given the fact that you could define even more complex behavior - like having the actor _subscribe_ to other actors at the creation time). Even more, it could facilitate a simplified syntax for achieving complex actor hierarchies (see [Akka](https://doc.akka.io/docs/akka/current/typed/actors.html)).

So there is no such thing as _one thing that I want to achieve_. Conceptually speaking I am building complex actors interactions within complex actor hierarchies (and there is no package in Julia satisfying my needs).

So my question would be reduced to mere _spawning tasks from inside a constructor_ thing (because I am already convinced that the coupling of object creation with behavior definition is a good thing - especially given the fact that I can create multiple constructors and have creation, behavior + interaction + hierarchy determined by a simple `MyActor(...)` instantiation call).

P. S. Please note that in my scenario, the `struct` is simply a container to link together multiple channels (and I can also add an ID and even an actor type if I define it as a `parametric composite`). So basically, all is reduced more or less to the behavior. But instead of having multiple agent constructors + multiple methods for defining the behavior, I find that only using constructors for both greatly reduces the complexity (and number of LOC). So all this depends on whether it is acceptable to spawn tasks from within constructors.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [July 31, 2023, 5:06pm UTC](https://discourse.julialang.org/t/help-me-find-flaws-in-this-pattern-spawning-task-from-constructor/102219/4 "2023-07-31T17:06:04Z")

</div>

Seems like a reasonable approach for defining a actor like object. I would either couple it even more strongly, i.e., hide the behavior from the `agent` function as well such that the constructor could be called as `Agent(1, Channel{Int}(1), Channel{Int}(1), a -> a + 1)` or instead just expose/export an API consisting of functions only. Thus, the user could `create_agent(id, in_chan, out_chan, fun)`, `send(agent, inp)` etc. and does not know about your internal data type. In that case, whether the agent behavior is stored in the struct or simply in a closure when spawning the agent task would be purely an implementation detail – which you can decide based on testability or such.
