126 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
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
 | 
						|
}
 |