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
 | |
| }
 | 
