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