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

Merge streams with function #65

Closed
ronslow opened this Issue May 14, 2015 · 5 comments

Comments

Projects
None yet
2 participants
@ronslow

ronslow commented May 14, 2015

Would it be possible to have a function which merged two streams using a combining function?

The particular case which I have is that there is a Stream<A> and a Stream<B> and I need to merge them to create a Stream<(A,B)>

I imagine writing something like

let s1: Stream<A> = ...
let s2: Stream<B> = ...

let m : Stream<(A,B)> = fMerge(|a,b| (a,b), s1, s2);

Thanks

Robert

@aepsil0n aepsil0n added the question label May 15, 2015

@aepsil0n

This comment has been minimized.

Owner

aepsil0n commented May 15, 2015

Hi Robert,

Good to see people starting to use Carboxyl.

Regarding your question: the short answer is yes, but you have to specify how exactly this should be done. The way you describe it, this operation is not well-defined yet. After all, the event occurences in both streams may not happen at the same time, so it's not entirely obvious how to merge them.

Note that Stream::merge uses two streams of the same type so it simply fires all events from both. To mimick this behaviour for different types, you'd have to build some enum like this:

enum Either<A, B> { Left(A), Right(B) }
let m: Stream<Either<A, B>> = s1.map(Either::Left).merge(&s2.map(Either::Right));

To use a function however, you somehow need to convert at least one of the streams into a signal first, as you only have an event of either type A or type B at the same time.

One way to do this would be to hold the last occurence of one stream in a signal and make a snapshot, whenever the other stream fires an event:

let m: Stream<(A, B)> = s1.hold(initial).snapshot(&s2, |a, b| (a, b));

However, you have to provide an initial value for hold in case s2 fires before s1 does. (s1 and s2 could also swap their roles in this example, of course.)

Another possibility would be to merge them into a signal instead of a stream by holding both and using lift!:

let m: Signal<(A, B)> = lift!(|a, b| (a, b), &s1.hold(init1), &s2.hold(init2));

In that case you need initial values for both though.

Depending on your exact use case, there may be more ways to solve this issue, but these appear to be the most obvious.

@ronslow

This comment has been minimized.

ronslow commented May 15, 2015

Thanks Eduard. I think that Carboxyl is a really exciting project.

I think that perhaps, in the light of your answer, I was wrong to focus on Stream in the first place.

Perhaps my question should be this:

Is there a way of merging 2 Signal using a combining function.
So

     let s1 : Signal<A> = ..
     let s2: Signal<B> = ..
     let m : Signal<(A,B)> = sMerge (|a,b| (a,b),&s1, &s2);

Ultimately, what I am looking to do is to allow Carboxyl elements representing behaviour (continuous time varying values paired with a Maybe Event) to be composed using Arrow notation:

    trait Arrow<A,B,AB,BC,AC,BCD> {
     fn arr(fn(&A) -> &B) -> AB; // e.g. (A -> B) -> Signal A B
     fn bind(&self, &BC) -> AC; // e.g. Signal A B -> Signal  B C -> Signal A C
     fn first(&self, &AC) -> BCD; // e.g. Signal A C -> Signal (A, D) (C, D)
    }
  • and I now think that the element I should implement as an Arrow perhaps should be Signal rather than Stream
    impl<A,B,C,D> Arrow<A,B,Signal<B>,Signal<C>, Signal<C>, Signal<(B,C)> for Signal<A> {
      fn arr(&f : fn(&A) -> &B) -> Signal<B> {
        ...
        } 
       fn bind(&self, &s:&Signal<C>) -> Signal<C> {
        ...
        }
       fn first(&self, &s:&Stream<B>) -> Signal<(B,C)>  {

      ...

       }
    }

It's the first function which requires the merging which I am aiming for.

Apologies for numerous syntax errors but I am new to Rust!

Robert

@aepsil0n

This comment has been minimized.

Owner

aepsil0n commented May 15, 2015

Ah, now I get what you were trying to do. That functionality is what the lift! macro is for (it's a macro because it combines up to four signals using an appropriate function). So in your case, just replace sMerge by lift! and that should do the trick.

So you're building an arrow-based API on top of carboxyl? I have to admit, that I'm not familiar with arrow notation for FRP. Therefore I am not quite sure how these correspond to carboxyl's types. But yes, signals are the same as a behaviour in classical FRP or a cell in Sodium. (This went through two renames, but I am fairly happy with its current name, which I borrowed from Elm.)

Internally a Signal<A> is represented as a function of type F: Fn() -> A. So there's no explicit notion of time. The ordering semantics are rather transactional (the details here are a bit of a work-in-progress). But I think it can be roughly viewed as a continuous time-varying value. Streams are the discrete counter part: a series of singular events.

In this context #55 may be of some interest, as I wrote down the formal algebraic laws of the current types. The current implementation probably has some issues with certain edge cases. But I intend to fix those as they are tested more exhaustively.

@aepsil0n

This comment has been minimized.

Owner

aepsil0n commented May 15, 2015

Oh, and also have a look at the updated docs. I realized only now that my automatic docs uploading was broken. So the docs are now more up to date.

@ronslow

This comment has been minimized.

ronslow commented May 15, 2015

Fantastic Eduardo.

I had started to write down Functor, Applicative and Monad for Stream - and you've covered this in #55

It's interesting that Elm was an inspiration since I will be porting Arrow based Elm programs (Arrows in Elm are called Automaton) to Rust.

Ultimately I am aiming for Arrow based programs in bounded memory without a garbage collector, for real time embedded systems. So I'm getting a good feeling from Rust/Carboxyl!

But I guess I will have lots of other questions from time to time!

Robert

@ronslow ronslow closed this May 15, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment