Home

Awesome

<h1 align="center">MemoizR</h1> <p align="center"> <img src="docs/MemoizR.png" alt="MemoizR-logo" width="120px" height="120px"/> <br> <em>Streamlined Concurrency</em> <br> </p>

.NET NuGet NuGet

"The world is still short on languages that deal super elegantly and inherently and intuitively with concurrency" Mads Torgersen Lead Designer of C# (https://www.youtube.com/watch?v=Nuw3afaXLUc&t=4402s)

MemoizR is a library for .NET that brings the power of Dynamic Lazy Memoization and Declarative Structured Concurrency to your fingertips. It streamlines complex multi-threaded scenarios, making them easier to write, maintain, and reason about.

Inspired From Stephen Cleary — Asynchronous streams https://www.youtube.com/watch?v=-Tq4wLyen7Q&t=706s

compared towhich isMemoizR/Signals
IEnumerablesynchronousasynchronous
Tasksingle valuemulti value
Observablepush basedpush-pull
IAsyncEnumerablepull basedpush-pull

Key Features

MemoizR draws inspiration from various sources

Advantages over ReactiveX and Dataflow

MemoizR offers several advantages over traditional concurrency libraries:

Implicit Subscription Handling

No need to manage subscriptions manually; MemoizR automatically tracks and synchronizes dependencies. Implicit LinkTo: Dependencies are automatically linked based on your code's structure, simplifying data flow setup. Simplified Error Handling: Structured concurrency makes error handling more robust and easier to reason about.

Usage

Basic Memoization

// Setup
var f = new MemoFactory();
var v1 = f.CreateSignal(1);
var m1 = f.CreateMemoizR(async() => await v1.Get());
var m2 = f.CreateMemoizR(async() => await v1.Get() * 2);
var m3 = f.CreateMemoizR(async() => await m1.Get() + await m2.Get());

// Get Value manually
await m3.Get(); // Calculates m1 + 2 * m1 => (1 + 2 * 1) = 3

// Change
await Task.Run(async () => await v1.Set(2));
// Synchronization is handled by MemoizR
await m3.Get(); // Calculates m1 + 2 * m1 => (1 + 2 * 2) = 6
await m3.Get(); // No operation, result is still 6

await v1.Set(3); // Setting v1 does not trigger evaluation of the graph
await v1.Set(2); // Setting v1 does not trigger evaluation of the graph
await m3.Get(); // No operation, result is still 6 (because the last time the graph was evaluated, v1 was already 2)

Dynamic Graphs

MemoizR can handle dynamic changes in the dependency graph:

var m3 = f.CreateMemoizR(async() => await v1.Get() ? await m1.Get() : await m2.Get());

Declarative Structured Concurrency

var f = new MemoFactory("DSC");

var child1 = f.CreateConcurrentMapReduce(
    async c =>
    {
        await Task.Delay(3000, c.Token);
        return 3;
    });

// all tasks get canceled if one fails
var c1 = f.CreateConcurrentMapReduce(
    async c =>
    {
        await child1.Get();
        return 4;
    });

var x = await c1.Get();

Reactivity

var f = new MemoFactory();
var v1 = f.CreateSignal(1);
var m1 = f.CreateMemoizR(async() => await v1.Get());
var m2 = f.CreateMemoizR(async() => await v1.Get() * 2);
var r1 = f.CreateReaction(m1, m2, (val1, val2) => val1 + val2);

Try it out!https: Experiment with MemoizR online: https://dotnetfiddle.net/Widget/EWtptc

Example From: Khalid Abuhakmeh