Skip to content

Syntax

ribs_core ships a set of Dart extension methods that add idiomatic convenience to both ribs types and standard Dart types. They come by default when you import the ribs_core library.


Option

OptionSyntaxOps adds a .some getter to every type, lifting a value into Some without an explicit constructor call.

dart
void optionSome() {
  final Option<int> x = 42.some; // Some(42)
  final Option<String> y = 'hello'.some; // Some('hello')
}

Either

EitherSyntaxOps adds .asLeft<B>() and .asRight<B>() to every type, so you can create Either values inline without naming the constructor.

dart
void eitherLift() {
  final Either<String, int> err = 'not found'.asLeft<int>();
  final Either<String, int> ok = 42.asRight<String>();
}

Duration

DurationOps adds named-unit getters to int for constructing Duration values. All standard units are supported: microseconds, milliseconds, seconds, minutes, hours, days (each also available in singular form).

dart
void durations() {
  final Duration d1 = 5.seconds;
  final Duration d2 = 100.milliseconds;
  final Duration d3 = 3.minutes;
  final Duration d4 = 2.hours;
}

Iterable

IterableOps adds conversion methods to Dart's Iterable<A>, making it easy to move from mutable collections into ribs immutable types.

dart
void iterableConvert() {
  final List<int> dart = [1, 2, 3];

  final IList<int> list = dart.toIList();
  final IVector<int> vector = dart.toIVector();
}

IList

Several extensions add higher-level operations to IList when its element type is known.

IList<Option<A>>

sequence() turns an IList<Option<A>> into an Option<IList<A>>None if any element is None, otherwise Some of all the unwrapped values.

dart
void ilistOptionSequence() {
  final IList<Option<int>> allSome = ilist([const Some(1), const Some(2), const Some(3)]);
  final Option<IList<int>> result = allSome.sequence(); // Some(IList[1,2,3])

  final IList<Option<int>> hasnone = ilist([const Some(1), none<int>(), const Some(3)]);
  final Option<IList<int>> none_ = hasnone.sequence(); // None
}

unNone() drops all None elements and returns an IList<A> of the unwrapped Some values.

dart
void ilistUnNone() {
  final IList<Option<int>> mixed = ilist([const Some(1), none<int>(), const Some(3)]);
  final IList<int> compact = mixed.unNone(); // IList[1, 3]
}

IList<(A, B)>

unzip() splits a list of pairs into a pair of lists. Variants exist for 3-tuples as well.

dart
void ilistUnzip() {
  final IList<(String, int)> pairs = ilist([('a', 1), ('b', 2)]);
  final (IList<String>, IList<int>) result = pairs.unzip();
}

String

StringOps adds familiar collection-style operations to String, treating it as a sequence of single-character strings.

dart
void stringOps() {
  const String s = 'Hello, World!';

  final String taken = s.take(5); // 'Hello'
  final String dropped = s.drop(7); // 'World!'
  final String filtered = s.filter((c) => c != ','); // 'Hello World!'
  final Option<String> f = s.find((c) => c == 'W'); // Some('W')
  final (String, String) parts = s.splitAt(5); // ('Hello', ', World!')
}

The full set includes take, takeRight, takeWhile, drop, dropRight, dropWhile, filter, filterNot, find, exists, forall, foldLeft, foldRight, splitAt, span, partition, slice, head/headOption, last/lastOption, tail, init, stripPrefix, stripSuffix, sliding, and grouped.


Tuples

TupleNOps extensions (generated for arities 2–22) add collection-like operations to Dart records, making them behave as heterogeneous lists.

Head, last, tail, init

head returns the first element; last returns the final element. tail drops the first element and returns the remaining elements as a smaller tuple; init drops the last element.

dart
void tupleHlist() {
  const t = ('hello', 42, true);

  final String first = t.head; // 'hello'
  final bool last = t.last; // true
  final (int, bool) tl = t.tail; // (42, true)
  final (String, int) it = t.init; // ('hello', 42)
}

Appending and prepending

appended and prepended grow the tuple by one element, returning a new tuple with a statically-correct arity and type.

dart
void tupleAppendPrepend() {
  const pair = ('a', 1);

  final (String, int, bool) appended = pair.appended(true); // ('a', 1, true)
  final (double, String, int) prepended = pair.prepended(3.14); // (3.14, 'a', 1)
}

Calling a function with tuple elements

call spreads the tuple's elements as positional arguments into a matching function, eliminating manual destructuring.

dart
void tupleCall() {
  const t = ('hello', 42);

  // Spread the tuple as positional arguments into a function.
  final int result = t.call((String s, int n) => s.length + n); // 47
}

RIterable numeric & tuple extensions

RIterableIntOps / RIterableDoubleOps add sum() and product() to any RIterableOnce<int> or RIterableOnce<double> (including IList, IVector, etc.).

dart
void riterableNumeric() {
  final IList<int> ints = ilist([1, 2, 3, 4, 5]);
  final int sum = ints.sum(); // 15
  final int prod = ints.product(); // 120

  final IList<double> doubles = ilist([1.0, 2.0, 3.0]);
  final double dsum = doubles.sum(); // 6.0
}

RIterableTuple2Ops adds toIMap() to a collection of pairs, constructing an IMap<A, B> directly.

dart
void riterableToIMap() {
  final IList<(String, int)> pairs = ilist([('a', 1), ('b', 2)]);
  final IMap<String, int> map = pairs.toIMap();
}