Skip to content

danReynolds/computables

Repository files navigation

Computables

Computables are streamable, composable values in Dart.

Readable

The current value of a computable is synchronously readable using get().

final computable = Computable(2);
print(computable.get()) // 2
computable.add(3);
print(computable.get()) // 3

Streamable

The value of the computable can be reactively watched using stream(). The stream starts with the current value of the computable.

import 'package:computables/computables.dart';

final computable = Computable(2);

computable.stream().listen((value) {
  print(value);
  // 2
  // 3
});

computable.add(3);

Composable

A computation is a type of computable that composes multiple input computables into a single output computable. The input to a computation can be any other computable value, including futures, streams and other computations.

final computation = Computation.compute2(
  Computable.fromStream(Stream.value(1), initialValue: 0),
  Computable.fromFuture(Future.value(2), initialValue: 0),
  (input1, input2) => input1 + input2,
);

computable.stream().listen((value) {
  print(value);
  // 0
  // 1
  // 3
});

Since a computation is just another type of computable, it can be immediately reused in other computations:

final computation = Computation.compute2(
  Computable.fromStream(Stream.value(1), initialValue: 0),
  Computable.fromFuture(Future.value(2), initialValue: 0),
  (input1, input2) => input1 + input2,
);

Computation.compute2(
  computation,
  Computable(1),
  (input1, input2) => input1 + input2,
).stream().listen((value) {
  print(value);
  // 1
  // 2
  // 4
});

Transformable

A computation maps multiple input values to a single output value.

A transformation maps multiple input values to a new computable that can emit one or more values.

final computable1 = Computable(1);
final computable2 = Computable(5);

Computation.transform2(
  computable1,
  computable2,
  (input1, input2) {
    return Computable.fromStream(
      Stream.fromIterable(
        List.generate(input2 - input1, (index) => index + 1),
      ),
      initialValue: 0,
    );
  },
).stream.listen((value) {
  print(value);
  // 0
  // 1
  // 2
  // 3
  // 4
})

The above transformation takes two computable values as inputs and returns a computable stream of values that begins with 0 and asynchronously emits the sequence 1, 2, 3, 4.

Subscribers

A ComputableSubscriber allows a computable to subscribe to changes from other sources like futures, streams, and other computables.

final subscriber = Computable.subscriber(0);

subscriber.forward(Computable.fromStream(Stream.fromIterable([1, 2, 3])));
subscriber.forwardFuture(Future.delayed(Duration(seconds: 1), () => 4));

subscriber.stream().listen((value) {
  print(value);
  // 0
  // 1
  // 2
  // 3
  // 4
})

Extensions

There are some helpful utility extensions that make working with computables easier:

Map

Map a source computable to a new computable:

final computable = Computable(2);
computable.map((value) => value + 1).stream().listen((value) {
  print(value);
  // 3
  // 4
});

computable.add(3);

Transform

Transform a source computable to a new computable:

final computable = Computable(2);

computable.transform(
  (value) => Computable.fromStream(
    Stream.iterable([value + 1, value + 2, value + 3]),
    initialValue: 0,
  ),
).stream().listen((value) {
  print(value);
  // 0
  // 3
  // 4
  // 5
});

Contributing

Reach out if there's any functionality you'd like added and happy coding!