-
Notifications
You must be signed in to change notification settings - Fork 20
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
Next: A Proposal to easily create Operators from Actors. #47
Comments
I had the same idea before, and I fixed it by implementing an I hope this helps! |
Hey! Thanks for using the package.
An actor which is both an actor and an observable is by definition a
In my head there is none, because again an
That is semantically wrong. This was a deliberate design choice that you cannot subscribe on
I'm not entirely sure I understand the purpose of this operator? As far as I can read from the code (I might be wrong) it multicasts the same messages to two actors, but there is already the Maybe if you explain your use case I can come up with a better solution to your problem. P.S. The |
Hello @bvdmitri, Thank you for your message. And thank you for Rocket.jl !
source = from(1:42) |> circularkeep(3)
subscribe!(source, logger())
|
Exactly! The pattern seems natural and redundant (at least for me). I was thinking about how to have it as a clean pattern to reuse. Thanks for sharing. |
Well, I would do something like this probably using Rocket, DataStructures
source = from(1:10) |> scan(CircularBuffer{Int}, (v, c) -> push!(c, v), CircularBuffer{Int}(3))
subscribe!(source, logger) prints
And you can do something like operatorcircularkeep(n) = scan(CircularBuffer{Any}, (v, c) -> push!(c, v), CircularBuffer{Any}(n))
subscribe!(from(1:10) |> operatorcircularkeep(3), logger())
subscribe!(from(1:10) |> operatorcircularkeep(5), logger()) The second approach is convenient, but type-unstable (I used Leveraging If you want to explore other possibilities you can also look into the |
@bvdmitri thank you for that beautiful example! It's perfectly clear now. For the rest, I'll stick to @wouterwln's pattern which I already use. Though I have the pattern a lot. :) Thank you gentlemen! |
To comment further for anyone coming after: Here is what I need following @bvdmitri 's suggestion: movingaverage(n) = scan(Vector{Union{Missing, Int}}, (val, vec) -> trail(vec, val), Vector{Union{Missing, Int}}(missing, n)) |> map(Union{Missing, Float64}, vec -> Statistics.mean(vec)) |> filter(v -> !ismissing(v))
function trail(vec::Vector, val::Int)
circshift!(vec, -1)
vec[end] = val
return vec
end
movingaverage(n) = scan(Vector{Union{Missing, Int}}, (val, vec) -> trail(vec, val), Vector{Union{Missing, Int}}(missing, n)) |> map(Union{Missing, Float64}, vec -> Statistics.mean(vec)) |> filter(v -> !ismissing(v))
source = from(1:10) |> movingaverage(3)
subscribe!(source, logger())
[LogActor] Data: 2.0
[LogActor] Data: 3.0
[LogActor] Data: 4.0
[LogActor] Data: 5.0
[LogActor] Data: 6.0
[LogActor] Data: 7.0
[LogActor] Data: 8.0
[LogActor] Data: 9.0
[LogActor] Completed Trying to remove the type unstability I came up with: trail(n::Int) = TrailOperator(n)
struct TrailOperator <: InferableOperator
length::Int
function TrailOperator(length::Int)
length > 0 || error("TrailOperator: $(length) must be positive to calculate a moving average.")
return new(length)
end
end
Rocket.operator_right(::TrailOperator, ::Type{L}) where {L} = Vector{Union{Missing, L}} #IndicatorType(SMAIndicator, length, L)
function trail(val::Int, vec::Vector)
circshift!(vec, -1)
vec[end] = val
return vec
end
function Rocket.on_call!(::Type{L}, ::Type{R}, operator::TrailOperator, source) where {L,R}
return proxy(R, source, Rocket.ScanProxy{L, R, Function}(trail, Vector{Union{Missing, L}}(missing, operator.length)))
end
sma(n::Int) = trail(n) |> map(Union{Missing, Float64}, vec -> Statistics.mean(vec)) |> filter(v -> !ismissing(v))
[LogActor] Data: 2.0
[LogActor] Data: 3.0
[LogActor] Data: 4.0
[LogActor] Data: 5.0
[LogActor] Data: 6.0
[LogActor] Data: 7.0
[LogActor] Data: 8.0
[LogActor] Data: 9.0
[LogActor] Completed Same thing, though leveraging Best! Thanks again. |
Hello,
On a high level, I need to be able to subscribe to Actors. This is nonsensical, but numerous actors do not act as pure sinks, but instead as processing Operators in a data flow.
A theoretical example:
To my understanding, this isn't possible. The workaround I'm using is to pass a Subject and hack subscribe!()
This is a similar design to what Actors that are through Proxies in Operators do. While I wish there would be a clean pattern to declare an Actor both an Actor and an Observable/Subject, I thought of an in-between step that matches the current design.
The idea occurred to me while looking at the
CircularKeep
Actor. I needed that Actor to actuallynext!
his buffer every time new data is received. SinceCircularKeep
is a 'sink' Actor, the only way to do that would be to rewrite an Actor that copies the behavior ofCircularKeep
:So this was painfully redundant, and there may be some pattern to turn any Actor into its Operator equivalent. (Though turning it into an Observable/Subject still makes sense to me.)
So I came up with a
next()
Operator. I would love to hear your thoughts as I think it would be a nice addition, possibly reduce the code base and allow for any actor to be turned into an operator easily:Best! 🚀
The text was updated successfully, but these errors were encountered: