{"id":22929044,"url":"https://github.com/teaentitylab/fprust","last_synced_at":"2025-04-07T18:13:27.829Z","repository":{"id":48570340,"uuid":"139460900","full_name":"TeaEntityLab/fpRust","owner":"TeaEntityLab","description":"Monad/MonadIO, Handler, Coroutine/doNotation, Functional Programming features for Rust","archived":false,"fork":false,"pushed_at":"2021-08-21T15:09:46.000Z","size":384,"stargazers_count":115,"open_issues_count":0,"forks_count":7,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-04-30T08:31:35.313Z","etag":null,"topics":["actor-model","async","coroutine","coroutine-library","coroutines","functional-programming","functional-reactive-programming","generator","handler","monad","monads","optional","optional-implementations","publisher-subscriber","publisher-subscriber-pattern","pubsub","reactive","reactive-programming","rust","rust-library"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TeaEntityLab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-02T15:25:22.000Z","updated_at":"2024-04-19T01:50:32.000Z","dependencies_parsed_at":"2022-09-04T19:20:33.828Z","dependency_job_id":null,"html_url":"https://github.com/TeaEntityLab/fpRust","commit_stats":null,"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpRust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpRust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpRust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TeaEntityLab%2FfpRust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TeaEntityLab","download_url":"https://codeload.github.com/TeaEntityLab/fpRust/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247704571,"owners_count":20982298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor-model","async","coroutine","coroutine-library","coroutines","functional-programming","functional-reactive-programming","generator","handler","monad","monads","optional","optional-implementations","publisher-subscriber","publisher-subscriber-pattern","pubsub","reactive","reactive-programming","rust","rust-library"],"created_at":"2024-12-14T09:29:06.677Z","updated_at":"2025-04-07T18:13:27.808Z","avatar_url":"https://github.com/TeaEntityLab.png","language":"Rust","readme":"# fpRust\n\n[![tag](https://img.shields.io/github/tag/TeaEntityLab/fpRust.svg)](https://github.com/TeaEntityLab/fpRust)\n[![Crates.io](https://img.shields.io/crates/d/fp_rust.svg)](https://crates.io/crates/fp_rust)\n[![Travis CI Build Status](https://api.travis-ci.org/TeaEntityLab/fpRust.svg?branch=master)](https://travis-ci.org/TeaEntityLab/fpRust)\n[![docs](https://img.shields.io/badge/docs-online-5023dd.svg)](https://docs.rs/fp_rust/)\n\n[![license](https://img.shields.io/github/license/TeaEntityLab/fpRust.svg?style=social\u0026label=License)](https://github.com/TeaEntityLab/fpRust)\n[![stars](https://img.shields.io/github/stars/TeaEntityLab/fpRust.svg?style=social\u0026label=Stars)](https://github.com/TeaEntityLab/fpRust)\n[![forks](https://img.shields.io/github/forks/TeaEntityLab/fpRust.svg?style=social\u0026label=Fork)](https://github.com/TeaEntityLab/fpRust)\n\nMonad, Functional Programming features for Rust\n\n# Why\n\nI love functional programming, Rx-style coding.\n\nHowever it's hard to implement them in Rust, and there're few libraries to achieve parts of them.\n\nThus I implemented fpRust. I hope you would like it :)\n\n# Features\n\n* MonadIO, Rx-like (*`fp_rust::monadio::MonadIO`*)\n  * map/fmap/subscribe\n  * async/sync\n  * Support *`Future`* (*`to_future()`*) with *`feature: for_futures`\n\n* Publisher (*`fp_rust::publisher::Publisher`*)\n  * Support *`Stream`* implementation(*`subscribe_as_stream()`*) with *`feature: for_futures`\n\n* Fp functions (*`fp_rust::fp`*)\n  * compose!(), pipe!()\n  * map!(), reduce!(), filter!(), foldl!(), foldr!()\n  * contains!(), reverse!()\n\n* Async (*`fp_rust::sync`* \u0026 *`fp_rust::handler::HandlerThread`*)\n  * simple BlockingQueue (inspired by *`Java BlockingQueue`*, implemented by built-in *`std::sync::mpsc::channel`*)\n  * HandlerThread (inspired by *`Android Handler`*, implemented by built-in *`std::thread`*)\n  * WillAsync (inspired by *`Java Future`*)\n    * Support as a *`Future`* with *`feature: for_futures`\n  * CountDownLatch (inspired by *`Java CountDownLatch`*, implemented by built-in *`std::sync::Mutex`*)\n    * Support as a *`Future`* with *`feature: for_futures`\n\n* Cor (*`fp_rust::cor::Cor`*)\n  * PythonicGenerator-like Coroutine\n  * yield/yieldFrom\n  * async/sync\n\n* Actor (*`fp_rust::actor::ActorAsync`*)\n  * Pure simple *`Actor`* model(`receive`/`send`/`spawn`)\n  * `Context` for keeping internal states\n  * Able to communicate with Parent/Children Actors\n\n* DoNotation (*`fp_rust::cor::Cor`*)\n  * Haskell DoNotation-like, *macro*\n\n~~* Pattern matching~~\n\n\n\n# Usage\n\n## MonadIO (RxObserver-like)\n\nExample:\n```rust\n\nextern crate fp_rust;\n\nuse std::{\n    thread,\n    time,\n    sync::{\n        Arc,\n        Mutex,\n        Condvar,\n    }\n};\n\nuse fp_rust::handler::{\n    Handler,\n    HandlerThread,\n};\nuse fp_rust::common::SubscriptionFunc;\nuse fp_rust::monadio::{\n    MonadIO,\n    of,\n};\nuse fp_rust::sync::CountDownLatch;\n\n// fmap \u0026 map (sync)\nlet mut _subscription = Arc::new(SubscriptionFunc::new(move |x: Arc\u003cu16\u003e| {\n    println!(\"monadio_sync {:?}\", x); // monadio_sync 36\n    assert_eq!(36, *Arc::make_mut(\u0026mut x.clone()));\n}));\nlet subscription = _subscription.clone();\nlet monadio_sync = MonadIO::just(1)\n    .fmap(|x| MonadIO::new(move || x * 4))\n    .map(|x| x * 3)\n    .map(|x| x * 3);\nmonadio_sync.subscribe(subscription);\n\n// fmap \u0026 map (async)\nlet mut _handler_observe_on = HandlerThread::new_with_mutex();\nlet mut _handler_subscribe_on = HandlerThread::new_with_mutex();\nlet monadio_async = MonadIO::new_with_handlers(\n    || {\n        println!(\"In string\");\n        String::from(\"ok\")\n    },\n    Some(_handler_observe_on.clone()),\n    Some(_handler_subscribe_on.clone()),\n);\n\nlet latch = CountDownLatch::new(1);\nlet latch2 = latch.clone();\n\nthread::sleep(time::Duration::from_millis(1));\n\nlet subscription = Arc::new(SubscriptionFunc::new(move |x: Arc\u003cString\u003e| {\n    println!(\"monadio_async {:?}\", x); // monadio_async ok\n\n    latch2.countdown(); // Unlock here\n}));\nmonadio_async.subscribe(subscription);\nmonadio_async.subscribe(Arc::new(SubscriptionFunc::new(move |x: Arc\u003cString\u003e| {\n    println!(\"monadio_async sub2 {:?}\", x); // monadio_async sub2 ok\n})));\n{\n    let mut handler_observe_on = _handler_observe_on.lock().unwrap();\n    let mut handler_subscribe_on = _handler_subscribe_on.lock().unwrap();\n\n    println!(\"hh2\");\n    handler_observe_on.start();\n    handler_subscribe_on.start();\n    println!(\"hh2 running\");\n\n    handler_observe_on.post(RawFunc::new(move || {}));\n    handler_observe_on.post(RawFunc::new(move || {}));\n    handler_observe_on.post(RawFunc::new(move || {}));\n    handler_observe_on.post(RawFunc::new(move || {}));\n    handler_observe_on.post(RawFunc::new(move || {}));\n}\nthread::sleep(time::Duration::from_millis(1));\n\n// Waiting for being unlcoked\nlatch.clone().wait();\n```\n\n## Publisher (PubSub-like)\n\nExample:\n```rust\n\nextern crate fp_rust;\n\nuse fp_rust::common::{SubscriptionFunc, RawFunc};\nuse fp_rust::handler::{Handler, HandlerThread};\nuse fp_rust::publisher::Publisher;\nuse std::sync::Arc;\n\nuse fp_rust::sync::CountDownLatch;\n\nlet mut pub1 = Publisher::new();\npub1.subscribe_fn(|x: Arc\u003cu16\u003e| {\n    println!(\"pub1 {:?}\", x);\n    assert_eq!(9, *Arc::make_mut(\u0026mut x.clone()));\n});\npub1.publish(9);\n\nlet mut _h = HandlerThread::new_with_mutex();\n\nlet mut pub2 = Publisher::new_with_handlers(Some(_h.clone()));\n\nlet latch = CountDownLatch::new(1);\nlet latch2 = latch.clone();\n\nlet s = Arc::new(SubscriptionFunc::new(move |x: Arc\u003cString\u003e| {\n    println!(\"pub2-s1 I got {:?}\", x);\n\n    latch2.countdown();\n}));\npub2.subscribe(s.clone());\npub2.map(move |x: Arc\u003cString\u003e| {\n    println!(\"pub2-s2 I got {:?}\", x);\n});\n\n{\n    let h = \u0026mut _h.lock().unwrap();\n\n    println!(\"hh2\");\n    h.start();\n    println!(\"hh2 running\");\n\n    h.post(RawFunc::new(move || {}));\n    h.post(RawFunc::new(move || {}));\n    h.post(RawFunc::new(move || {}));\n    h.post(RawFunc::new(move || {}));\n    h.post(RawFunc::new(move || {}));\n}\n\npub2.publish(String::from(\"OKOK\"));\npub2.publish(String::from(\"OKOK2\"));\n\npub2.unsubscribe(s.clone());\n\npub2.publish(String::from(\"OKOK3\"));\n\nlatch.clone().wait();\n```\n\n## Cor (PythonicGenerator-like)\n\nExample:\n```rust\n\n#[macro_use]\nextern crate fp_rust;\n\nuse std::time;\nuse std::thread;\n\nuse fp_rust::cor::Cor;\n\nprintln!(\"test_cor_new\");\n\nlet _cor1 = cor_newmutex!(\n    |this| {\n        println!(\"cor1 started\");\n\n        let s = cor_yield!(this, Some(String::from(\"given_to_outside\")));\n        println!(\"cor1 {:?}\", s);\n    },\n    String,\n    i16\n);\nlet cor1 = _cor1.clone();\n\nlet _cor2 = cor_newmutex!(\n    move |this| {\n        println!(\"cor2 started\");\n\n        println!(\"cor2 yield_from before\");\n\n        let s = cor_yield_from!(this, cor1, Some(3));\n        println!(\"cor2 {:?}\", s);\n    },\n    i16,\n    i16\n);\n\n{\n    let cor1 = _cor1.clone();\n    cor1.lock().unwrap().set_async(true); // NOTE Cor default async\n                                          // NOTE cor1 should keep async to avoid deadlock waiting.(waiting for each other)\n}\n{\n    let cor2 = _cor2.clone();\n    cor2.lock().unwrap().set_async(false);\n    // NOTE cor2 is the entry point, so it could be sync without any deadlock.\n}\ncor_start!(_cor1);\ncor_start!(_cor2);\n\nthread::sleep(time::Duration::from_millis(1));\n```\n\n## Do Notation (Haskell DoNotation-like)\n\nExample:\n```rust\n\n#[macro_use]\nextern crate fp_rust;\n\nuse std::time;\nuse std::thread;\n\nuse fp_rust::cor::Cor;\n\n\nlet v = Arc::new(Mutex::new(String::from(\"\")));\n\nlet _v = v.clone();\ndo_m!(move |this| {\n    println!(\"test_cor_do_m started\");\n\n    let cor_inner1 = cor_newmutex_and_start!(\n        |this| {\n            let s = cor_yield!(this, Some(String::from(\"1\")));\n            println!(\"cor_inner1 {:?}\", s);\n        },\n        String,\n        i16\n    );\n    let cor_inner2 = cor_newmutex_and_start!(\n        |this| {\n            let s = cor_yield!(this, Some(String::from(\"2\")));\n            println!(\"cor_inner2 {:?}\", s);\n        },\n        String,\n        i16\n    );\n    let cor_inner3 = cor_newmutex_and_start!(\n        |this| {\n            let s = cor_yield!(this, Some(String::from(\"3\")));\n            println!(\"cor_inner3 {:?}\", s);\n        },\n        String,\n        i16\n    );\n\n    {\n        (*_v.lock().unwrap()) = [\n            cor_yield_from!(this, cor_inner1, Some(1)).unwrap(),\n            cor_yield_from!(this, cor_inner2, Some(2)).unwrap(),\n            cor_yield_from!(this, cor_inner3, Some(3)).unwrap(),\n        ].join(\"\");\n    }\n});\n\nlet _v = v.clone();\n\n{\n    assert_eq!(\"123\", *_v.lock().unwrap());\n}\n```\n\n## Fp Functions (Compose, Pipe, Map, Reduce, Filter)\n\nExample:\n\n```rust\n#[macro_use]\nextern crate fp_rust\n\nuse fp_rust::fp::{\n  compose_two,\n  map, reduce, filter,\n};\n\nlet add = |x| x + 2;\nlet multiply = |x| x * 3;\nlet divide = |x| x / 2;\n\nlet result = (compose!(add, multiply, divide))(10);\nassert_eq!(17, result);\nprintln!(\"Composed FnOnce Result is {}\", result);\n\nlet result = (pipe!(add, multiply, divide))(10);\nassert_eq!(18, result);\nprintln!(\"Piped FnOnce Result is {}\", result);\n\nlet result = (compose!(reduce!(|a, b| a * b), filter!(|x| *x \u003c 6), map!(|x| x * 2)))(vec![1, 2, 3, 4]);\nassert_eq!(Some(8), result);\nprintln!(\"test_map_reduce_filter Result is {:?}\", result);\n```\n\n## Actor\n\n### Actor common(send/receive/spawn/states)\n\nExample:\n\n```rust\nuse std::time::Duration;\n\nuse fp_rust::common::LinkedListAsync;\n\n#[derive(Clone, Debug)]\nenum Value {\n    // Str(String),\n    Int(i32),\n    VecStr(Vec\u003cString\u003e),\n    Spawn,\n    Shutdown,\n}\n\nlet result_i32 = LinkedListAsync::\u003ci32\u003e::new();\nlet result_i32_thread = result_i32.clone();\nlet result_string = LinkedListAsync::\u003cVec\u003cString\u003e\u003e::new();\nlet result_string_thread = result_string.clone();\nlet mut root = ActorAsync::new(\n    move |this: \u0026mut ActorAsync\u003c_, _\u003e, msg: Value, context: \u0026mut HashMap\u003cString, Value\u003e| {\n        match msg {\n            Value::Spawn =\u003e {\n                println!(\"Actor Spawn\");\n                let result_i32_thread = result_i32_thread.clone();\n                let spawned = this.spawn_with_handle(Box::new(\n                    move |this: \u0026mut ActorAsync\u003c_, _\u003e, msg: Value, _| {\n                        match msg {\n                            Value::Int(v) =\u003e {\n                                println!(\"Actor Child Int\");\n                                result_i32_thread.push_back(v * 10);\n                            }\n                            Value::Shutdown =\u003e {\n                                println!(\"Actor Child Shutdown\");\n                                this.stop();\n                            }\n                            _ =\u003e {}\n                        };\n                    },\n                ));\n                let list = context.get(\"children_ids\").cloned();\n                let mut list = match list {\n                    Some(Value::VecStr(list)) =\u003e list,\n                    _ =\u003e Vec::new(),\n                };\n                list.push(spawned.get_id());\n                context.insert(\"children_ids\".into(), Value::VecStr(list));\n            }\n            Value::Shutdown =\u003e {\n                println!(\"Actor Shutdown\");\n                if let Some(Value::VecStr(ids)) = context.get(\"children_ids\") {\n                    result_string_thread.push_back(ids.clone());\n                }\n\n                this.for_each_child(move |id, handle| {\n                    println!(\"Actor Shutdown id {:?}\", id);\n                    handle.send(Value::Shutdown);\n                });\n                this.stop();\n            }\n            Value::Int(v) =\u003e {\n                println!(\"Actor Int\");\n                if let Some(Value::VecStr(ids)) = context.get(\"children_ids\") {\n                    for id in ids {\n                        println!(\"Actor Int id {:?}\", id);\n                        if let Some(mut handle) = this.get_handle_child(id) {\n                            handle.send(Value::Int(v));\n                        }\n                    }\n                }\n            }\n            _ =\u003e {}\n        }\n    },\n);\n\nlet mut root_handle = root.get_handle();\nroot.start();\n\n// One child\nroot_handle.send(Value::Spawn);\nroot_handle.send(Value::Int(10));\n// Two children\nroot_handle.send(Value::Spawn);\nroot_handle.send(Value::Int(20));\n// Three children\nroot_handle.send(Value::Spawn);\nroot_handle.send(Value::Int(30));\n\n// Send Shutdown\nroot_handle.send(Value::Shutdown);\n\nthread::sleep(Duration::from_millis(1));\n// 3 children Actors\nassert_eq!(3, result_string.pop_front().unwrap().len());\n\nlet mut v = Vec::\u003cOption\u003ci32\u003e\u003e::new();\nfor _ in 1..7 {\n    let i = result_i32.pop_front();\n    println!(\"Actor {:?}\", i);\n    v.push(i);\n}\nv.sort();\nassert_eq!(\n    [\n        Some(100),\n        Some(200),\n        Some(200),\n        Some(300),\n        Some(300),\n        Some(300)\n    ],\n    v.as_slice()\n)\n```\n\n### Actor Ask (inspired by Akka/Erlang)\n\nExample:\n\n```rust\nuse std::time::Duration;\n\nuse fp_rust::common::LinkedListAsync;\n\n#[derive(Clone, Debug)]\nenum Value {\n    AskIntByLinkedListAsync((i32, LinkedListAsync\u003ci32\u003e)),\n    AskIntByBlockingQueue((i32, BlockingQueue\u003ci32\u003e)),\n}\n\nlet mut root = ActorAsync::new(\n    move |_: \u0026mut ActorAsync\u003c_, _\u003e, msg: Value, _: \u0026mut HashMap\u003cString, Value\u003e| match msg {\n        Value::AskIntByLinkedListAsync(v) =\u003e {\n            println!(\"Actor AskIntByLinkedListAsync\");\n            v.1.push_back(v.0 * 10);\n        }\n        Value::AskIntByBlockingQueue(mut v) =\u003e {\n            println!(\"Actor AskIntByBlockingQueue\");\n\n            // NOTE If negative, hanging for testing timeout\n            if v.0 \u003c 0 {\n                return;\n            }\n\n            // NOTE General Cases\n            v.1.offer(v.0 * 10);\n        } // _ =\u003e {}\n    },\n);\n\nlet mut root_handle = root.get_handle();\nroot.start();\n\n// LinkedListAsync\u003ci32\u003e\nlet result_i32 = LinkedListAsync::\u003ci32\u003e::new();\nroot_handle.send(Value::AskIntByLinkedListAsync((1, result_i32.clone())));\nroot_handle.send(Value::AskIntByLinkedListAsync((2, result_i32.clone())));\nroot_handle.send(Value::AskIntByLinkedListAsync((3, result_i32.clone())));\nthread::sleep(Duration::from_millis(1));\nlet i = result_i32.pop_front();\nassert_eq!(Some(10), i);\nlet i = result_i32.pop_front();\nassert_eq!(Some(20), i);\nlet i = result_i32.pop_front();\nassert_eq!(Some(30), i);\n\n// BlockingQueue\u003ci32\u003e\nlet mut result_i32 = BlockingQueue::\u003ci32\u003e::new();\nresult_i32.timeout = Some(Duration::from_millis(1));\nroot_handle.send(Value::AskIntByBlockingQueue((4, result_i32.clone())));\nroot_handle.send(Value::AskIntByBlockingQueue((5, result_i32.clone())));\nroot_handle.send(Value::AskIntByBlockingQueue((6, result_i32.clone())));\nthread::sleep(Duration::from_millis(1));\nlet i = result_i32.take();\nassert_eq!(Some(40), i);\nlet i = result_i32.take();\nassert_eq!(Some(50), i);\nlet i = result_i32.take();\nassert_eq!(Some(60), i);\n\n// Timeout case:\nroot_handle.send(Value::AskIntByBlockingQueue((-1, result_i32.clone())));\nlet i = result_i32.take();\nassert_eq!(None, i);\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteaentitylab%2Ffprust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteaentitylab%2Ffprust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteaentitylab%2Ffprust/lists"}