Functional Reactive Programming library for Rust

Overview

Build Status

Carboxyl is a library for functional reactive programming in Rust, a functional and composable approach to handle events in interactive applications. Read more in the docs…

Usage example

Here is a simple example of how you can use the primitives provided by Carboxyl. First of all, events can be sent into a sink. From a sink one can create a stream of events. Streams can also be filtered, mapped and merged. A signal is an abstraction of a value that may change over time. One can e.g. hold the last event from a stream in a signal.

extern crate carboxyl;

fn main() {
    let sink = carboxyl::Sink::new();
    let stream = sink.stream();
    let signal = stream.hold(3);

    // The current value of the signal is initially 3
    assert_eq!(signal.sample(), 3);

    // When we fire an event, the signal get updated accordingly
    sink.send(5);
    assert_eq!(signal.sample(), 5);
}

One can also directly iterate over the stream instead of holding it in a signal:

extern crate carboxyl;

fn main() {
    let sink = carboxyl::Sink::new();
    let stream = sink.stream();

    let mut events = stream.events();
    sink.send(4);
    assert_eq!(events.next(), Some(4));
}

Streams and signals can be combined using various primitives. We can map a stream to another stream using a function:

extern crate carboxyl;

fn main() {
    let sink = carboxyl::Sink::new();
    let stream = sink.stream();

    let squares = stream.map(|x| x * x).hold(0);
    sink.send(4);
    assert_eq!(squares.sample(), 16);
}

Or we can filter a stream to create a new one that only contains events that satisfy a certain predicate:

extern crate carboxyl;

fn main() {
    let sink = carboxyl::Sink::new();
    let stream = sink.stream();

    let negatives = stream.filter(|&x| x < 0).hold(0);

    // This won't arrive at the signal.
    sink.send(4);
    assert_eq!(negatives.sample(), 0);

    // But this will!
    sink.send(-3);
    assert_eq!(negatives.sample(), -3);
}

There are a couple of other primitives to compose streams and signals:

  • merge two streams of events of the same type.
  • Make a snapshot of a signal, whenever a stream fires an event.
  • lift! an ordinary function to a function on signals.
  • switch between different signals using a signal containing a signal.

See the documentation for details.

License

Copyright 2014-2020 Carboxyl contributors.

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

