Much sketching of new hkts, ints, classes, and so on.
This commit is contained in:
parent
084ed4a00b
commit
153dd993f8
530
sketching/april_2025/closure.dm
Normal file
530
sketching/april_2025/closure.dm
Normal file
@ -0,0 +1,530 @@
|
||||
pub enum Closure(P, D = Any, S = Any) -> T {
|
||||
Read(ReadClosure), // reads self (where Self is Closure) and/or environment
|
||||
ReadRef(ReadRefClosure), // reads environment/self, return value tied to closure
|
||||
Mut(MutClosure), // mutates self/environment
|
||||
MutRef(MutRefClosure), // mutates self/environment, return value tied to closure
|
||||
Cons(ConsClosure), // consumes self/environment
|
||||
}
|
||||
|
||||
class Foo(bar: Bar) {}
|
||||
|
||||
class Bar(mut i: Int) {}
|
||||
|
||||
fn readClosure(cl: Closure(Int) -> String) -> String {
|
||||
cl(42)
|
||||
}
|
||||
|
||||
fn useReadClosure() {
|
||||
let s = readClosure { i ->
|
||||
'i: ' + i.toString()
|
||||
}
|
||||
assertEq('i: 42', s)
|
||||
}
|
||||
|
||||
fn mutClosure(cl: mut Closure(Int) -> String) -> String {
|
||||
cl(42)
|
||||
}
|
||||
|
||||
fn useMutClosure() {
|
||||
let mut x = 0
|
||||
let cl: mut Closure(Int) -> String = mut { i ->
|
||||
let result = 'Val: ' + (i + x)
|
||||
x++
|
||||
result
|
||||
}
|
||||
let s0 = mutClosure cl
|
||||
assertEq('Val: 42', s0)
|
||||
let s1 = mutClosure cl
|
||||
assertEq('Val: 43', s1)
|
||||
}
|
||||
|
||||
fn consClosure(cl: cons Closure(Int) -> String) -> String {
|
||||
cl(42)
|
||||
// cl(43) // Error! cl was already consumed
|
||||
}
|
||||
|
||||
fn useConsClosure() {
|
||||
let foo = Foo(Bar(10))
|
||||
let cl: cons Closure(Int) -> String = cons { i ->
|
||||
let fooBarI = foo.bar.i
|
||||
drop(foo)
|
||||
'Val: ' + (i + fooBarI)
|
||||
}
|
||||
let s = consClosure(cl)
|
||||
// let err0 = consClosure(cl) // Error! cl may have been consumed
|
||||
// let err1 = foo // Error! foo may have been consumed
|
||||
assertEq('Val: 52', s)
|
||||
}
|
||||
|
||||
fn readRefClosure(cl: Closure(foo: Foo) -> Bar ref foo) -> Int {
|
||||
let foo = Foo(Bar(42))
|
||||
let bar = cl(foo)
|
||||
bar.i
|
||||
}
|
||||
|
||||
fn useReadRefClosure() {
|
||||
let i = readRefClosure { foo -> foo.bar }
|
||||
assertEq(42, i)
|
||||
}
|
||||
|
||||
fn mutRefClosure(cl: mut Closure(foo: Foo) -> Bar ref foo) -> Int {
|
||||
let foo = Foo(Bar(42))
|
||||
let bar = cl(foo)
|
||||
bar.i
|
||||
}
|
||||
|
||||
fn useMutRefClosure() {
|
||||
let i = mutRefClosure mut { foo ->
|
||||
foo.bar.i = 84
|
||||
foo.bar
|
||||
}
|
||||
assertEq(84, i)
|
||||
}
|
||||
|
||||
fn readClosureWithDelegate(cl: Closure(P = Int, D = Foo) -> String) -> String {
|
||||
cl(42)
|
||||
}
|
||||
|
||||
fn useReadClosureWithDelegate() {
|
||||
let foo = Foo(Bar(42))
|
||||
let cl = { i: Int ->
|
||||
'Val: ' + (i + bar.i) // bar found in delegate
|
||||
}
|
||||
cl.delegate = &foo
|
||||
let s = readClosureWithDelegate(cl)
|
||||
assertEq('Val: 84', s)
|
||||
}
|
||||
|
||||
class Baz(foo: Foo) {}
|
||||
|
||||
fn mutRefClosureWithDelegate(baz: &mut Baz, cl: Closure(Int, baz: &mut Baz)(D = Foo) -> &Bar ref baz) -> String {
|
||||
let foo = Foo(Bar(1))
|
||||
cl.delegate = &foo
|
||||
let bar = cl(2, baz)
|
||||
'Val: ' + bar.i
|
||||
}
|
||||
|
||||
fn useMutRefClosureWithDelegate() {
|
||||
let cl: Closure(Int, baz: &mut Baz)(D = Foo) -> &Bar ref baz = mut { i, baz ->
|
||||
let iFromDelegate = bar.i
|
||||
baz.foo.bar.i = iFromDelegate + i
|
||||
&baz.foo.bar
|
||||
}
|
||||
let baz = Baz(Foo(Bar(20)))
|
||||
let bar = mutRefClosureWithDelegate(cl)
|
||||
assertEq(3, bar.i)
|
||||
}
|
||||
|
||||
fn mutClosureWithDelegate(cl: Closure(P = (Int, Int), D = mut Foo) -> String {
|
||||
let foo = Foo(Bar(42))
|
||||
cl.delegate = &mut foo
|
||||
cl(1, 2)
|
||||
}
|
||||
|
||||
fn useMutClosureWithDelegate() {
|
||||
let cl: Closure(P = (Int, Int), D = mut Foo) -> String = mut { x, y ->
|
||||
bar.i = 3
|
||||
'Val: ' + (x + y + bar.i)
|
||||
}
|
||||
let s = mutClosureWithDelegate(cl)
|
||||
assertEq('Val: 6', s)
|
||||
}
|
||||
|
||||
pub int ClosureBase {
|
||||
params: Array<ClosureParam>
|
||||
mut resolutionStrategy: ResolutionStrategy
|
||||
ref fn getDelegate() -> Option<&Any>
|
||||
ref fn getSelfObject() -> Option<&Any>
|
||||
}
|
||||
|
||||
pub enum ResolutionStrategy {
|
||||
DelegateFirst,
|
||||
DelegateOnly,
|
||||
SelfFirst,
|
||||
SelfOnly,
|
||||
None
|
||||
}
|
||||
|
||||
pub int ConsClosure<T> : ClosureBase {
|
||||
mut fn setDelegate(d: Any) -> Void
|
||||
mut fn setSelf(s: Any) -> Void
|
||||
mut fn call(...args: Array<Any>) -> T
|
||||
def mut op () (...args: Array<Any) -> T = call(...args)
|
||||
}
|
||||
|
||||
pub int MutClosure<T> : ClosureBase {
|
||||
mut fn setDelegate(d: &mut Any) -> Void
|
||||
mut fn setSelf(s: &mut Any) -> Void
|
||||
mut fn call(...args: Array<Any>) -> T
|
||||
def mut op () (...args: Array<Any>) -> T = call(...args)
|
||||
}
|
||||
|
||||
pub int MutRefClosure<T> : ClosureBase {
|
||||
mut fn setDelegate(d: &mut Any) -> Void
|
||||
mut fn setSelf(s: &mut Any) -> Void
|
||||
mut ref fn call(...args: Array<Any>) -> T // T ref self
|
||||
def mut ref op () (...args: Array<Any>) -> T = call(...args)
|
||||
}
|
||||
|
||||
pub int ReadClosure<T> : ClosureBase {
|
||||
mut fn setDelegate(d: &Any) -> Void
|
||||
mut fn setSelf(s: &Any) -> Void
|
||||
fn call(...args: Array<Any>) -> T
|
||||
def op () (...args: Array<Any>) -> T = call(...args)
|
||||
}
|
||||
|
||||
pub int ReadRefClosure<T> : ClosureBase {
|
||||
mut fn setDelegate(d: &Any) -> Void
|
||||
mut fn setSelf(s: &Any) -> Void
|
||||
ref fn call(...args: Array<Any>) -> T
|
||||
def op () (...args) -> T = call(..args)
|
||||
}
|
||||
|
||||
pub int ClosureParam {
|
||||
clazz: Class<Any>
|
||||
def op typeof () -> Class<Any> = clazz
|
||||
}
|
||||
|
||||
// Example of how closures are compiled
|
||||
|
||||
fn main() {
|
||||
let is = [1, 2, 3]
|
||||
let cl0 = { acc: String, i: Int ->
|
||||
acc + i
|
||||
}
|
||||
println is.reduce(cl0)
|
||||
|
||||
let x = 10
|
||||
let cl1 = /* read */ { acc: String, i: Int ->
|
||||
acc + (i + x)
|
||||
}
|
||||
println is.reduce(cl1)
|
||||
|
||||
let mut counter = 0
|
||||
let cl2 = mut { acc: String, i: Int ->
|
||||
let result = acc + (i + counter)
|
||||
counter++
|
||||
result
|
||||
}
|
||||
println is.reduce(cl2)
|
||||
|
||||
let mut foo = Foo(Bar(1))
|
||||
let cl3 = cons { acc: String, i: Int ->
|
||||
// do something pointless just for illustration
|
||||
if foo.bar.y % 2 == 0 {
|
||||
foo.bar = Bar(1)
|
||||
}
|
||||
let result = acc + (i + foo.bar.y)
|
||||
foo.bar.y++
|
||||
result
|
||||
}
|
||||
// foo.bar = Bar(42) // Error! foo moved into consuming cl3
|
||||
println [1, 2, 3, 4].reduce(cl3) // "11243645"
|
||||
}
|
||||
|
||||
// Compiled
|
||||
|
||||
@Synthetic
|
||||
fn closure_main_cl0(acc: String, i: Int) = acc + i
|
||||
|
||||
@Synthetic
|
||||
class Closure_main_cl1_env(x: &Int) -> String {}
|
||||
|
||||
@Synthetic
|
||||
class Closure_main_cl1(env: Closure_main_cl1_env) : ReadClosure<String> {
|
||||
impl fn call(...args: Array<Any>) -> String {
|
||||
let acc: String = args[0]
|
||||
let i: Int = args[1]
|
||||
let x: Int = *env.x // because env.getX() -> &&Int
|
||||
acc + (i + x)
|
||||
}
|
||||
}
|
||||
|
||||
@Synthetic
|
||||
class Closure_main_cl2_env(counter: &mut Int) {}
|
||||
|
||||
@Synthetic
|
||||
class Closure_main_cl2(env: Closure_main_cl2_env) : MutClosure<String> {
|
||||
impl mut fn call(...args: Array<Any>) -> String {
|
||||
let acc: String = args[0]
|
||||
let i: Int = args[1]
|
||||
let counter = &mut env.counter
|
||||
let result = acc + (i + counter)
|
||||
*counter++
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@Synthetic
|
||||
class Closure_main_cl3(mut foo: Foo) : ConsClosure<String> {
|
||||
impl mut fn call(...args: Array<Any>) -> String {
|
||||
let acc: String = args[0]
|
||||
let i: Int = args[1]
|
||||
if foo.bar.y % 3 == 0 {
|
||||
foo.bar = Bar(1)
|
||||
}
|
||||
let result = acc + (i + foo.bar.y)
|
||||
foo.bar.y++
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let is: List<Int> = ArrayList() {
|
||||
add 1
|
||||
add 2
|
||||
add 3
|
||||
}
|
||||
let cl0 = &closure_main_cl0
|
||||
println is.reduce(cl0)
|
||||
|
||||
let x = 10
|
||||
let cl1_env = HashMap() {
|
||||
put 'x', &x
|
||||
}
|
||||
let cl1: Closure<String> = Closure::ReadClosure(Closure_main_cl1(cl1_env))
|
||||
println is.reduce(cl1)
|
||||
|
||||
let mut counter = 0
|
||||
let cl2_env = Closure_main_cl2_env(&counter)
|
||||
let cl2: Closure<String> = Closure::MutClosure(Closure_main_cl2(cl2_env))
|
||||
println is.reduce(cl2)
|
||||
|
||||
let mut foo = Foo(Bar(1))
|
||||
let cl3: Closure<String> = Closure::ConsClosure(Closure_main_cl3(foo)) // foo moved into closure
|
||||
let tmp0: List<Int> = ArrayList() {
|
||||
addAll 1, 2, 3, 4
|
||||
}
|
||||
println tmp0.reduce(cl3)
|
||||
}
|
||||
|
||||
// Example
|
||||
|
||||
// original
|
||||
class Foo {
|
||||
mut fld current = 0
|
||||
|
||||
ref fn getClosure(base: Int) -> (mut Closure(Int) -> Int) {
|
||||
return mut { given ->
|
||||
let result = base + given + current
|
||||
current++
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo = Foo()
|
||||
let cl = foo.getClosure(10)
|
||||
let nums = [cl(1), cl(2), cl(3), cl(4)]
|
||||
assertEq([11, 13, 15, 17], nums)
|
||||
}
|
||||
|
||||
// Compiles to
|
||||
|
||||
@Synthetic
|
||||
class Closure_Foo_getClosure_0(base: &Int, current: &mut Int) : MutClosure(Int) -> Int {
|
||||
impl mut fn call(given: Int) -> Int {
|
||||
let result = *base + given + *current
|
||||
*current++
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
class Foo {
|
||||
mut fld current = 0
|
||||
|
||||
ref fn getClosure(base: Int) -> (mut Closure(Int) -> Int) {
|
||||
return Closure_Foo_getClosure_0(&base, &mut current)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
// Library code
|
||||
|
||||
pub int ReadClosure(P : () = ())<T, D = Any, S = Any> : Into[Self(P)<T, D, S>, Option<D>] {
|
||||
mut fn setDelegate(d: D) -> Void
|
||||
ref fn getDelegate() -> Option<&D>
|
||||
mut fn setSelfObject(s: &S) -> Void
|
||||
ref fn getSelfObject() -> Option<&S>
|
||||
fn call(...params: P) -> T
|
||||
def op () (...params: P) -> T = call(...params)
|
||||
|
||||
def cons fn intoDelegate() -> D {
|
||||
Into[Self(P)<T, D, S>, Option<D>].into()
|
||||
}
|
||||
}
|
||||
|
||||
pub abstract class AbstractReadClosure(P : () = ())<T, D = Any, S = Any> : ReadClosure(P)<T, D, S> {
|
||||
mut fld delegate: Option<D>
|
||||
mut fld selfObject: Option<&S>
|
||||
|
||||
impl mut fn setDelegate(d: D) {
|
||||
delegate = Some(d)
|
||||
}
|
||||
|
||||
impl ref fn getDelegate() -> Option<&D> {
|
||||
&delegate is Some(d) ? Some(d) : None
|
||||
}
|
||||
|
||||
impl mut fn setSelfObject(s: &S) {
|
||||
selfObject = Some(s)
|
||||
}
|
||||
|
||||
impl ref fn getSelfObject() -> Option<&S> {
|
||||
&selfObject is Some(s) ? Some(s) : None
|
||||
}
|
||||
|
||||
impl Into[Self(P)<T, D, S>, D] {
|
||||
cons fn into() -> Option<D> = delegate
|
||||
}
|
||||
}
|
||||
|
||||
// User code demonstration
|
||||
|
||||
class Foo(bar: Bar) {}
|
||||
|
||||
class Bar(num: Int) {}
|
||||
|
||||
class Baz(foo: Foo) {
|
||||
pub ref fn getClosure() -> Closure<Int, D = Foo> {
|
||||
return { foo.bar.num + bar.num }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let baz0 = Baz(Foo(Bar(1)))
|
||||
let cl = baz0.getClosure()
|
||||
let delegate = Foo(Bar(2))
|
||||
cl.delegate = delegate
|
||||
let result0 = cl()
|
||||
assertEq(3, result0)
|
||||
let baz1 = Baz(Foo(Bar(2)))
|
||||
cl.selfObject = &baz1
|
||||
let result1 = cl()
|
||||
assertEq(4, result1)
|
||||
let movedDelegate = cl.intoDelegate()
|
||||
assertEq(2, movedDelegate.bar.num)
|
||||
}
|
||||
|
||||
// User code compiles to
|
||||
|
||||
class Foo(bar: Bar) {}
|
||||
|
||||
class Bar(num: Int) {}
|
||||
|
||||
@Synthetic
|
||||
class Closure_Baz_getClosure_0: AbstractReadClosure()<Int, D = Foo> {
|
||||
ctor(selfObject: &Baz) ref selfObject {
|
||||
self.selfObject = Some(selfObject)
|
||||
}
|
||||
|
||||
fn call() -> Int {
|
||||
let t0 = selfObject.unwrap().foo.bar.num
|
||||
let t1 = self.delegate.unwrap().bar.num
|
||||
t0 + t1
|
||||
}
|
||||
}
|
||||
|
||||
class Baz(foo: Foo) {
|
||||
pub ref fn getClosure() -> Closure<Int, D = Foo> {
|
||||
return Closure_Baz_getClosure_0(&self)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// fn vs Closure syntax
|
||||
|
||||
int FnOrClosureTaker {
|
||||
fn read(f: fn (Int) -> Int) -> Int
|
||||
fn mut(f: mut fn (Int) -> Int) -> Int
|
||||
fn cons(f: cons fn (Int) -> Int) -> Int
|
||||
}
|
||||
|
||||
class FnOrClosureTakerImpl : FnOrClosureTaker {
|
||||
impl fn read(f: fn (Int) -> Int) {
|
||||
let i0 = f(0)
|
||||
let i1 = f(1)
|
||||
let i2 = f(2)
|
||||
i0 + i1 + i2
|
||||
}
|
||||
|
||||
impl fn mut(f: mut fn (Int) -> Int) {
|
||||
/* same as read */
|
||||
}
|
||||
|
||||
impl fn cons(f: cons fn (Int) -> Int) {
|
||||
let i0 = f()
|
||||
// let i1 = f() // Error! f already consumed
|
||||
i0
|
||||
}
|
||||
}
|
||||
|
||||
mod User {
|
||||
fn getReader() -> fn (Int) -> Int {
|
||||
return { it * 2 }
|
||||
}
|
||||
|
||||
fn useReader() {
|
||||
let reader = getReader()
|
||||
let fnOrClosureTaker: FnOrClosureTaker = FnOrClosureTakerImpl()
|
||||
let result = fnOrClosureTaker.read(reader)
|
||||
assertEq(6, result)
|
||||
}
|
||||
}
|
||||
|
||||
// compiles to
|
||||
|
||||
mod User {
|
||||
fn getReader_closure_0(it: Int) -> Int {
|
||||
it * 2
|
||||
}
|
||||
|
||||
fn getReader() -> &fn (Int) -> Int {
|
||||
&getReader_closure_0
|
||||
}
|
||||
|
||||
// etc.
|
||||
}
|
||||
|
||||
// New interfaces?
|
||||
|
||||
pub int ReadClosure(P : () = ())<D> -> T : fn (P) -> T {
|
||||
ref fn getDelegate() -> Option<&D>
|
||||
mut fn setDelegate(d: D) -> Void
|
||||
fn call(...p: P) -> T
|
||||
def op () alias call
|
||||
}
|
||||
|
||||
pub int Iterator<T> {
|
||||
mut fn next() -> Option<T>
|
||||
}
|
||||
|
||||
pub int Iterable<T> : Into[Self<T>, Iterator<T>] {
|
||||
ref fn iterator() -> Iterator<&T>
|
||||
|
||||
def cons fn intoIterator() = Into[Self, Iterator].into(self)
|
||||
}
|
||||
|
||||
pub class MyIterable : Iterable<Int> {
|
||||
fld items: List<Int> = [1, 2, 3]
|
||||
|
||||
impl ref fn iterator() -> Iterator<&Int> {
|
||||
let mut index = 0
|
||||
return mut {
|
||||
if index >= items.length {
|
||||
None
|
||||
} else {
|
||||
let next = items[index]
|
||||
index++
|
||||
Some(next)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
260
sketching/april_2025/hkt.dm
Normal file
260
sketching/april_2025/hkt.dm
Normal file
@ -0,0 +1,260 @@
|
||||
pub hkt Functor[Self<T>] {
|
||||
fn <U> map(f: fn &T -> U) -> Self<U> // &self implied
|
||||
}
|
||||
|
||||
pub hkt Applicative[Self<T>] : Functor[Self<T>] {
|
||||
static fn lift(t: T) -> Self<T> // static: no implicit self parameter
|
||||
fn <U, V> apply(us: &Self<U>) -> Self<V> where T : fn (u: &U) -> V
|
||||
}
|
||||
|
||||
pub hkt Monad[Self<T>] : Applicative[Self<T>] {
|
||||
fn <U> flatMap(f: fn (t: &T) -> Self<U>) -> Self<U>
|
||||
}
|
||||
|
||||
// Here, T is a separate type parameter from the container's inner type
|
||||
// The container's inner type is ..., which can be anything, of any number of parameters
|
||||
pub hkt Into[Self<...>, T] {
|
||||
cons fn into() -> T // consumes self
|
||||
}
|
||||
|
||||
pub hkt Zero[Self<T>] {
|
||||
static fn zero() -> Self<T>
|
||||
}
|
||||
|
||||
pub int Iterable<T> : Into[Self<T>, Iterator<T>] {
|
||||
/**
|
||||
* Iterator for references to contained item(s).
|
||||
*/
|
||||
ref fn iter() -> Iterator<&T> // ref indicates return value cannot outlive self
|
||||
|
||||
def cons fn intoIter() = Into[Self<T>, Iterator<T>].into()
|
||||
}
|
||||
|
||||
pub type Iterator<T> = () -> Option<T>
|
||||
|
||||
pub int Exception {
|
||||
message: Option<String>
|
||||
}
|
||||
|
||||
pub enum Option<T> : Monad[Self<T>] {
|
||||
Some(T),
|
||||
None;
|
||||
|
||||
class EmptyOptionException(message: Option<String> = Empty) : Exception
|
||||
|
||||
// cons implies that self is consumed (like Rust `self: Self`)
|
||||
cons fn unwrap() -> T = self is Some(t) ? t : throw EmptyOptionException()
|
||||
|
||||
cons fn expect(message: String) -> T {
|
||||
if let Some(t) = self {
|
||||
t
|
||||
} else {
|
||||
throw EmptyOptionException(message)
|
||||
}
|
||||
}
|
||||
|
||||
cons fn unwrapOr(self, other: T) -> T = self is Some(t) ? t : other
|
||||
|
||||
cons fn unwrapOrElse(self, f: fn () -> T) = self is Some(t) ? t : f()
|
||||
|
||||
impl Monad[Self<T>] {
|
||||
fn <U> map(f: fn (t: &T) -> U) -> Self<U> = self is Some(t) ? Some(f(t)) : Empty
|
||||
|
||||
static fn lift(t: T) -> Self<T> = Some(t)
|
||||
|
||||
fn <U, V> apply(us: &Self<U>) -> Self<V> where T : fn (u: U) -> V =
|
||||
self is Some(f) && us is Some(u) ? Some(f(u)) : Empty
|
||||
// note in above that `f` is `&fn (u: U) -> V`, but `f()` is sugar for `(*f)()`
|
||||
|
||||
fn <U> flatMap(f: fn (t: &T) -> Self<U>) -> Self<U> = self is Some(t) ? f(t) : Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub class OptionT[M]<T>(fld o: M<Option<T>>) : Monad[Self[M]<T>] where M : Monad[Option<T>] {
|
||||
pub fn intoInner() -> M<Option<T>> = Into[Self[M]<T>, M<Option<T>>].into(self)
|
||||
|
||||
pub fn <U> flatMapInner(f: fn (t: &T) -> M<Option<U>>) -> Self[M]<U> {
|
||||
OptionT(
|
||||
Monad[M].flatMap(o) { ts: &Option<T> ->
|
||||
ts is Some(t) ? f(t) : None
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl Monad[Self[M]<T>] {
|
||||
fn <U> map(f: fn (t: &T) -> U) -> Self[M]<U> {
|
||||
OptionT(
|
||||
Monad[M].map(o) {
|
||||
// implicit `it` parameter is `&Option<T>`
|
||||
// When destructured, `t` is `&T`
|
||||
it is Some(t) ? Some(f(t)) : None
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
static fn lift(t: T) -> Self[M]<T> = OptionT(Monad[M].lift(Some(t)))
|
||||
|
||||
fn <U, V> apply(us: &Self[M]<U>) -> Self[M]<V> where T : fn (u: &U) -> V {
|
||||
OptionT(
|
||||
Monad[M].flatMap(o) { fOpt: &Option<fn (u: &U) -> V> ->
|
||||
fOpt is Some(f)
|
||||
? Monad[M].map(us.o) { uOpt: &Option<U> ->
|
||||
uOpt is Some(u) ? Some(f(u)) : None
|
||||
}
|
||||
: Monad[M].lift(None)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn <U> flatMap(f: fn (t: &T) -> Self[M]<U>) -> Self[M]<U> {
|
||||
OptionT(
|
||||
Monad[M].flatMap(o) {
|
||||
it is Some(t) ? f(t).o : Monad[M].lift(None)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Self[M]<T>, M<Option<T>>> {
|
||||
cons fn into() -> M<Option<T>> = o
|
||||
}
|
||||
}
|
||||
|
||||
pub int List<T> : Iterable<T> + Zero[Self<T>] + Monad[Self<T>] {
|
||||
mut fn add(t: T) -> Void // mut implies &mut self
|
||||
|
||||
// def = default
|
||||
// mut = &mut self
|
||||
// op = operator overload
|
||||
// << = left shift
|
||||
def mut op << (t: T) -> Void = add(t)
|
||||
|
||||
ref fn get(i: Int) -> Option<&T> // ref implies that the returned object cannot outlive self
|
||||
// implied &self
|
||||
|
||||
// Alternative syntax for get(i) definition:
|
||||
// fn get(i: Int) -> Option<&T> ref self
|
||||
|
||||
def ref op [] (i: Int) -> Option<&T> = get(i)
|
||||
|
||||
impl Monad[Self<T>] {
|
||||
fn <U> map(f: fn (t: &T) -> U) -> Self<U> {
|
||||
let out: List<U> = Zero[Self<U>].zero()
|
||||
for item in self { // item is &T
|
||||
out << f(item)
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
static fn lift(t: T) -> Self<T> = ArrayList(t)
|
||||
|
||||
fn <U ,V> apply(us: &Self<U>) -> Self<V> where T : fn (u: &U) -> V {
|
||||
let out: List<V> = Zero[Self<V>].zero()
|
||||
for f in self { // f is &fn (u: &U) -> V
|
||||
for u in us { // u is &&u
|
||||
out << f(*u) // syntax sugar for out.add((*f)(*u))
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn <U> flatMap(f: fn (t: &T) -> Self<U>) -> Self<U> {
|
||||
let out: List<U> = Zero[Self<U>].zero()
|
||||
for t in self { // t is &T
|
||||
for u in Into[Self<U>, Iterator<U>].into(f(t)) { // u is U
|
||||
out << u // out take ownership of u
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub class LinkedList<T> : List<T> {
|
||||
|
||||
// inner classes are static by default
|
||||
class Node<T>(data: T) {
|
||||
mut next: Option<Self<T>>
|
||||
}
|
||||
|
||||
mut fld head: Option<Node<T>>
|
||||
|
||||
impl mut fn add(t: T) -> Void {
|
||||
match head {
|
||||
None => {
|
||||
head = Some(Node(t))
|
||||
}
|
||||
Some => {
|
||||
let mut current: &Option<Node<T>> = &head
|
||||
while let Some(next) = ¤t?.next { // get a reference to current.next, or None if either are none
|
||||
current = next
|
||||
}
|
||||
current.next = Some(Node(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ref fn get(index: Int) -> Option<&T> {
|
||||
let mut current: &Option<Node<T>> = &head
|
||||
let mut currentIndex = 0
|
||||
while currentIndex < index {
|
||||
if let Some(next) = ¤t?.next {
|
||||
currentIndex++
|
||||
current = next
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
current is Some(node) ? Some(&node.data) : None
|
||||
}
|
||||
|
||||
impl ref fn iter() -> Iterator<&T> {
|
||||
let mut current: &Option<Node<T>> = &head
|
||||
() -> {
|
||||
if let Some(node) = current {
|
||||
let data: &T = &node.data
|
||||
current = &node.next
|
||||
Some(data)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into[Self<T>, Iterator<T>] {
|
||||
cons fn into() -> Iterator<T> {
|
||||
let mut current: Option<Node<T>> = take(head) // take replaces head with None
|
||||
return () -> {
|
||||
let result = current
|
||||
current = current?.next
|
||||
result is Some(node) ? Some(node.data) : None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero[Self<T>] {
|
||||
static fn zero() -> Self<T> = LinkedList()
|
||||
}
|
||||
}
|
||||
|
||||
fn optionTListDemo() {
|
||||
let oddsOnly: List<Option<Int>> = [
|
||||
Some(1),
|
||||
None,
|
||||
Some(3),
|
||||
None,
|
||||
Some(5)
|
||||
]
|
||||
|
||||
let liftedOddsOnly: OptionT[]<> = OptionT(oddsOnly)
|
||||
|
||||
let mapped = liftedOddsOnly
|
||||
.flatMapInner { [Some(it), Some(it + 1)] }
|
||||
.map { "Value: ${it.toString()}" }
|
||||
|
||||
for o in mapped.intoInner() {
|
||||
if o is Some(s) {
|
||||
println s
|
||||
}
|
||||
}
|
||||
}
|
125
sketching/april_2025/string.dm
Normal file
125
sketching/april_2025/string.dm
Normal file
@ -0,0 +1,125 @@
|
||||
pub int Copy {
|
||||
fn copy() -> Self
|
||||
}
|
||||
|
||||
pub int Display {
|
||||
fn toString() -> String
|
||||
}
|
||||
|
||||
pub int LazyDisplay {
|
||||
ref fn toLazyString() -> DString
|
||||
}
|
||||
|
||||
decl pub class String : Copy, Display {
|
||||
decl impl fn toString() -> String
|
||||
decl impl fn copy() -> Self
|
||||
}
|
||||
|
||||
pub int DString : Display {
|
||||
parts: Array<DStringPart>
|
||||
}
|
||||
|
||||
pub enum DStringPart {
|
||||
Constant(String),
|
||||
Display(Display),
|
||||
ReadClosure(ReadClosure<Display>),
|
||||
ReadRefClosure(ReadRefClosure<Display>)
|
||||
}
|
||||
|
||||
#internal
|
||||
class DStringImpl(parts) : DString {
|
||||
mut fld cachedEval: Option<String>
|
||||
mut fld canCache: Option<Boolean>
|
||||
|
||||
fld cachedReads: Map<&ReadClosure ref self, String> = HashMap()
|
||||
|
||||
fn calcCanCache() -> Boolean {
|
||||
for part in parts {
|
||||
if part is ReadRefClosure {
|
||||
return false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
impl Display {
|
||||
fn toString() -> String {
|
||||
if cachedEval is Some(s) {
|
||||
return s
|
||||
}
|
||||
if canCache is None {
|
||||
canCache = Some(calcCanCache())
|
||||
}
|
||||
let canCache = canCache.unwrap()
|
||||
if canCache {
|
||||
cachedEval = Some(parts.reduce('') { acc, part ->
|
||||
acc + match part {
|
||||
Constant(s) => s,
|
||||
Display(d) => d,
|
||||
ReadClosure(c) => c(),
|
||||
ReadRefClosure(c) => throw DumbProgrammerException(
|
||||
'Cannot cache when parts contains a ReadRefClosure'
|
||||
)
|
||||
}
|
||||
})
|
||||
return cachedEval.unwrap()
|
||||
} else {
|
||||
return parts.reduce('') { acc, part ->
|
||||
acc + match part {
|
||||
Constant(s) => s,
|
||||
Display(d) => d,
|
||||
ReadClosure(c) => {
|
||||
if let Some(s) = cachedReads.get(&c) {
|
||||
s
|
||||
} else {
|
||||
let s = c()
|
||||
cachedReads.put(&c, s)
|
||||
s
|
||||
}
|
||||
},
|
||||
ReadRefClosure(c) => c()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Foo(mut bar: Bar) {}
|
||||
|
||||
class Bar(mut y: Int) {}
|
||||
|
||||
fn stringDemo() {
|
||||
let plain: String = 'Hello, World!'
|
||||
let x = 42
|
||||
let withDisplay: String = "Hello! x is $x".
|
||||
let withReadClosure: String = "Hello! x is ${ x + 42 }".
|
||||
let mut foo = Foo(Bar(16))
|
||||
let withReadRefClosure: String = "Hello! y is ${ foo.bar.y }" // "Hello!, y is 16"
|
||||
let lazyDString = `Lazy: y is ${ -> foo.bar.y }` // type is DString, not auto String
|
||||
let eval0: String = lazyDString.toString() // Lazy: y is 16
|
||||
foo.bar.y = 64
|
||||
let eval1: String = lazyDString.toString() // Lazy: y is 64
|
||||
}
|
||||
|
||||
fn badDStringClosures() {
|
||||
let x = 16
|
||||
let c0 = {
|
||||
x // Int implements copy, so nothing consumed
|
||||
} // ReadClosure
|
||||
let foo = Foo(Bar(8))
|
||||
let c1 = {
|
||||
foo.bar.y
|
||||
} // Read closure
|
||||
let c2_e0 = {
|
||||
foo.bar
|
||||
} // Error! foo.bar cannot be moved out
|
||||
let c2_e1 = {
|
||||
&foo.bar
|
||||
} // Error! reference to foo.bar moved out; closure must declared ref
|
||||
let c2 = {
|
||||
&foo.bar
|
||||
} ref foo // OK, captures foo and return value is tied to lifetime of foo
|
||||
drop(foo)
|
||||
c2() // Error! c2 outlives foo
|
||||
}
|
Loading…
Reference in New Issue
Block a user