Akkling
Akkling copied to clipboard
Ideas on further API change
There are several ideas I have on potential changes concerning current API, some of them are breaking changes:
- Move away from returning effects. I still need to evaluate on that, but I think, it might be better to leave effect-free code in user hands. Requiring to return effect is pretty ugly in longer run, and it looks bad in terms of composition with user-defined code. Simple
unit
return may be enough. - Move away from
AskResult<>
: by default we should be able to interop with ask pattern just by usinglet! response = ref <? request
and not having to unwrap the code each time if not necessary. If someone wants to have it presented in form of an object, he could use try pattern linelet 'try block = try Success <| block() with e -> Failure e
. - Since we expose actor context directly as function parameter, we might want to simply provide a mock for it in TestKit, that will record all method calls in form of list of events that we could use for assertions later. This way we could make unit tests on actors without need of creating a whole actor system, just passing context mock.
- Look forward If we are able to fully integrate with
async { ... }
computation expression. The idea here is to be able to define actor not only fromactor { ... }
but directly from async look as well.
In general I'd like to reduce a footprint of Akkling API in user code in a way that user could potentially define his/her domain logic with minimal dependencies to Akka.NET/Akkling libraries.
I like 2nd, 3d and 4th points. I like your general idea. But what about effects? I understand that they are not following the idea of reducing API, but they are still very useful. Maybe they can be an optional extension library? (sorry if I've made mistakes in the text)
- Would this move to using side effects instead?
- Awesome, this has always bugged me, I think I commented it on it before
- I've started doing something similar already, I can call stepNext on an an effect and collect a stream of tell events.
- This sounds cool, I've also wanted to be able to compose
actor
. Possibly call Recieve in a nested fashion. It sort of worked, one of the things I played with was creating my of ResultEffect type to get the value out but it was pretty clunky.
@Lavinski :
- With current design we already have side effects. Things like setting up receive timeout, even sending a message to another actor, are actually side effects. While it is possible to build side-effect free code by the user, making a whole API to work that way seems to be counter-productive. But as I said this decision still needs to be evaluated.
- AFAIK you can call Receive multiple times to get multiple messages.
async
can also be composed insideactor
expression (starting from v0.5 if I remember correctly). What I want to do is to make props creator to work with both actor/async expressions.
@Horusiath How do you think, when will your API changes be started/released? I want to use Akkling at my work, but I've got some doubts about the status of it. Is it production ready now? If I start using it now, how much time should I consider for the subsequent migration to the new API?
@Horusiath
With regards to composing actor
I'm taking about calling Receive once in a nested fashion.
let receivePreProcess mailbox = actor {
let! msg = mailbox.Receive()
match msg with
| :? X -> Ignore
| _ -> ReturnValue msg
}
let rec loop () = actor {
let! message = receivePreProcess mailbox
loop ()
}
@Neftedollar most of the API will remain intact, while things like Effects and AskResult will probably be simplified, this won't be as noisy change as one would except in user code. I.e. while I want to get rid of Effect<>
, user intent will still be possible to declare directly via returned behavior (see how akka-typed introduced behaviors).
Sample code:
let ref = spawn system "my-actor" <| props(fun ctx ->
let rec loop () = actor {
let! msg = m.Receive ()
match msg with
| "stop" -> return Stop
| "unhandle" -> return Unhandled
| x ->
printfn "%s" x
return! loop ()
}
loop ())
This looks exactly like existing code however, Stop
and Unhandled
won't be instances of Effect<>
interface any more, they will be behaviors (just like the loop
function itself).
I would like a ReceiveAny
function on the context. Background: Often, an actor has child actors that do some elaborate work, and I can't always use <?
, because I have scenarios where I , e.G., send an 'A
to the child and get multiple 'B
back. However, I don't want to make all my actors Actor<obj>
.
I want to have something like this:
let floater (ctx: Actor<int>) =
let rec loop() = actor {
let! i = ctx.Receive()
ctx.Sender() <! (float i)
return! loop()
}
loop()
let inter (ctx: Actor<int>) =
let f = spawnAnonymous ctx (props floater)
let rec loop() = actor {
match! ctx.ReceiveAny() with // should return obj
| :? int as i -> f <! i
| :? float as flt -> printfn "Received a float: %f" flt
return! loop()
}
loop()
So, the actor still only accepts int
from the outside but can accept anything from the inside.