├── .gitignore ├── LICENSE ├── README.md └── lazy.ts /.gitignore: -------------------------------------------------------------------------------- 1 | deno_linux_x64 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazy Evaluation 2 | 3 | ## Quick Start 4 | 5 | On Linux x86_64 6 | 7 | ```console 8 | $ wget https://github.com/denoland/deno/releases/download/v0.32.0/deno_linux_x64.gz 9 | $ gunzip deno_linux_x64.gz 10 | $ chmod +x deno_linux_x64 11 | $ ./deno_linux_x64 ./lazy.ts 12 | ``` 13 | -------------------------------------------------------------------------------- /lazy.ts: -------------------------------------------------------------------------------- 1 | // Introduction ////////////////////////////// 2 | 3 | function sum(a: number, b: number): number { 4 | return a + b; 5 | } 6 | 7 | console.log("Eager sum:\t", sum(10 + 5, 20)); 8 | 9 | type Lazy = () => T 10 | 11 | function lazySum(a: Lazy, b: Lazy): Lazy { 12 | return () => a() + b(); 13 | } 14 | 15 | console.log("Lazy sum:\t", lazySum(() => 10 + 5, () => 20)()); 16 | 17 | // Avoiding big computations that are not needed ////////////////////////////// 18 | 19 | function hang(): T { 20 | return hang() 21 | } 22 | 23 | function first(a: number, b: number): number { 24 | return a; 25 | } 26 | 27 | function lazyFirst(a: Lazy, b: Lazy): Lazy { 28 | return a; 29 | } 30 | 31 | console.log("Lazy First: ", lazyFirst(() => 10, () => hang())()); 32 | 33 | // Short-circuit computation ////////////////////////////// 34 | 35 | console.log("\n==============================\n"); 36 | 37 | function and(a: Lazy, b: Lazy): Lazy { 38 | return () => !a() ? false : b(); 39 | } 40 | 41 | function trace(x: Lazy, message: string): Lazy { 42 | return () => { 43 | console.log(message); 44 | return x(); 45 | } 46 | } 47 | 48 | console.log("false && false ==", and(trace(()=> false, "L"), 49 | trace(()=> false, "R"))()); 50 | console.log("true && false ==", and(trace(()=> true, "L"), 51 | trace(()=> false, "R"))()); 52 | console.log("true && true ==", and(trace(()=> true, "L"), 53 | trace(()=> true, "R"))()); 54 | console.log("false && true ==", and(trace(()=> false, "L"), 55 | trace(()=> true, "R"))()); 56 | 57 | function or(a: Lazy, b: Lazy): Lazy { 58 | return () => a() ? true : b(); 59 | } 60 | 61 | console.log("---"); 62 | console.log("false || false ==", or(trace(()=> false, "L"), 63 | trace(()=> false, "R"))()); 64 | console.log("true || false ==", or(trace(()=> true, "L"), 65 | trace(()=> false, "R"))()); 66 | console.log("true || true ==", or(trace(()=> true, "L"), 67 | trace(()=> true, "R"))()); 68 | console.log("false || true ==", or(trace(()=> false, "L"), 69 | trace(()=> true, "R"))()); 70 | 71 | // Infinite Data Structures ////////////////////////////// 72 | 73 | type LazyList = Lazy<{ 74 | head: Lazy, 75 | tail: LazyList 76 | } | null> 77 | 78 | console.log("\n==============================\n"); 79 | 80 | function toList(xs: T[]): LazyList { 81 | return () => { 82 | if (xs.length === 0) { 83 | return null; 84 | } else { 85 | return { 86 | head: () => xs[0], 87 | tail: toList(xs.slice(1)) 88 | }; 89 | } 90 | }; 91 | } 92 | 93 | console.log(toList([1, 2, 3])); 94 | console.log(toList([1, 2, 3])()); 95 | console.log(toList([1, 2, 3])()!.head()); 96 | console.log(toList([1, 2, 3])()!.tail()!.head()); 97 | console.log(toList([1, 2, 3])()!.tail()!.tail()!.head()); 98 | console.log(toList([1, 2, 3])()!.tail()!.tail()!.tail()); 99 | 100 | function range(begin: Lazy): LazyList { 101 | return () => { 102 | let x = begin(); 103 | return { 104 | head: () => x, 105 | tail: range(() => x + 1) 106 | }; 107 | } 108 | } 109 | 110 | console.log("---"); 111 | 112 | console.log(range(() => 3)); 113 | console.log(range(() => 3)()); 114 | console.log(range(() => 3)()!.head()); 115 | console.log(range(() => 3)()!.tail()!.head()); 116 | console.log(range(() => 3)()!.tail()!.tail()!.head()); 117 | console.log(range(() => 3)()!.tail()!.tail()!.tail()!.head()); 118 | console.log(range(() => 3)()!.tail()!.tail()!.tail()!.tail()!.head()); 119 | 120 | function printList(xs: LazyList) { 121 | let pair = xs() 122 | while(pair !== null) { 123 | console.log(pair.head()); 124 | pair = pair.tail() 125 | } 126 | } 127 | 128 | console.log("---"); 129 | 130 | printList(toList([1, 2, 3, 4, 5])); 131 | 132 | console.log("---"); 133 | 134 | function take(n: Lazy, xs: LazyList): LazyList { 135 | return () => { 136 | let m = n(); 137 | let pair = xs(); 138 | if (m > 0 && pair !== null) { 139 | return { 140 | head: pair.head, 141 | tail: take(() => m - 1, pair.tail) 142 | } 143 | } else { 144 | return null; 145 | } 146 | }; 147 | } 148 | 149 | printList(take(() => 10, range(() => 3))); 150 | 151 | function filter(f: (x: T) => boolean, xs: LazyList): LazyList { 152 | return () => { 153 | let pair = xs(); 154 | if (pair === null) { 155 | return null; 156 | } else { 157 | let x = pair.head(); 158 | if (f(x)) { 159 | return { 160 | head: () => x, 161 | tail: filter(f, pair.tail) 162 | }; 163 | } else { 164 | return filter(f, pair.tail)(); 165 | } 166 | } 167 | }; 168 | } 169 | 170 | console.log("---"); 171 | 172 | printList( 173 | take(() => 10, 174 | filter((x) => x % 2 === 0, 175 | range(() => 1)))); 176 | 177 | 178 | console.log("\n==============================\n"); 179 | 180 | function sieve(xs: LazyList): LazyList { 181 | return () => { 182 | let pair = xs(); 183 | if (pair === null) { 184 | return null; 185 | } else { 186 | let y = pair.head(); 187 | return { 188 | head: () => y, 189 | tail: sieve(filter((x) => x % y !== 0, pair.tail)) 190 | }; 191 | } 192 | }; 193 | } 194 | 195 | let prime = sieve(range(() => 2)); 196 | 197 | printList(take(() => 10, prime)); 198 | --------------------------------------------------------------------------------