Comments
  • Properly implement a transaction system

    Properly implement a transaction system

    This is a plan to implement a transaction system to ensure that effects of events to not overlap inappropriately.

    • [x] Step 1: Implement sequential transactions using a global lock. This ensures the semantics to be correct first. (see PR #13)
    • [ ] Step 2: Use software transactional memory (STM) to achieve concurrent transactions. This is important for performance at some point, but may be quite a large implementation effort.

    Original post

    To provide stronger guarantees on how events are ordered, a transaction system should be implemented. This needs some research though.

    enhancement 
    opened by milibopp 13
  • Relicense to MPL 2.0

    Relicense to MPL 2.0

    Dear contributors (i.e. @Moredread, @killercup, @tilpner and @llogiq),

    Would you agree to re-license your contributions to Carboxyl under the Mozilla Public License Version 2.0?

    To provide some context just in case, anyone of you has not read this: I started a conversation about creating a reactive ecosystem based on Carboxyl. During that discussion it turned out that a lot of people consider LGPL problematic to use for Rust code, as it limits usage in proprietary software too much. I have been convinced that my original intention to make proprietary users contribute bug fixes & improvements back upstream is better implemented using MPL 2.0.

    Of course, in order to change that, we need to have a consensus among contributors to change the license. Please state your opinion on that, and if you are fine with it, explicitly say: I hereby consent to make my contributions to Carboxyl available under the Mozilla Public License Version 2.0

    opened by milibopp 8
  • Relicense to MPL 2.0 as per issue #100

    Relicense to MPL 2.0 as per issue #100

    Hello, I found your talk about this project after searching for rust frp on YouTube. The talk convinced me to look into your library. Upon finding that it was licensed under GPL-3.0+, I was going to ask if you could change the license to something more permissive but then I found that you already had opened an issue (#100) about this yourself and agreed with the other contributors to go with MPL 2.0. Good choice :)

    At the end of the conversation, you asked if anyone could submit a PR for the changes necessary. Here is my go at making such a PR. You will also note that I have bumped the version to v0.2.1. The reason for this bump is that IMO, a change of license warrants at least a minuscule version bump, so that any build systems which rely on version number are triggered to update. Not sure how crates.io works wrt this since I am still very much a newcommer to Rust. Hope this PR is good, let me know if there is anything in it that you would like to have different.

    I have tagged this commit but I don't know if tags are included in pull requests or not.

    opened by yarwelp 5
  • Cyclic SignalMut definition

    Cyclic SignalMut definition

    Is it possible to implement a function analogous to Signal::cyclic for SignalMut? Alternatively, is it possible to define SignalMut recursively using today's API?

    opened by potocpav 5
  • Merge streams with function

    Merge streams with function

    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

    question 
    opened by ronslow 5
  • Fix private_in_public warnings

    Fix private_in_public warnings

    This fixes the "warning: private type in public interface" warnings. Rustc tells us that "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!".

    Please note that this means that the FuncSignal struct (but not its members will be public).

    opened by killercup 4
  • Interop with reactive in other languages?

    Interop with reactive in other languages?

    I was wondering, how feasible it is to use this library in a custom business logic library (written in Rust), which is meant to be used in an interface-specific environment - say an iOS Swift app, which also use reactive (e.g. rxswift)?

    The idea would be that the business logic library exposes observables (and other things) which can be somehow observed / composed in the apps.

    * I'm a Rust newbie - I know this is a very broad (and maybe difficult) answer, just want to get an idea of the possibilities even if it's very vague.

    opened by ivanschuetz 3
  • Marble support

    Marble support

    See RxMarbles. Some API that allows easy creation of scheduled event sequences without explicitly spawning a thread and feeding a Sink. Maybe as a separate crate.

    opened by milibopp 3
  • Can't compile crate with rust 1.2

    Can't compile crate with rust 1.2

    What rustc version (compiler flags?) do you need? I'm trying with 1.2 stable, and have following errors:

    C:\temp\rust-carboxyl-master\carboxyl-master>cargo build
        Updating registry `https://github.com/rust-lang/crates.io-index`
     Downloading rand v0.3.11
     Downloading quickcheck v0.2.23
     Downloading log v0.3.2
     Downloading winapi v0.2.2
     Downloading lazy_static v0.1.14
     Downloading libc v0.1.10
     Downloading winapi-build v0.1.1
     Downloading advapi32-sys v0.1.2
       Compiling lazy_static v0.1.14
       Compiling carboxyl v0.1.1 (file:///C:/temp/rust-carboxyl-master/carboxyl-mast
    er)
    src\lib.rs:136:1: 136:29 error: #[feature] may not be used on the stable release
     channel
    src\lib.rs:136 #![feature(arc_weak, fnbox)]
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    error: aborting due to previous error
    Could not compile `carboxyl`.
    
    To learn more, run the command again with --verbose.
    
    opened by tower120 3
  • No weak pointers?

    No weak pointers?

    According to @acrichton weak pointers won't be stabilized for 1.0. Internally this crate makes heavy use of them, so it is unlikely that it will be ready to use for 1.0.

    Ideas to get around this

    • Shipping own Weak<T>: Would not be exposed, so could be done as a mere implementation detail. Also fairly easy to do (just copy & paste from stdlib).
    • Lifetime bounds instead of ref-counting: Good for performance, but it might make the library slightly harder to use, as this will propagate into the API. This approach is radically different and goes into afaik unexplored terrain for FRP libraries. Requires some experimentation.
    question research 
    opened by milibopp 3
  • Remove derivation of Show trait from test, as this seems not to be stable yet.

    Remove derivation of Show trait from test, as this seems not to be stable yet.

        <anon>:5:21: 5:25 error: `#[derive]` for custom traits is not stable enough for use and is subject to change
    <anon>:5     #[derive(Clone, Show)]
                                 ^~~~
    <anon>:5:21: 5:25 help: add #![feature(custom_derive)] to the crate attributes to enable
    error: aborting due to previous error
    
    
    failures:
        signal::switch_0
    
    opened by Moredread 2
  • Update criterion requirement from 0.3 to 0.4

    Update criterion requirement from 0.3 to 0.4

    Updates the requirements on criterion to permit the latest version.

    Changelog

    Sourced from criterion's changelog.

    [0.4.0] - 2022-09-10

    Removed

    • The Criterion::can_plot function has been removed.
    • The Criterion::bench_function_over_inputs function has been removed.
    • The Criterion::bench_functions function has been removed.
    • The Criterion::bench function has been removed.

    Changed

    • HTML report hidden behind non-default feature flag: 'html_reports'
    • Standalone support (ie without cargo-criterion) feature flag: 'cargo_bench_support'
    • MSRV bumped to 1.57
    • rayon and plotters are optional (and default) dependencies.
    • Status messages ('warming up', 'analyzing', etc) are printed to stderr, benchmark results are printed to stdout.
    • Accept subsecond durations for --warm-up-time, --measurement-time and --profile-time.
    • Replaced serde_cbor with ciborium because the former is no longer maintained.
    • Upgrade clap to v3 and regex to v1.5.

    Added

    • A --discard-baseline flag for discarding rather than saving benchmark results.
    • Formal support for benchmarking code compiled to web-assembly.
    • A --quiet flag for printing just a single line per benchmark.
    • A Throughput::BytesDecimal option for measuring throughput in bytes but printing them using decimal units like kilobytes instead of binary units like kibibytes.

    Fixed

    • When using bench_with_input, the input parameter will now be passed through black_box before passing it to the benchmark.

    [0.3.6] - 2022-07-06

    Changed

    • MSRV bumped to 1.49
    • Symbol for microseconds changed from ASCII 'us' to unicode 'µs'
    • Documentation fixes
    • Clippy fixes

    [0.3.5] - 2021-07-26

    Fixed

    • Corrected Criterion.toml in the book.
    • Corrected configuration typo in the book.

    Changed

    • Bump plotters dependency to always include a bug-fix.
    • MSRV bumped to 1.46.

    ... (truncated)

    Commits
    • 5e27b69 Merge branch 'version-0.4'
    • 4d6d69a Increment version numbers.
    • 935c632 Add Throughput::BytesDecimal. Fixes #581.
    • f82ce59 Remove critcmp code (it belongs in cargo-criterion) (#610)
    • a18d080 Merge branch 'master' into version-0.4
    • f9c6b8d Merge pull request #608 from Cryptex-github/patch-1
    • 8d0224e Fix html report path
    • 2934163 Add missing black_box for bench_with_input parameters. Fixes 566.
    • dfd7b65 Add duplicated benchmark ID to assertion message.
    • ce8259e Bump criterion-plot version number.
    • Additional commits viewable in compare view

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • Excessive cloning in Stream::fold

    Excessive cloning in Stream::fold

    Tried to implement something that accumulates all the stream values into a Vec using Stream::fold, but found that it clones the accumulator values multiple times during it's operation. Example code:

    extern crate carboxyl;
    use carboxyl::Sink;
    use std::fmt::Debug;
    
    #[derive(Debug)]
    struct Storage<T>
    {
        vec: Vec<T>,
    }
    
    impl<T> Storage<T>
    {
        fn new() -> Self
        {
            Storage{ vec: Vec::new() }
        }
    
        fn push(mut self, item: T) -> Self
        {
            self.vec.push(item);
            self
        }
    }
    
    impl<T: Clone + Debug> Clone for Storage<T>
    {
        fn clone(&self) -> Self
        {
            println!("storage cloned! {:?}", self.vec);
            Storage{ vec: self.vec.clone() }
        }
    }
    
    fn main()
    {
        let sink = Sink::new();
        let signal = sink.stream().fold(Storage::new(), Storage::push);
    
        sink.send(11);
        sink.send(22);
        sink.send(33);
    
        println!("result: {:?}", signal.sample());
    }
    

    output:

    storage cloned! []
    storage cloned! []
    storage cloned! [11]
    storage cloned! [11]
    storage cloned! [11]
    storage cloned! [11, 22]
    storage cloned! [11, 22]
    storage cloned! [11, 22]
    storage cloned! [11, 22, 33]
    storage cloned! [11, 22, 33]
    storage cloned! [11, 22, 33]
    result: Storage { vec: [11, 22, 33] }
    

    Don't know if I'm using it wrong, but this seems pretty inefficient. A fold operation shouldn't require cloning the accumulator.

    opened by wolfiestyle 8
  • Deal with dynamic switching more explicitly

    Deal with dynamic switching more explicitly

    The result of creating new events and signals in Carboxyl depends on when it is done. This is necessary to avoid space-leaks, i.e. memorizing the entire history of these objects. By design of the implementation, Carboxyl cannot memorize this, as it does not rely on a garbage collector to clean it up, but rather only has one memory location, where the current value of a signal is stored.

    However, this behaviour could be expressed more explicitly. At the moment the semantics of an expression implicitly depend on when it is executed, which is somewhat undesirable. The API would have to be changed to allow this, so this is something to be considered for version 0.2.

    The main offender is snapshot. In the paper on FRPNow (see below) it is argued that this could be alleviated by not returning the stream/signal directly, but rather a signal containing it to make the dependence on evaluation time explicit.

    Background infos

    research api 
    opened by milibopp 1
  • Create stream from an IntoIterator type

    Create stream from an IntoIterator type

    Essentially, impl<T> FromIterator<T> for Stream<T>. It should be clear that this will not consume the iterator directly but rather spawn a background thread to do so.

    enhancement api ready 
    opened by milibopp 0
  • Return intermediate values from `Signal::cyclic`

    Return intermediate values from `Signal::cyclic`

    Currently it is a bit hacky to return an intermediate value from inside the defining closure passed to Signal::cyclic. There should be a more convenient alternative API method to do this.

    api feature 
    opened by milibopp 0
  • Allow non-'static lifetimes

    Allow non-'static lifetimes

    This is currently a fairly significant limitation of this crate. Only types T: 'static are allowed for streams and signals. It should in principle be possible to lift this restriction but it might require some tweaks to the implementation. Also, not all parts of the API can work that way. For instance, one cannot feed a sink non-statics in a detached background thread. But the borrow checker should take care of such limitations.

    research feature 
    opened by milibopp 2
Owner
Emilia Bopp
Software Developer @ Bürgerwerke eG
Emilia Bopp
A simplistic functional programming language based around Lisp syntax.

Orchid A simplistic functional programming language based around Lisp syntax. Short taste # function to return the larger list (fn larger-list (as bs)

rem 3 May 7, 2022
An advanced, reactive UI library for Rust

An advanced, reactive UI library for Rust Report a Bug · Request a Feature . Ask a Question What is agui? Agui is an advanced reactive GUI project for

Trevin Miller 28 Nov 26, 2022
High-order Virtual Machine (HVM) is a pure functional compile target that is lazy, non-garbage-collected and massively parallel

High-order Virtual Machine (HVM) High-order Virtual Machine (HVM) is a pure functional compile target that is lazy, non-garbage-collected and massivel

null 5.5k Jan 2, 2023
👌 A smol functional language that targets other languages

ditto A small, pure functional language that targets other languages. Syntax highlighting coming soon Elevator pitch ⏱️ Ditto is a mashup of my favour

ditto 45 Dec 17, 2022
ShakeFlow: Functional Hardware Description with Latency-Insensitive Interface Combinators

ShakeFlow: Functional Hardware Description with Latency-Insensitive Interface Combinators This repository contains the artifact for the following pape

KAIST Concurrency & Parallelism Laboratory 36 Feb 16, 2023
The Devils' Programming Language (Quantum Programming Language)

devilslang has roots in Scheme and ML-flavored languages: it's the culmination of everything I expect from a programming language, including the desire to keep everything as minimalistic and concise as possible. At its core, devilslang is lambda-calculus with pattern-matching, structural types, fiber-based concurrency, and syntactic extension.

Devils' Language 2 Aug 26, 2022
Rust library to facilitate event-driven programming.

Squeak Squeak is a zero-dependency Rust library to facilitate event-driven programming. Examples use squeak::{Delegate, Response}; let on_damage_rece

Antoine Gersant 58 Dec 31, 2022
A Rust-powered linear programming library for Python.

Dantzig: A Rust-powered LP library for Python Dantzig is a lightweight and concise linear programming solver suitable for small and large-scale proble

Matteo Santamaria 4 Jan 10, 2023
Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

co-rs 47 Nov 17, 2022
clone of grep cli written in Rust. From Chapter 12 of the Rust Programming Language book

minigrep is a clone of the grep cli in rust Minigrep will find a query string in a file. To test it out, clone the project and run cargo run body poem

Raunak Singh 1 Dec 14, 2021
The Rust Compiler Collection is a collection of compilers for various languages, written with The Rust Programming Language.

rcc The Rust Compiler Collection is a collection of compilers for various languages, written with The Rust Programming Language. Compilers Language Co

null 2 Jan 17, 2022
Game Boy Emulator written in Rust, as a way to fully grasp the Rust programming language

Flan's Game Boy Emulator Game Boy Emulator written in Rust, as a way to get hands-on with the Rust programming language, and creating a proper project

Flan 3 Dec 31, 2022
A minimal version of 'grep' implemented in Rust. Exercise in the "The Rust Programming Language" book.

Minigrep - A simple grep-like tool implemented in Rust This simple CLI tool searches for a given pattern in a specified file and as a result, it print

Filip Szutkowski 3 Mar 15, 2024
Nixt is an interpreted programming language written in Rust

Nixt Nixt is an interpreted lisp inspired programming language written in Rust Index About Examples Installation Build About Nixt goal is to provide a

Wafelack 17 Jul 18, 2022
a function programming language for real world applications made in rust

a function programming language for real world applications made in rust

Tanay Pingalkar 6 Jun 12, 2022
Rust implementation of µKanren, a featherweight relational programming language.

µKanren-rs This is a Rust implementation of µKanren, a featherweight relational programming language. See the original Scheme implementation here for

Eric Zhang 99 Dec 8, 2022
Aspect-oriented programming in Rust

Aspect Oriented Programming (AOP) for Rust The needs of AOP Aspect-oriented programming (AOP) is a programming paradigm that aims to increase modulari

null 8 Jul 4, 2022
This repository contains the source of "The Rust Programming Language" book.

The Rust Programming Language This repository contains the source of "The Rust Programming Language" book. The book is available in dead-tree form fro

The Rust Programming Language 11.2k Jan 8, 2023
A short exercise to introduce people to the Rust programming language

Searching primes by brute force This code is ment to be an exercice to teach rust and give a first impression on how to work with the language during

JoelImgu 3 Dec 14, 2022