Last active
March 31, 2022 13:43
-
-
Save eernstg/e7ff35dfb7435bac268383c94757baaa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------------------------------------------------------------------- | |
// 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