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 } pub enum DStringPart { Constant(String), Display(Display), ReadClosure(ReadClosure), ReadRefClosure(ReadRefClosure) } #internal class DStringImpl(parts) : DString { mut fld cachedEval: Option mut fld canCache: Option 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 }