Skip to content

Instantly share code, notes, and snippets.

@eernstg
Last active March 31, 2022 13:43
Show Gist options
  • Save eernstg/e7ff35dfb7435bac268383c94757baaa to your computer and use it in GitHub Desktop.
Save eernstg/e7ff35dfb7435bac268383c94757baaa to your computer and use it in GitHub Desktop.
// ----------------------------------------------------------------------
// Basic example, hypothetical code only.
// typeclass Index<inout X, in Y> {
// X X.operator [](Y y);
// }
// instance Index<int, int> {
// int int.operator [](int y) => this + y;
// }
// instance Index<double, double> {
// double double.operator [](double y) => this + y;
// }
// instance Index<int, double> {
// int int.operator [](double y) => (toDouble() + y).round();
// }
// instance Index<double, int> {
// double double.operator [](int y) => this + y.toDouble();
// }
// instance<inout X, in Y> Index<Iterable<X>, Iterable<Y>> where Index<X, Y> {
// Iterable<X> Iterable<X>.operator [](Iterable<Y> ys) {
// Never fail() => throw StateError("Index: Lists of different length");
// Iterator<Y> it = ys.iterator;
// List<X> result = [];
// for (var x in this) {
// if (!it.moveNext()) fail();
// result.add(x[it.current]);
// }
// if (it.moveNext()) fail();
// return result;
// }
// }
// void callIndex<X, Y>(X x, Y y) where Index<X, Y> {
// print(x[y]);
// }
// void callIndexList<X, Y>(X x, Y y) where Index<X, Y> {
// print([x][[y]]);
// }
// void main1() {
// print(1[1])); // '2'.
// print(1.0[1.0])); // '2.0'.
// print(1[1.0])); // '2'.
// print(1.0[1])); // '2.0'.
// callIndex(1, 1); // '2'.
// callIndex(1.0, 1.0); // '2.0'.
// callIndex(1, 1.0); // '2'.
// callIndex(1.0, 1); // '2.0'.
// print([1][[1.0]]); // '[2]'.
// callIndex([1.0, 2.0], [2, 1]); // '[3.0, 3.0]'.
// callIndexList([1.0, 2.0], [2, 1]); // '[[3.0, 3.0]]'.
// }
// ----------------------------------------------------------------------
// Basic example, with desugared code.
// typeclass Index<inout X, in Y> {
// X X.operator [](Y y);
// }
abstract class Index$Dispatcher<inout X, in Y> {
const Index$Dispatcher();
X operator$index(X self, Y y);
}
// instance Index<int, int> {
// int int.operator [](int y) => this + y;
// }
class IndexIntInt$Dispatcher implements Index$Dispatcher<int, int> {
const IndexIntInt$Dispatcher();
int operator$index(int self, int y) => self + y;
}
// instance Index<double, double> {
// double double.operator [](double y) => this + y;
// }
class IndexDoubleDouble$Dispatcher implements Index$Dispatcher<double, double> {
const IndexDoubleDouble$Dispatcher();
double operator$index(double self, double y) => self + y;
}
// instance Index<int, double> {
// int int.operator [](double y) => (toDouble() + y).round();
// }
class IndexIntDouble$Dispatcher implements Index$Dispatcher<int, double> {
const IndexIntDouble$Dispatcher();
int operator$index(int self, double y) => (self.toDouble() + y).round();
}
// instance Index<double, int> {
// double double.operator [](int y) => this + y.toDouble();
// }
class IndexDoubleInt$Dispatcher implements Index$Dispatcher<double, int> {
const IndexDoubleInt$Dispatcher();
double operator$index(double self, int y) => self + y.toDouble();
}
// instance<inout X, in Y> Index<Iterable<X>, Iterable<Y>> where Index<X, Y> {
// Iterable<X> Iterable<X>.operator [](Iterable<Y> ys) {
// Never fail() => throw StateError("Index: Lists of different length");
// Iterator<Y> it = ys.iterator;
// List<X> result = [];
// for (var x in this) {
// if (!it.moveNext()) fail();
// result.add(x[it.current]);
// }
// if (it.moveNext()) fail();
// return result;
// }
// }
class IndexIterableIterable$Dispatcher<inout X, in Y>
implements Index$Dispatcher<Iterable<X>, Iterable<Y>> {
final Index$Dispatcher<X, Y> dispatcher;
const IndexIterableIterable$Dispatcher(this.dispatcher);
Iterable<X> operator$index(Iterable<X> self, Iterable<Y> ys) {
Never fail() => throw StateError("Index: Lists of different length");
Iterator<Y> it = ys.iterator;
List<X> result = [];
for (var x in self) {
if (!it.moveNext()) fail();
result.add(dispatcher.operator$index(x, it.current));
}
if (it.moveNext()) fail();
return result;
}
}
// void callIndex<X, Y>(X x, Y y) where Index<X, Y> {
// print(x[y]);
// }
void callIndex<X, Y>(
Index$Dispatcher<X, Y> dispatcher, X x, Y y) {
print(dispatcher.operator$index(x, y));
}
// void callIndexList<X, Y>(X x, Y y) where Index<X, Y> {
// print([x][[y]]);
// }
void callIndexList<X, Y>(
Index$Dispatcher<X, Y> dispatcher, X x, Y y) {
print(IndexIterableIterable$Dispatcher(dispatcher).operator$index([x], [y]));
}
void main1() {
// print(1[1]);
print(const IndexIntInt$Dispatcher().operator$index(1, 1));
// print(1.0[1.0]);
print(const IndexDoubleDouble$Dispatcher().operator$index(1.0, 1.0));
// print(1[1.0]);
print(const IndexIntDouble$Dispatcher().operator$index(1, 1.0));
// print(1.0[1]);
print(const IndexDoubleInt$Dispatcher().operator$index(1.0, 1));
// callIndex(1, 1);
callIndex(const IndexIntInt$Dispatcher(), 1, 1);
// callIndex(1.0, 1.0);
callIndex(const IndexDoubleDouble$Dispatcher(), 1.0, 1.0);
// callIndex(1, 1.0);
callIndex(const IndexIntDouble$Dispatcher(), 1, 1.0);
// callIndex(1.0, 1);
callIndex(const IndexDoubleInt$Dispatcher(), 1.0, 1);
// print([1][[1.0]]);
print(const IndexIterableIterable$Dispatcher(const IndexIntDouble$Dispatcher())
.operator$index([1], [1.0]));
// callIndex([1.0, 2.0], [2, 1]);
callIndex(
const IndexIterableIterable$Dispatcher(const IndexDoubleInt$Dispatcher()),
[1.0, 2.0],
[2, 1],
);
// callIndexList([1.0, 2.0], [2, 1]);
callIndexList(
const IndexIterableIterable$Dispatcher(const IndexDoubleInt$Dispatcher()),
[1.0, 2.0],
[2, 1],
);
}
// ----------------------------------------------------------------------
// Same type variable, multiple type classes.
// typeclass A<in X, out Y> {
// Y get X.g;
// }
abstract class A$Dispatcher<in X, out Y> {
const A$Dispatcher();
Y getter$g(X x);
}
// typeclass B<out X> {
// X get gNoReceiver;
// }
abstract class B$Dispatcher<out X> {
const B$Dispatcher();
X get gNoReceiver;
}
// instance<inout X> A<X, X> {
// X get X.g => this;
// }
class AXX$Dispatcher<inout X> implements A$Dispatcher<X, X> {
const AXX$Dispatcher();
X getter$g(X self) => self;
}
// instance B<int> {
// int get gNoReceiver => 1;
// }
class BInt$Dispatcher implements B$Dispatcher<int> {
const BInt$Dispatcher();
int get gNoReceiver => 1;
}
// Y foo<X extends num, Y>() where A<X, Y>, B<X> {
// return gNoReceiver.g;
// }
Y foo<X extends num, Y>(
A$Dispatcher<X, Y> aDispatcher, B$Dispatcher<X> bDispatcher) {
return aDispatcher.getter$g(bDispatcher.gNoReceiver);
}
void main2() {
// num n = foo();
num n = foo(const AXX$Dispatcher(), const BInt$Dispatcher());
// print(n);
print(n);
}
// ----------------------------------------------------------------------
// Dispatchers in multiple scopes.
// typeclass A2<in X, out Y> {
// Y X.m(X x);
// }
abstract class A2$Dispatcher<in X, out Y> {
const A2$Dispatcher();
Y m(X self, X x);
}
// typeclass B2<out X> {
// X get gNoReceiver;
// }
abstract class B2$Dispatcher<out X> {
const B2$Dispatcher();
X get gNoReceiver;
}
// instance<inout X> A2<X, X> {
// X X.m(X x) => x;
// }
class A2XX$Dispatcher<inout X> implements A2$Dispatcher<X, X> {
const A2XX$Dispatcher();
X m(X self, X x) => x;
}
// instance B2<int> {
// int get gNoReceiver => 1;
// }
class B2Int$Dispatcher implements B2$Dispatcher<int> {
const B2Int$Dispatcher();
int get gNoReceiver => 1;
}
// class C<inout X, out Y> where A2<X, Y> {
// X x;
// C(this.x);
// Y foo<Z extends X>() where B<Z> => x.m(gNoReceiver);
// }
class C<inout X, out Y> {
final A2$Dispatcher<X, Y> a2Dispatcher;
X x;
C(this.a2Dispatcher, this.x);
Y foo<Z extends X>(B2$Dispatcher<Z> bDispatcher) {
return a2Dispatcher.m(x, bDispatcher.gNoReceiver);
}
}
void main3() {
// var c = C(1.5);
var c = C(const A2XX$Dispatcher(), 1.5); // NB: <dynamic, dynamic>.
// print(c.foo());
print(c.foo(const B2Int$Dispatcher()));
}
// ----------------------------------------------------------------------
void main() {
main1();
main2();
main3();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment