├── .gitignore ├── README.md ├── intellij highlighting └── pony-file-type-intellij-settings.jar └── src ├── function-types └── fn.pony ├── mutable-data ├── benchmarks │ └── bench.pony ├── lists.pony ├── mutable-data └── test.pony └── persistent-data ├── Option.pony ├── benchmarks └── bench.pony ├── list.pony ├── map.pony ├── persistent-data ├── persistent.pony └── test.pony /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | *.dSYM/ 4 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pony-functional-data 2 | Functional data structures and transformations for the Pony programming language 3 | 4 | ## persistent-data/Map 5 | 6 | A persistent immutable map based on the Hash Array Mapped Trie described by Bagwell in 7 | this paper: http://lampwww.epfl.ch/papers/idealhashtrees.pdf and inspired by 8 | Clojure persistent maps (using path copying for sharing structure). 9 | 10 | Currently has the following methods: 11 | ``` 12 | (For K: (Hashable val & Equatable[K] val) and V: Any val) 13 | 14 | fun size(): U64 15 | 16 | fun apply(k: K): (V | None) ? 17 | 18 | fun get(k: K): (V | None) ? 19 | 20 | fun getOption(k: K): Option[V] ? 21 | 22 | fun getOrElse(k: K, v: V): V ? 23 | 24 | fun put(k: K, v: V): Map[K,V] ? 25 | 26 | fun contains(k: K): Bool ? 27 | 28 | fun remove(k: K): Map[K,V] ? 29 | ``` 30 | 31 | There is also a primitive called Maps with helper methods: 32 | ``` 33 | //Creates an empty Map 34 | empty[K,V](): Map[K,V] 35 | 36 | //Creates a Map from an array of tuples (k, v) 37 | from[K,V](arr: Array[(K, V)]): Map[K,V] 38 | ``` 39 | 40 | ## persistent-data/List, Lists 41 | 42 | Immutable linked list 43 | 44 | Currently has the following methods: 45 | ``` 46 | size(): U64 47 | 48 | is_empty(): Bool 49 | 50 | is_non_empty(): Bool 51 | 52 | head(): A ? 53 | 54 | tail(): List[A] ? 55 | 56 | reverse(): List[A] 57 | 58 | prepend(a: A): List[A] 59 | 60 | concat(l: List[A]): List[A] 61 | 62 | map[B: Any val](f: Fn1[A!,B^]): List[B] ? 63 | 64 | flat_map[B: Any val](f: Fn1[A!,List[B]]): List[B] ? 65 | 66 | for_each(f: SeFn1[A!]) ? 67 | 68 | filter(f: Fn1[A!, Bool]): List[A] ? 69 | 70 | fold[B: Any val](f: Fn2[B!,A!,B^], acc: B): B ? 71 | 72 | every(f: Fn1[A!,Bool]): Bool ? 73 | 74 | exists(f: Fn1[A!,Bool]): Bool ? 75 | 76 | partition(f: Fn1[A!,Bool]): (List[A], List[A]) ? 77 | 78 | drop(n: U64): List[A] 79 | 80 | drop_while(f: Fn1[A!,Bool]): List[A] ? 81 | 82 | take(n: U64): List[A] 83 | 84 | take_while(f: Fn1[A!,Bool]): List[A] ? 85 | 86 | ``` 87 | 88 | There is also a primitive called Lists with helper methods: 89 | ``` 90 | //Returns empty List of As 91 | empty[A: Any val](): List[A] 92 | 93 | cons[A: Any val](a: A, t: List[A]): List[A] 94 | 95 | //Create a list from an Array of As 96 | // e.g. ListT.from[U32]([1, 2, 3, 4]) 97 | from[A: Any val](arr: Array[A]): List[A] 98 | 99 | flatten[A: Any val](l: List[List[A]]): List[A] 100 | 101 | eq[A: Equatable[A] val](l1: List[A], l2: List[A]): Bool ? 102 | 103 | ``` 104 | 105 | ## persistent-data/Option 106 | 107 | An Option[V] is either an ONone[V] or an OSome[V] 108 | 109 | Currently has the following methods: 110 | ``` 111 | is_empty(): Bool 112 | 113 | is_non_empty(): Bool 114 | 115 | value(): V ? 116 | 117 | map[B: Any val](f: Fn1[V!,B^]): Option[B] ? 118 | 119 | flat_map[B: Any val](f: Fn1[V!,Option[B]]): Option[B] ? 120 | 121 | filter(f: Fn1[V!,Bool]): Option[V] ? 122 | ``` 123 | 124 | 125 | ## mutable-data/Lists 126 | 127 | Helper methods for the "collections" package mutable List 128 | 129 | The primitive mutable-data/Lists has the following methods: 130 | 131 | ``` 132 | unit[A](a: A): List[A] 133 | 134 | map[A: Any #read, B](l: List[A], f: Fn1[A!, B^]): List[B] 135 | 136 | flat_map[A: Any #read, B](l: List[A], f: Fn1[A!,List[B]]): List[B] 137 | 138 | flatten[A](l: List[List[A]]): List[A] 139 | 140 | filter[A: Any #read](l: List[A], f: Fn1[A!, Bool]): List[A] 141 | 142 | fold[A: Any #read,B: Any #read](l: List[A], f: Fn2[B!,A!,B^], acc: B): B 143 | 144 | every[A: Any #read](l: List[A], f: Fn1[A!,Bool]): Bool 145 | 146 | exists[A: Any #read](l: List[A], f: Fn1[A!,Bool]): Bool 147 | 148 | partition[A: Any #read](l: List[A], f: Fn1[A!,Bool]): (List[A], List[A]) 149 | 150 | drop[A: Any #read](l: List[A], n: USize): List[A] 151 | 152 | take[A: Any #read](l: List[A], n: USize): List[A] 153 | 154 | take_while[A: Any #read](l: List[A], f: Fn1[A!,Bool]): List[A] 155 | ``` 156 | 157 | ## function-types 158 | 159 | Provides abstract functional interfaces: 160 | ``` 161 | Fn0[OUT] 162 | 163 | Fn1[IN1: Any #read,OUT] 164 | 165 | Fn2[IN1: Any #read,IN2: Any #read,OUT] 166 | 167 | Fn3[IN1: Any #read,IN2: Any #read,IN3: Any #read,OUT] 168 | 169 | Fn4[IN1: Any #read,IN2: Any #read,IN3: Any #read,IN4: Any #read,OUT] 170 | 171 | Fn5[IN1: Any #read,IN2: Any #read,IN3: Any #read,IN4: Any #read,IN5: Any #read,OUT] 172 | 173 | // Side effecting function 174 | SeFn1[IN1: Any #read] 175 | ``` 176 | -------------------------------------------------------------------------------- /intellij highlighting/pony-file-type-intellij-settings.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtfmumm/pony-functional-data/182f4dc9f49c8946aedf48cacb2226b684627987/intellij highlighting/pony-file-type-intellij-settings.jar -------------------------------------------------------------------------------- /src/function-types/fn.pony: -------------------------------------------------------------------------------- 1 | interface box Fn0[OUT] 2 | fun apply(): OUT ? 3 | interface box Fn1[IN1: Any #read,OUT] 4 | fun apply(a: IN1): OUT ? 5 | interface box Fn2[IN1: Any #read,IN2: Any #read,OUT] 6 | fun apply(a: IN1, b: IN2): OUT ? 7 | interface box Fn3[IN1: Any #read,IN2: Any #read,IN3: Any #read,OUT] 8 | fun apply(a: IN1, b: IN2, c: IN3): OUT ? 9 | interface box Fn4[IN1: Any #read,IN2: Any #read,IN3: Any #read,IN4: Any #read,OUT] 10 | fun apply(a: IN1, b: IN2, c: IN3, d: IN4): OUT ? 11 | interface box Fn5[IN1: Any #read,IN2: Any #read,IN3: Any #read,IN4: Any #read,IN5: Any #read,OUT] 12 | fun apply(a: IN1, b: IN2, c: IN3, d: IN4, e: IN5): OUT ? 13 | interface box SeFn1[IN1: Any #read] 14 | fun apply(a: IN1) ? -------------------------------------------------------------------------------- /src/mutable-data/benchmarks/bench.pony: -------------------------------------------------------------------------------- 1 | use "../" 2 | use mut = "collections" 3 | use "time" 4 | use "debug" 5 | use "random" 6 | 7 | 8 | primitive Bench 9 | fun bench(iterations: USize, keys: U64) ? => 10 | var pMap: Map[String,U64] = Maps.empty[String,U64]() 11 | let mMap: mut.Map[String,U64] = mut.Map[String,U64]() 12 | let kvs = Array[(String,U64)]() 13 | // Different seed each run 14 | let dice = Dice(MT(Time.millis())) 15 | var count: USize = 0 16 | var perf_begin: U64 = 0 17 | var perf_end: U64 = 0 18 | 19 | while(count < iterations) do 20 | let k0 = dice(1,keys).string() 21 | let k: String val = consume k0 22 | let v = dice(1,100000) 23 | kvs.push((k, v)) 24 | count = count + 1 25 | end 26 | 27 | //WRITES 28 | count = 0 29 | Time.perf_begin() 30 | perf_begin = Time.millis() 31 | while(count < iterations) do 32 | let k = kvs(count)._1 33 | let v = kvs(count)._2 34 | pMap = pMap.put(k, v) 35 | count = count + 1 36 | end 37 | perf_end = Time.millis() 38 | Time.perf_end() 39 | Debug.out("Persistent writes: " + (perf_end - perf_begin).string()) 40 | 41 | count = 0 42 | Time.perf_begin() 43 | perf_begin = Time.millis() 44 | while(count < iterations) do 45 | let k = kvs(count)._1 46 | let v = kvs(count)._2 47 | mMap.update(k, v) 48 | count = count + 1 49 | end 50 | perf_end = Time.millis() 51 | Time.perf_end() 52 | Debug.out("Mutable writes: " + (perf_end - perf_begin).string()) 53 | 54 | 55 | //READS 56 | count = 0 57 | Time.perf_begin() 58 | perf_begin = Time.millis() 59 | while(count < iterations) do 60 | let pmv = pMap.get(kvs(count)._1) 61 | count = count + 1 62 | end 63 | perf_end = Time.millis() 64 | Time.perf_end() 65 | Debug.out("Persistent reads: " + (perf_end - perf_begin).string()) 66 | 67 | count = 0 68 | Time.perf_begin() 69 | perf_begin = Time.millis() 70 | while(count < iterations) do 71 | let mmv = mMap(kvs(count)._1) 72 | count = count + 1 73 | end 74 | perf_end = Time.millis() 75 | Time.perf_end() 76 | Debug.out("Mutable reads: " + (perf_end - perf_begin).string()) 77 | 78 | // fun list_bench(iterations: U64, keys: U64) => 79 | // var list: List[U64] = Lists.empty[U64]() 80 | // // Different seed each run 81 | // let dice = Dice(MT) 82 | // var count: U64 = 0 83 | // var perf_begin: U64 = 0 84 | // var perf_end: U64 = 0 85 | // 86 | // var l1: List[U64] = Lists.empty[U64]() 87 | // var l2: List[U64] = Lists.empty[U64]() 88 | // var l3: List[U64] = Lists.empty[U64]() 89 | // var l4: List[U64] = Lists.empty[U64]() 90 | // 91 | // while(count < iterations) do 92 | // let v: U64 = dice(1,100000) 93 | // l1 = l1.prepend(v) 94 | // l2 = l2.prepend(v) 95 | // l3 = l3.prepend(v) 96 | // l4 = l4.prepend(v) 97 | // count = count + 1 98 | // end 99 | // 100 | // //WRITES 101 | // Time.perf_begin() 102 | // perf_begin = Time.millis() 103 | //// l1.reverse() 104 | //// l2.reverse() 105 | //// l3.reverse() 106 | //// l4.reverse() 107 | //// l1.reverse() 108 | //// l2.reverse() 109 | //// l3.reverse() 110 | //// l4.reverse() 111 | //// l1.reverse() 112 | //// l2.reverse() 113 | //// l3.reverse() 114 | //// l4.reverse() 115 | // perf_end = Time.millis() 116 | // Time.perf_end() 117 | // Debug.out("TRY: reverse: " + (perf_end - perf_begin).string()) 118 | 119 | // Time.perf_begin() 120 | // perf_begin = Time.millis() 121 | //// l1.concat(l2) 122 | //// l2.concat(l3) 123 | //// l3.concat(l4) 124 | //// l4.concat(l1) 125 | //// l1.concat(l2) 126 | //// l2.concat(l3) 127 | //// l3.concat(l4) 128 | //// l4.concat(l1) 129 | //// l1.concat(l2) 130 | //// l2.concat(l3) 131 | //// l3.concat(l4) 132 | //// l4.concat(l1) 133 | // perf_end = Time.millis() 134 | // Time.perf_end() 135 | // Debug.out("TRY: concat: " + (perf_end - perf_begin).string()) 136 | -------------------------------------------------------------------------------- /src/mutable-data/lists.pony: -------------------------------------------------------------------------------- 1 | use "collections" 2 | use "../function-types" 3 | 4 | primitive Lists 5 | fun unit[A](a: A): List[A] => List[A].push(consume a) 6 | 7 | fun map[A: Any #read, B](l: List[A], f: Fn1[A!, B^]): List[B] => 8 | try 9 | _map[A, B](l.head(), f, List[B]) 10 | else 11 | List[B] 12 | end 13 | 14 | fun _map[A: Any #read, B](ln: ListNode[A], f: Fn1[A!, B^], acc: List[B]): List[B] => 15 | try acc.push(f(ln())) end 16 | 17 | try 18 | _map[A, B](ln.next() as ListNode[A], f, acc) 19 | else 20 | acc 21 | end 22 | 23 | fun flat_map[A: Any #read, B](l: List[A], f: Fn1[A!,List[B]]): List[B] => 24 | try 25 | _flat_map[A,B](l.head(), f, List[List[B]]) 26 | else 27 | List[B] 28 | end 29 | 30 | fun _flat_map[A: Any #read, B](ln: ListNode[A], f: Fn1[A!,List[B]], acc: List[List[B]]): List[B] => 31 | try acc.push(f(ln())) end 32 | 33 | try 34 | _flat_map[A,B](ln.next() as ListNode[A], f, acc) 35 | else 36 | flatten[B](acc) 37 | end 38 | 39 | fun flatten[A](l: List[List[A]]): List[A] => 40 | let resList = List[A] 41 | for subList in l.values() do 42 | resList.append_list(subList) 43 | end 44 | resList 45 | 46 | fun filter[A: Any #read](l: List[A], f: Fn1[A!, Bool]): List[A] => 47 | try 48 | _filter[A](l.head(), f, List[A]) 49 | else 50 | List[A] 51 | end 52 | 53 | fun _filter[A: Any #read](ln: ListNode[A], f: Fn1[A!, Bool], acc: List[A]): List[A] => 54 | try 55 | let cur = ln() 56 | if (f(cur)) then acc.push(consume cur) end 57 | end 58 | 59 | try 60 | _filter[A](ln.next() as ListNode[A], f, acc) 61 | else 62 | acc 63 | end 64 | 65 | fun fold[A: Any #read,B: Any #read](l: List[A], f: Fn2[B!,A!,B^], acc: B): B => 66 | try 67 | _fold[A,B](l.head(), f, acc) 68 | else 69 | acc 70 | end 71 | 72 | fun _fold[A: Any #read,B: Any #read](ln: ListNode[A], f: Fn2[B!,A!,B^], acc: B!): B => 73 | let nextAcc: B! = try f(acc, ln()) else acc end 74 | 75 | try 76 | _fold[A,B](ln.next() as ListNode[A], f, nextAcc) 77 | else 78 | nextAcc 79 | end 80 | 81 | fun every[A: Any #read](l: List[A], f: Fn1[A!,Bool]): Bool => 82 | try 83 | return _every[A](l.head(), f) 84 | else 85 | return true 86 | end 87 | 88 | fun _every[A: Any #read](ln: ListNode[A], f: Fn1[A!,Bool]): Bool => 89 | try 90 | let a: A = ln() 91 | if (not(f(a))) then 92 | return false 93 | else 94 | return _every[A](ln.next() as ListNode[A], f) 95 | end 96 | else 97 | return true 98 | end 99 | 100 | fun exists[A: Any #read](l: List[A], f: Fn1[A!,Bool]): Bool => 101 | try 102 | return _exists[A](l.head(), f) 103 | else 104 | return false 105 | end 106 | 107 | fun _exists[A: Any #read](ln: ListNode[A], f: Fn1[A!,Bool]): Bool => 108 | try 109 | let a: A = ln() 110 | if (f(a)) then 111 | return true 112 | else 113 | return _exists[A](ln.next() as ListNode[A], f) 114 | end 115 | else 116 | return false 117 | end 118 | 119 | fun partition[A: Any #read](l: List[A], f: Fn1[A!,Bool]): (List[A], List[A]) ? => 120 | let l1: List[A] = List[A] 121 | let l2: List[A] = List[A] 122 | for item in l.values() do 123 | if (f(item)) then l1.push(item) else l2.push(item) end 124 | end 125 | (l1, l2) 126 | 127 | fun drop[A: Any #read](l: List[A], n: USize): List[A] => 128 | if (l.size() < (n + 1)) then return List[A] end 129 | 130 | try 131 | _drop[A](l.clone().head(), n) 132 | else 133 | List[A] 134 | end 135 | 136 | fun _drop[A: Any #read](ln: ListNode[A], n: USize): List[A] => 137 | var count = n 138 | var cur: ListNode[A] = ln 139 | while(count > 0) do 140 | try cur = cur.next() as ListNode[A] else return List[A] end 141 | count = count - 1 142 | end 143 | let res = List[A] 144 | try res.push(cur()) end 145 | while (cur.has_next()) do 146 | try 147 | cur = cur.next() as ListNode[A] 148 | res.push(cur()) 149 | end 150 | end 151 | res 152 | 153 | fun take[A: Any #read](l: List[A], n: USize): List[A] => 154 | if (l.size() <= n) then l end 155 | 156 | try 157 | _take[A](l.clone().head(), n) 158 | else 159 | List[A] 160 | end 161 | 162 | fun _take[A: Any #read](ln: ListNode[A], n: USize): List[A] => 163 | var count = n 164 | let res = List[A] 165 | var cur: ListNode[A] = ln 166 | while(count > 0) do 167 | try res.push(cur()) end 168 | try cur = cur.next() as ListNode[A] else return res end 169 | count = count - 1 170 | end 171 | res 172 | 173 | fun take_while[A: Any #read](l: List[A], f: Fn1[A!,Bool]): List[A] => 174 | try 175 | _take_while[A](l.clone().head(), f) 176 | else 177 | List[A] 178 | end 179 | 180 | fun _take_while[A: Any #read](ln: ListNode[A], f: Fn1[A!,Bool]): List[A] => 181 | let res = List[A] 182 | var cur: ListNode[A] = ln 183 | try 184 | let initial = cur() 185 | if f(initial) then res.push(initial) else return res end 186 | end 187 | while(cur.has_next()) do 188 | try cur = cur.next() as ListNode[A] else return res end 189 | try 190 | let value = cur() 191 | if f(value) then res.push(value) else return res end 192 | end 193 | end 194 | res -------------------------------------------------------------------------------- /src/mutable-data/mutable-data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtfmumm/pony-functional-data/182f4dc9f49c8946aedf48cacb2226b684627987/src/mutable-data/mutable-data -------------------------------------------------------------------------------- /src/mutable-data/test.pony: -------------------------------------------------------------------------------- 1 | use "ponytest" 2 | use "collections" 3 | 4 | actor Main is TestList 5 | new create(env: Env) => PonyTest(env, this) 6 | new make() => None 7 | 8 | fun tag tests(test: PonyTest) => 9 | test(_TestMap) 10 | test(_TestFlatMap) 11 | test(_TestFlatten) 12 | test(_TestFilter) 13 | test(_TestFold) 14 | test(_TestEvery) 15 | test(_TestExists) 16 | test(_TestPartition) 17 | test(_TestDrop) 18 | // test(_TestDropWhile) 19 | test(_TestTake) 20 | test(_TestTakeWhile) 21 | 22 | class iso _TestMap is UnitTest 23 | fun name(): String => "list-transforms/map()" 24 | 25 | fun apply(h: TestHelper): TestResult ? => 26 | let a = List[U32] 27 | a.push(0).push(1).push(2) 28 | 29 | let f = lambda(a: U32): U32 => consume a * 2 end 30 | let c = Lists.map[U32,U32](a, f) 31 | 32 | h.expect_eq[U32](c(0), 0) 33 | h.expect_eq[U32](c(1), 2) 34 | h.expect_eq[U32](c(2), 4) 35 | 36 | true 37 | 38 | class iso _TestFlatMap is UnitTest 39 | fun name(): String => "list-transforms/flat_map()" 40 | 41 | fun apply(h: TestHelper): TestResult ? => 42 | let a = List[U32] 43 | a.push(0).push(1).push(2) 44 | 45 | let f = lambda(a: U32): List[U32] => List[U32].push(consume a * 2) end 46 | let c = Lists.flat_map[U32,U32](a, f) 47 | 48 | h.expect_eq[U32](c(0), 0) 49 | h.expect_eq[U32](c(1), 2) 50 | h.expect_eq[U32](c(2), 4) 51 | 52 | true 53 | 54 | class iso _TestFlatten is UnitTest 55 | fun name(): String => "list-transforms/flatten()" 56 | 57 | fun apply(h: TestHelper): TestResult ? => 58 | let a = List[List[U32]] 59 | let l1 = List[U32].push(0).push(1) 60 | let l2 = List[U32].push(2).push(3) 61 | let l3 = List[U32].push(4) 62 | a.push(l1).push(l2).push(l3) 63 | 64 | let b: List[U32] = Lists.flatten[U32](a) 65 | 66 | h.expect_eq[U32](b(0), 0) 67 | h.expect_eq[U32](b(1), 1) 68 | h.expect_eq[U32](b(2), 2) 69 | h.expect_eq[U32](b(3), 3) 70 | h.expect_eq[U32](b(4), 4) 71 | 72 | let c = List[List[U32]] 73 | let d = Lists.flatten[U32](c) 74 | h.expect_eq[USize](d.size(), 0) 75 | 76 | true 77 | 78 | class iso _TestFilter is UnitTest 79 | fun name(): String => "list-transforms/filter()" 80 | 81 | fun apply(h: TestHelper): TestResult ? => 82 | let a = List[U32] 83 | a.push(0).push(1).push(2).push(3) 84 | 85 | let f = lambda(a: U32): Bool => consume a > 1 end 86 | let b = Lists.filter[U32](a, f) 87 | 88 | h.expect_eq[USize](b.size(), 2) 89 | h.expect_eq[U32](b(0), 2) 90 | h.expect_eq[U32](b(1), 3) 91 | 92 | true 93 | 94 | class iso _TestFold is UnitTest 95 | fun name(): String => "list-transforms/fold()" 96 | 97 | fun apply(h: TestHelper): TestResult ? => 98 | let a = List[U32] 99 | a.push(0).push(1).push(2).push(3) 100 | 101 | let f = lambda(acc: U32, x: U32): U32 => acc + x end 102 | let value = Lists.fold[U32,U32](a, f, 0) 103 | 104 | h.expect_eq[U32](value, 6) 105 | 106 | let g = lambda(acc: List[U32], x: U32): List[U32] => acc.push(x * 2) end 107 | let resList = Lists.fold[U32,List[U32]](a, g, List[U32]) 108 | 109 | try h.expect_eq[U32](resList(0), 0) else error end 110 | try h.expect_eq[U32](resList(1), 2) else error end 111 | try h.expect_eq[U32](resList(2), 4) else error end 112 | try h.expect_eq[U32](resList(3), 6) else error end 113 | 114 | true 115 | 116 | class iso _TestEvery is UnitTest 117 | fun name(): String => "list-transforms/every()" 118 | 119 | fun apply(h: TestHelper): TestResult => 120 | let a = List[U32] 121 | a.push(0).push(1).push(2).push(3) 122 | 123 | let f = lambda(x: U32): Bool => x < 4 end 124 | let g = lambda(x: U32): Bool => x < 3 end 125 | let z = lambda(x: U32): Bool => x < 0 end 126 | let lessThan4 = Lists.every[U32](a, f) 127 | let lessThan3 = Lists.every[U32](a, g) 128 | let lessThan0 = Lists.every[U32](a, z) 129 | 130 | h.expect_eq[Bool](lessThan4, true) 131 | h.expect_eq[Bool](lessThan3, false) 132 | h.expect_eq[Bool](lessThan0, false) 133 | 134 | let b = List[U32] 135 | let empty = Lists.every[U32](b, f) 136 | h.expect_eq[Bool](empty, true) 137 | 138 | true 139 | 140 | class iso _TestExists is UnitTest 141 | fun name(): String => "list-transforms/exists()" 142 | 143 | fun apply(h: TestHelper): TestResult => 144 | let a = List[U32] 145 | a.push(0).push(1).push(2).push(3) 146 | 147 | let f = lambda(x: U32): Bool => x > 2 end 148 | let g = lambda(x: U32): Bool => x >= 0 end 149 | let z = lambda(x: U32): Bool => x < 0 end 150 | let gt2 = Lists.exists[U32](a, f) 151 | let gte0 = Lists.exists[U32](a, g) 152 | let lt0 = Lists.exists[U32](a, z) 153 | 154 | h.expect_eq[Bool](gt2, true) 155 | h.expect_eq[Bool](gte0, true) 156 | h.expect_eq[Bool](lt0, false) 157 | 158 | let b = List[U32] 159 | let empty = Lists.exists[U32](b, f) 160 | h.expect_eq[Bool](empty, false) 161 | 162 | true 163 | 164 | class iso _TestPartition is UnitTest 165 | fun name(): String => "list-transforms/partition()" 166 | 167 | fun apply(h: TestHelper): TestResult ? => 168 | let a = List[U32] 169 | a.push(0).push(1).push(2).push(3) 170 | 171 | let isEven = lambda(x: U32): Bool => x % 2 == 0 end 172 | (let evens, let odds) = Lists.partition[U32](a, isEven) 173 | 174 | try h.expect_eq[U32](evens(0), 0) else error end 175 | try h.expect_eq[U32](evens(1), 2) else error end 176 | try h.expect_eq[U32](odds(0), 1) else error end 177 | try h.expect_eq[U32](odds(1), 3) else error end 178 | 179 | let b = List[U32] 180 | (let emptyEvens, let emptyOdds) = Lists.partition[U32](b, isEven) 181 | 182 | h.expect_eq[USize](emptyEvens.size(), 0) 183 | h.expect_eq[USize](emptyOdds.size(), 0) 184 | 185 | true 186 | 187 | class iso _TestDrop is UnitTest 188 | fun name(): String => "list-transforms/drop()" 189 | 190 | fun apply(h: TestHelper): TestResult ? => 191 | let a = List[U32] 192 | a.push(0).push(1).push(2).push(3).push(4) 193 | 194 | let b = Lists.drop[U32](a, 2) 195 | let c = Lists.drop[U32](a, 4) 196 | let d = Lists.drop[U32](a, 5) 197 | let e = Lists.drop[U32](a, 6) 198 | 199 | h.expect_eq[USize](b.size(), 3) 200 | try h.expect_eq[U32](b(0), 2) else error end 201 | try h.expect_eq[U32](b(2), 4) else error end 202 | h.expect_eq[USize](c.size(), 1) 203 | try h.expect_eq[U32](c(0), 4) else error end 204 | h.expect_eq[USize](d.size(), 0) 205 | h.expect_eq[USize](e.size(), 0) 206 | 207 | let empty = List[U32] 208 | let l = Lists.drop[U32](empty, 3) 209 | h.expect_eq[USize](l.size(), 0) 210 | 211 | true 212 | 213 | //class iso _TestDropWhile is UnitTest 214 | // fun name(): String => "list-transforms/drop_while()" 215 | // 216 | // fun apply(h: TestHelper): TestResult ? => 217 | // let a = List[U32] 218 | // a.push(0).push(1).push(2).push(3).push(4) 219 | // 220 | // let f = lambda(x: U32): Bool => x < 5 end 221 | // let g = lambda(x: U32): Bool => x < 4 end 222 | // let y = lambda(x: U32): Bool => x < 1 end 223 | // let z = lambda(x: U32): Bool => x < 0 end 224 | // let b = Lists.drop_while[U32](a, f) 225 | // let c = Lists.drop_while[U32](a, g) 226 | // let d = Lists.drop_while[U32](a, y) 227 | // let e = Lists.drop_while[U32](a, z) 228 | // 229 | // h.expect_eq[U64](b.size(), 0) 230 | // h.expect_eq[U64](c.size(), 1) 231 | // try h.expect_eq[U32](c(0), 0) else error end 232 | // h.expect_eq[U64](d.size(), 4) 233 | // try h.expect_eq[U32](d(0), 1) else error end 234 | // try h.expect_eq[U32](d(1), 2) else error end 235 | // try h.expect_eq[U32](d(2), 3) else error end 236 | // try h.expect_eq[U32](d(3), 4) else error end 237 | // h.expect_eq[U64](e.size(), 5) 238 | // try h.expect_eq[U32](e(0), 0) else error end 239 | // try h.expect_eq[U32](e(1), 1) else error end 240 | // try h.expect_eq[U32](e(2), 2) else error end 241 | // try h.expect_eq[U32](e(3), 3) else error end 242 | // try h.expect_eq[U32](e(4), 4) else error end 243 | // 244 | // let empty = List[U32] 245 | // let l = Lists.drop_while[U32](empty, g) 246 | // h.expect_eq[U64](l.size(), 0) 247 | // 248 | // true 249 | 250 | class iso _TestTake is UnitTest 251 | fun name(): String => "list-transforms/take()" 252 | 253 | fun apply(h: TestHelper): TestResult ? => 254 | let a = List[U32] 255 | a.push(0).push(1).push(2).push(3).push(4) 256 | 257 | let b = Lists.take[U32](a, 2) 258 | let c = Lists.take[U32](a, 4) 259 | let d = Lists.take[U32](a, 5) 260 | let e = Lists.take[U32](a, 6) 261 | let m = Lists.take[U32](a, 0) 262 | 263 | h.expect_eq[USize](b.size(), 2) 264 | try h.expect_eq[U32](b(0), 0) else error end 265 | try h.expect_eq[U32](b(1), 1) else error end 266 | h.expect_eq[USize](c.size(), 4) 267 | try h.expect_eq[U32](c(0), 0) else error end 268 | try h.expect_eq[U32](c(1), 1) else error end 269 | try h.expect_eq[U32](c(2), 2) else error end 270 | try h.expect_eq[U32](c(3), 3) else error end 271 | h.expect_eq[USize](d.size(), 5) 272 | try h.expect_eq[U32](d(0), 0) else error end 273 | try h.expect_eq[U32](d(1), 1) else error end 274 | try h.expect_eq[U32](d(2), 2) else error end 275 | try h.expect_eq[U32](d(3), 3) else error end 276 | try h.expect_eq[U32](d(4), 4) else error end 277 | h.expect_eq[USize](e.size(), 5) 278 | try h.expect_eq[U32](e(0), 0) else error end 279 | try h.expect_eq[U32](e(1), 1) else error end 280 | try h.expect_eq[U32](e(2), 2) else error end 281 | try h.expect_eq[U32](e(3), 3) else error end 282 | try h.expect_eq[U32](e(4), 4) else error end 283 | h.expect_eq[USize](m.size(), 0) 284 | 285 | let empty = List[U32] 286 | let l = Lists.take[U32](empty, 3) 287 | h.expect_eq[USize](l.size(), 0) 288 | 289 | true 290 | 291 | class iso _TestTakeWhile is UnitTest 292 | fun name(): String => "list-transforms/take_while()" 293 | 294 | fun apply(h: TestHelper): TestResult ? => 295 | let a = List[U32] 296 | a.push(0).push(1).push(2).push(3).push(4) 297 | 298 | let f = lambda(x: U32): Bool => x < 5 end 299 | let g = lambda(x: U32): Bool => x < 4 end 300 | let y = lambda(x: U32): Bool => x < 1 end 301 | let z = lambda(x: U32): Bool => x < 0 end 302 | let b = Lists.take_while[U32](a, f) 303 | let c = Lists.take_while[U32](a, g) 304 | let d = Lists.take_while[U32](a, y) 305 | let e = Lists.take_while[U32](a, z) 306 | 307 | h.expect_eq[USize](b.size(), 5) 308 | try h.expect_eq[U32](b(0), 0) else error end 309 | try h.expect_eq[U32](b(1), 1) else error end 310 | try h.expect_eq[U32](b(2), 2) else error end 311 | try h.expect_eq[U32](b(3), 3) else error end 312 | try h.expect_eq[U32](b(4), 4) else error end 313 | h.expect_eq[USize](c.size(), 4) 314 | try h.expect_eq[U32](c(0), 0) else error end 315 | try h.expect_eq[U32](c(1), 1) else error end 316 | try h.expect_eq[U32](c(2), 2) else error end 317 | try h.expect_eq[U32](c(3), 3) else error end 318 | h.expect_eq[USize](d.size(), 1) 319 | try h.expect_eq[U32](d(0), 0) else error end 320 | h.expect_eq[USize](e.size(), 0) 321 | 322 | let empty = List[U32] 323 | let l = Lists.take_while[U32](empty, g) 324 | h.expect_eq[USize](l.size(), 0) 325 | 326 | true -------------------------------------------------------------------------------- /src/persistent-data/Option.pony: -------------------------------------------------------------------------------- 1 | use "../function-types" 2 | 3 | trait val Option[V: Any val] 4 | fun is_empty(): Bool 5 | fun is_non_empty(): Bool => not(is_empty()) 6 | fun value(): V ? 7 | fun map[B: Any val](f: Fn1[V!,B^]): Option[B] ? 8 | fun flat_map[B: Any val](f: Fn1[V!,Option[B]]): Option[B] ? 9 | fun val filter(f: Fn1[V!,Bool]): Option[V] ? 10 | 11 | class val ONone[V: Any val] is Option[V] 12 | fun is_empty(): Bool => true 13 | 14 | fun value(): V ? => error 15 | 16 | fun map[B: Any val](f: Fn1[V!,B^]): Option[B] => ONone[B] 17 | 18 | fun flat_map[B: Any val](f: Fn1[V!,Option[B]]): Option[B] => ONone[B] 19 | 20 | fun val filter(f: Fn1[V!,Bool]): Option[V] => ONone[V] 21 | 22 | class val OSome[V: Any val] is Option[V] 23 | let _value: V 24 | 25 | new val create(v: V) => 26 | _value = consume v 27 | 28 | fun is_empty(): Bool => false 29 | 30 | fun value(): V => _value 31 | 32 | fun map[B: Any val](f: Fn1[V,B^]): Option[B] ? => OSome[B](f(_value)) 33 | 34 | fun flat_map[B: Any val](f: Fn1[V,Option[B]]): Option[B] ? => f(_value) 35 | 36 | fun val filter(f: Fn1[V!,Bool]): Option[V] ? => 37 | if (f(_value)) then recover val OSome[V](_value) end else recover val ONone[V] end end 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/persistent-data/benchmarks/bench.pony: -------------------------------------------------------------------------------- 1 | use "../" 2 | use mut = "collections" 3 | use "time" 4 | use "debug" 5 | use "random" 6 | 7 | 8 | primitive Bench 9 | fun bench(iterations: USize, keys: U64) ? => 10 | var pMap: Map[String,U64] = Maps.empty[String,U64]() 11 | let mMap: mut.Map[String,U64] = mut.Map[String,U64]() 12 | let kvs = Array[(String,U64)]() 13 | // Different seed each run 14 | let dice = Dice(MT(Time.millis())) 15 | var count: USize = 0 16 | var perf_begin: U64 = 0 17 | var perf_end: U64 = 0 18 | 19 | while(count < iterations) do 20 | let k0 = dice(1,keys).string() 21 | let k: String val = consume k0 22 | let v = dice(1,100000) 23 | kvs.push((k, v)) 24 | count = count + 1 25 | end 26 | 27 | //WRITES 28 | count = 0 29 | Time.perf_begin() 30 | perf_begin = Time.millis() 31 | while(count < iterations) do 32 | let k = kvs(count)._1 33 | let v = kvs(count)._2 34 | pMap = pMap.update(k, v) 35 | count = count + 1 36 | end 37 | perf_end = Time.millis() 38 | Time.perf_end() 39 | Debug.out("Persistent writes: " + (perf_end - perf_begin).string()) 40 | 41 | count = 0 42 | Time.perf_begin() 43 | perf_begin = Time.millis() 44 | while(count < iterations) do 45 | let k = kvs(count)._1 46 | let v = kvs(count)._2 47 | mMap.update(k, v) 48 | count = count + 1 49 | end 50 | perf_end = Time.millis() 51 | Time.perf_end() 52 | Debug.out("Mutable writes: " + (perf_end - perf_begin).string()) 53 | 54 | 55 | //READS 56 | count = 0 57 | Time.perf_begin() 58 | perf_begin = Time.millis() 59 | while(count < iterations) do 60 | let pmv = pMap(kvs(count)._1) 61 | count = count + 1 62 | end 63 | perf_end = Time.millis() 64 | Time.perf_end() 65 | Debug.out("Persistent reads: " + (perf_end - perf_begin).string()) 66 | 67 | count = 0 68 | Time.perf_begin() 69 | perf_begin = Time.millis() 70 | while(count < iterations) do 71 | let mmv = mMap(kvs(count)._1) 72 | count = count + 1 73 | end 74 | perf_end = Time.millis() 75 | Time.perf_end() 76 | Debug.out("Mutable reads: " + (perf_end - perf_begin).string()) 77 | 78 | //fun list_try_bench(iterations: USize) => 79 | // // Different seed each run 80 | // let dice = Dice(MT(Time.millis())) 81 | // var count: USize = 0 82 | // var long_lst: List[U64] = Lists.empty[U64]() 83 | // var short_lst: List[U64] = Lists.empty[U64]().prepend(1).prepend(2).prepend(3).prepend(4) 84 | // var perf_begin: U64 = 0 85 | // var perf_end: U64 = 0 86 | // 87 | // while(count < iterations) do 88 | // let v: U64 = dice(1,100000) 89 | // long_lst = long_lst.prepend(v) 90 | // count = count + 1 91 | // end 92 | // 93 | // //MAPS 94 | // count = 0 95 | // Time.perf_begin() 96 | // perf_begin = Time.millis() 97 | // while(count < iterations) do 98 | // let new_m = long_lst.map[U64](lambda(x: U64): U64 => x * 2 end) 99 | // count = count + 1 100 | // end 101 | // perf_end = Time.millis() 102 | // Time.perf_end() 103 | // Debug.out("Match map (LONG): " + (perf_end - perf_begin).string()) 104 | // 105 | // count = 0 106 | // Time.perf_begin() 107 | // perf_begin = Time.millis() 108 | // while(count < iterations) do 109 | // let new_m = long_lst.map_try[U64](lambda(x: U64): U64 => x * 2 end) 110 | // count = count + 1 111 | // end 112 | // perf_end = Time.millis() 113 | // Time.perf_end() 114 | // Debug.out("Try map (LONG): " + (perf_end - perf_begin).string()) 115 | // 116 | // count = 0 117 | // Time.perf_begin() 118 | // perf_begin = Time.millis() 119 | // while(count < iterations) do 120 | // let new_match_m = short_lst.map[U64](lambda(x: U64): U64 => x * 2 end) 121 | // count = count + 1 122 | // end 123 | // perf_end = Time.millis() 124 | // Time.perf_end() 125 | // Debug.out("Match map (SHORT): " + (perf_end - perf_begin).string()) 126 | // 127 | // count = 0 128 | // Time.perf_begin() 129 | // perf_begin = Time.millis() 130 | // while(count < iterations) do 131 | // let new_match_m = short_lst.map_try[U64](lambda(x: U64): U64 => x * 2 end) 132 | // count = count + 1 133 | // end 134 | // perf_end = Time.millis() 135 | // Time.perf_end() 136 | // Debug.out("Try map (SHORT): " + (perf_end - perf_begin).string()) 137 | 138 | // fun list_bench(iterations: U64, keys: U64) => 139 | // var list: List[U64] = Lists.empty[U64]() 140 | // // Different seed each run 141 | // let dice = Dice(MT) 142 | // var count: U64 = 0 143 | // var perf_begin: U64 = 0 144 | // var perf_end: U64 = 0 145 | // 146 | // var l1: List[U64] = Lists.empty[U64]() 147 | // var l2: List[U64] = Lists.empty[U64]() 148 | // var l3: List[U64] = Lists.empty[U64]() 149 | // var l4: List[U64] = Lists.empty[U64]() 150 | // 151 | // while(count < iterations) do 152 | // let v: U64 = dice(1,100000) 153 | // l1 = l1.prepend(v) 154 | // l2 = l2.prepend(v) 155 | // l3 = l3.prepend(v) 156 | // l4 = l4.prepend(v) 157 | // count = count + 1 158 | // end 159 | // 160 | // //WRITES 161 | // Time.perf_begin() 162 | // perf_begin = Time.millis() 163 | //// l1.reverse() 164 | //// l2.reverse() 165 | //// l3.reverse() 166 | //// l4.reverse() 167 | //// l1.reverse() 168 | //// l2.reverse() 169 | //// l3.reverse() 170 | //// l4.reverse() 171 | //// l1.reverse() 172 | //// l2.reverse() 173 | //// l3.reverse() 174 | //// l4.reverse() 175 | // perf_end = Time.millis() 176 | // Time.perf_end() 177 | // Debug.out("TRY: reverse: " + (perf_end - perf_begin).string()) 178 | 179 | // Time.perf_begin() 180 | // perf_begin = Time.millis() 181 | //// l1.concat(l2) 182 | //// l2.concat(l3) 183 | //// l3.concat(l4) 184 | //// l4.concat(l1) 185 | //// l1.concat(l2) 186 | //// l2.concat(l3) 187 | //// l3.concat(l4) 188 | //// l4.concat(l1) 189 | //// l1.concat(l2) 190 | //// l2.concat(l3) 191 | //// l3.concat(l4) 192 | //// l4.concat(l1) 193 | // perf_end = Time.millis() 194 | // Time.perf_end() 195 | // Debug.out("TRY: concat: " + (perf_end - perf_begin).string()) 196 | -------------------------------------------------------------------------------- /src/persistent-data/list.pony: -------------------------------------------------------------------------------- 1 | type List[A] is (Cons[A] | Nil[A]) 2 | """ 3 | A persistent list with functional transformations. 4 | 5 | ## Usage 6 | 7 | ``` 8 | let l1 = Lists[U32]([2, 4, 6, 8]) // List(2, 4, 6, 8) 9 | 10 | let empty = Lists[U32].empty() // List() 11 | 12 | // prepend() returns a new List, leaving the 13 | // old list unchanged 14 | let l2 = empty.prepend(3) // List(3) 15 | let l3 = l2.prepend(2) // List(2, 3) 16 | let l4 = l3.prepend(1) // List(1, 2, 3) 17 | let l4_head = l4.head() // 1 18 | let l4_tail = l4.tail() // List(2, 3) 19 | 20 | h.assert_eq[U32](l4_head, 1) 21 | h.assert_true(Lists[U32].eq(l4, Lists[U32]([1, 2, 3]))) 22 | h.assert_true(Lists[U32].eq(l4_tail, Lists[U32]([2, 3]))) 23 | 24 | let doubled = l4.map[U32](lambda(x: U32): U32 => x * 2 end) 25 | 26 | h.assert_true(Lists[U32].eq(doubled, Lists[U32]([2, 4, 6]))) 27 | 28 | ``` 29 | """ 30 | 31 | primitive Lists[A] 32 | """ 33 | A primitive containing helper functions for constructing and 34 | testing Lists. 35 | """ 36 | 37 | fun empty(): List[A] => 38 | """ 39 | Returns an empty list. 40 | """ 41 | Nil[A] 42 | 43 | fun cons(h: val->A, t: List[A]): List[A] => 44 | """ 45 | Returns a list that has h as a head and t as a tail. 46 | """ 47 | Cons[A](h, t) 48 | 49 | fun apply(arr: Array[val->A]): List[A] => 50 | """ 51 | Builds a new list from an Array 52 | """ 53 | var lst = this.empty() 54 | for v in arr.values() do 55 | lst = lst.prepend(v) 56 | end 57 | lst.reverse() 58 | 59 | fun from(iter: Iterator[val->A]): List[A] => 60 | """ 61 | Builds a new list from an iterator 62 | """ 63 | var l: List[A] = Nil[A] 64 | 65 | for i in iter do 66 | l = Cons[A](i, l) 67 | end 68 | l 69 | 70 | fun eq[T: Equatable[T] val = A](l1: List[T], l2: List[T]): Bool ? => 71 | """ 72 | Checks whether two lists are equal. 73 | """ 74 | if (l1.is_empty() and l2.is_empty()) then 75 | true 76 | elseif (l1.is_empty() and l2.is_non_empty()) then 77 | false 78 | elseif (l1.is_non_empty() and l2.is_empty()) then 79 | false 80 | elseif (l1.head() != l2.head()) then 81 | false 82 | else 83 | eq[T](l1.tail(), l2.tail()) 84 | end 85 | 86 | primitive Nil[A] 87 | """ 88 | The empty list of As. 89 | """ 90 | 91 | fun size(): U64 => 92 | """ 93 | Returns the size of the list. 94 | """ 95 | 0 96 | 97 | fun is_empty(): Bool => 98 | """ 99 | Returns a Bool indicating if the list is empty. 100 | """ 101 | true 102 | 103 | fun is_non_empty(): Bool => 104 | """ 105 | Returns a Bool indicating if the list is non-empty. 106 | """ 107 | false 108 | 109 | fun head(): val->A ? => 110 | """ 111 | Returns an error, since Nil has no head. 112 | """ 113 | error 114 | 115 | fun tail(): List[A] ? => 116 | """ 117 | Returns an error, since Nil has no tail. 118 | """ 119 | error 120 | 121 | fun reverse(): Nil[A] => 122 | """ 123 | The reverse of the empty list is the empty list. 124 | """ 125 | this 126 | 127 | fun prepend(a: val->A!): Cons[A] => 128 | """ 129 | Builds a new list with an element added to the front of this list. 130 | """ 131 | Cons[A](consume a, this) 132 | 133 | fun concat(l: List[A]): List[A] => 134 | """ 135 | The concatenation of any list l with the empty list is l. 136 | """ 137 | l 138 | 139 | fun add(l: List[A]): List[A] => l 140 | 141 | fun map[B](f: {(val->A): val->B} box): Nil[B] => 142 | """ 143 | Mapping a function from A to B over the empty list yields the 144 | empty list of Bs. 145 | """ 146 | Nil[B] 147 | 148 | fun flat_map[B](f: {(val->A): List[B]} box): Nil[B] => 149 | """ 150 | Flatmapping a function from A to B over the empty list yields the 151 | empty list of Bs. 152 | """ 153 | Nil[B] 154 | 155 | fun for_each(f: {(val->A)} box) => 156 | """ 157 | Applying a function to every member of the empty list is a no-op. 158 | """ 159 | None 160 | 161 | fun filter(f: {(val->A): Bool} box): Nil[A] => 162 | """ 163 | Filtering the empty list yields the empty list. 164 | """ 165 | this 166 | 167 | fun fold[B](f: {(B, val->A): B^} box, acc: B): B => 168 | """ 169 | Folding over the empty list yields the initial accumulator. 170 | """ 171 | consume acc 172 | 173 | fun every(f: {(val->A): Bool} box): Bool => 174 | """ 175 | Any predicate is true of every member of the empty list. 176 | """ 177 | true 178 | 179 | fun exists(f: {(val->A): Bool} box): Bool => 180 | """ 181 | For any predicate, there is no element that satisfies it in the empty list. 182 | """ 183 | false 184 | 185 | fun partition(f: {(val->A): Bool} box): (Nil[A], Nil[A]) => 186 | """ 187 | The only partition of the empty list is two empty lists. 188 | """ 189 | (this, this) 190 | 191 | fun drop(n: U64): Nil[A] => 192 | """ 193 | There are no elements to drop from the empty list. 194 | """ 195 | this 196 | 197 | fun drop_while(f: {(val->A): Bool} box): Nil[A] => 198 | """ 199 | There are no elements to drop from the empty list. 200 | """ 201 | this 202 | 203 | fun take(n: U64): Nil[A] => 204 | """ 205 | There are no elements to take from the empty list. 206 | """ 207 | this 208 | 209 | fun take_while(f: {(val->A): Bool} box): Nil[A] => 210 | """ 211 | There are no elements to take from the empty list. 212 | """ 213 | this 214 | 215 | fun val contains[T: (A & HasEq[A!] #read) = A](a: val->T): Bool => 216 | false 217 | // 218 | // fun string[T: Stringable #read = A](): String => 219 | // "List()" 220 | // 221 | // fun _string[T: Stringable #read = A](): String => 222 | // "" 223 | 224 | // 225 | // fun eq[T: Equatable[T] val = A](other: List[T]): Bool => 226 | // """ 227 | // Checks whether two lists are equal. 228 | // """ 229 | // match other 230 | // | let n: Nil[T] => true 231 | // else 232 | // false 233 | // end 234 | 235 | class val Cons[A] 236 | """ 237 | A list with a head and a tail, where the tail can be empty. 238 | """ 239 | 240 | let _size: U64 241 | let _head: val->A 242 | let _tail: List[A] val 243 | 244 | new val create(a: val->A, t: List[A]) => 245 | _head = consume a 246 | _tail = consume t 247 | _size = 1 + _tail.size() 248 | 249 | fun size(): U64 => 250 | """ 251 | Returns the size of the list. 252 | """ 253 | _size 254 | 255 | fun is_empty(): Bool => 256 | """ 257 | Returns a Bool indicating if the list is empty. 258 | """ 259 | false 260 | 261 | fun is_non_empty(): Bool => 262 | """ 263 | Returns a Bool indicating if the list is non-empty. 264 | """ 265 | true 266 | 267 | fun head(): val->A => 268 | """ 269 | Returns the head of the list. 270 | """ 271 | _head 272 | 273 | fun tail(): List[A] => 274 | """ 275 | Returns the tail of the list. 276 | """ 277 | _tail 278 | 279 | fun val reverse(): List[A] => 280 | """ 281 | Builds a new list by reversing the elements in the list. 282 | """ 283 | _reverse(this, Nil[A]) 284 | 285 | fun val _reverse(l: List[A], acc: List[A]): List[A] => 286 | """ 287 | Private helper for reverse, recursively working on elements. 288 | """ 289 | match l 290 | | let cons: Cons[A] => _reverse(cons.tail(), acc.prepend(cons.head())) 291 | else 292 | acc 293 | end 294 | 295 | fun val prepend(a: val->A!): Cons[A] => 296 | """ 297 | Builds a new list with an element added to the front of this list. 298 | """ 299 | Cons[A](consume a, this) 300 | 301 | fun val concat(l: List[A]): List[A] => 302 | """ 303 | Builds a new list that is the concatenation of this list and the provided 304 | list. 305 | """ 306 | _concat(l, this.reverse()) 307 | 308 | fun val add(l: List[A]): List[A] => concat(l) 309 | 310 | fun val _concat(l: List[A], acc: List[A]): List[A] => 311 | """ 312 | Private helper for concat that recursively builds the new list. 313 | """ 314 | match l 315 | | let cons: Cons[A] => _concat(cons.tail(), acc.prepend(cons.head())) 316 | else 317 | acc.reverse() 318 | end 319 | 320 | fun val map[B](f: {(val->A): val->B} box): List[B] => 321 | """ 322 | Builds a new list by applying a function to every member of the list. 323 | """ 324 | _map[B](this, f, Nil[B]) 325 | 326 | fun _map[B](l: List[A], f: {(val->A): val->B} box, acc: List[B]): List[B] => 327 | """ 328 | Private helper for map, recursively applying function to elements. 329 | """ 330 | match l 331 | | let cons: Cons[A] => _map[B](cons.tail(), f, acc.prepend(f(cons.head()))) 332 | else 333 | acc.reverse() 334 | end 335 | 336 | fun val flat_map[B](f: {(val->A): List[B]} box): List[B] => 337 | """ 338 | Builds a new list by applying a function to every member of the list and 339 | using the elements of the resulting lists. 340 | """ 341 | _flat_map[B](this, f, Nil[B]) 342 | 343 | fun _flat_map[B](l: List[A], f: {(val->A): List[B]} box, acc: List[B]): List[B] => 344 | """ 345 | Private helper for flat_map, recursively working on elements. 346 | """ 347 | match l 348 | | let cons: Cons[A] => _flat_map[B](cons.tail(), f, _rev_prepend[B](f(cons.head()), acc)) 349 | else 350 | acc.reverse() 351 | end 352 | 353 | fun tag _rev_prepend[B](l: List[B], target: List[B]): List[B] => 354 | """ 355 | Prepends l in reverse order onto target 356 | """ 357 | match l 358 | | let cns: Cons[B] => 359 | _rev_prepend[B](cns.tail(), target.prepend(cns.head())) 360 | else 361 | target 362 | end 363 | 364 | // fun val flatten[B](): List[B] ? => 365 | // """ 366 | // Builds a new list out of the elements of the lists in this one. 367 | // """ 368 | // match (_head, _tail) 369 | // | (let h: List[B], let t: List[List[B]]) => _flatten[B](Cons[List[B]](h, t), Nil[B]) 370 | // else 371 | // error 372 | // end 373 | // 374 | // fun val _flatten[B](l: List[List[B]], acc: List[B]): List[B] => 375 | // match l 376 | // | let cns: Cons[List[B]] => 377 | // _flatten(cns.tail(), _rev_prepend[B](cns.head(), acc)) 378 | // else 379 | // acc.reverse() 380 | // end 381 | 382 | fun val for_each(f: {(val->A)} box) => 383 | """ 384 | Applies the supplied function to every element of the list in order. 385 | """ 386 | _for_each(this, f) 387 | 388 | fun _for_each(l: List[A], f: {(val->A)} box) => 389 | """ 390 | Private helper for for_each, recursively working on elements. 391 | """ 392 | match l 393 | | let cons: Cons[A] => 394 | f(cons.head()) 395 | _for_each(cons.tail(), f) 396 | end 397 | 398 | fun val filter(f: {(val->A): Bool} box): List[A] => 399 | """ 400 | Builds a new list with those elements that satisfy a provided predicate. 401 | """ 402 | _filter(this, f, Nil[A]) 403 | 404 | fun _filter(l: List[A], f: {(val->A): Bool} box, acc: List[A]): List[A] => 405 | """ 406 | Private helper for filter, recursively working on elements, keeping those 407 | that match the predicate and discarding those that don't. 408 | """ 409 | match l 410 | | let cons: Cons[A] => 411 | if (f(cons.head())) then 412 | _filter(cons.tail(), f, acc.prepend(cons.head())) 413 | else 414 | _filter(cons.tail(), f, acc) 415 | end 416 | else 417 | acc.reverse() 418 | end 419 | 420 | fun val fold[B](f: {(B, val->A): B^} box, acc: B): B => 421 | """ 422 | Folds the elements of the list using the supplied function. 423 | """ 424 | _fold[B](this, f, consume acc) 425 | 426 | fun val _fold[B](l: List[A], f: {(B, val->A): B^} box, acc: B): B => 427 | """ 428 | Private helper for fold, recursively working on elements. 429 | """ 430 | match l 431 | | let cons: Cons[A] => 432 | _fold[B](cons.tail(), f, f(consume acc, cons.head())) 433 | else 434 | acc 435 | end 436 | 437 | fun val every(f: {(val->A): Bool} box): Bool => 438 | """ 439 | Returns true if every element satisfies the provided predicate, false 440 | otherwise. 441 | """ 442 | _every(this, f) 443 | 444 | fun _every(l: List[A], f: {(val->A): Bool} box): Bool => 445 | """ 446 | Private helper for every, recursively testing predicate on elements, 447 | returning false immediately on an element that fails to satisfy the 448 | predicate. 449 | """ 450 | match l 451 | | let cons: Cons[A] => 452 | if (f(cons.head())) then 453 | _every(cons.tail(), f) 454 | else 455 | false 456 | end 457 | else 458 | true 459 | end 460 | 461 | fun val exists(f: {(val->A): Bool} box): Bool => 462 | """ 463 | Returns true if at least one element satisfies the provided predicate, 464 | false otherwise. 465 | """ 466 | _exists(this, f) 467 | 468 | fun _exists(l: List[A], f: {(val->A): Bool} box): Bool => 469 | """ 470 | Private helper for exists, recursively testing predicate on elements, 471 | returning true immediately on an element satisfying the predicate. 472 | """ 473 | match l 474 | | let cons: Cons[A] => 475 | if (f(cons.head())) then 476 | true 477 | else 478 | _exists(cons.tail(), f) 479 | end 480 | else 481 | false 482 | end 483 | 484 | fun val partition(f: {(val->A): Bool} box): (List[A], List[A]) => 485 | """ 486 | Builds a pair of lists, the first of which is made up of the elements 487 | satisfying the supplied predicate and the second of which is made up of 488 | those that do not. 489 | """ 490 | var hits: List[A] = Nil[A] 491 | var misses: List[A] = Nil[A] 492 | var cur: List[A] = this 493 | while(true) do 494 | match cur 495 | | let cons: Cons[A] => 496 | let next = cons.head() 497 | if (f(next)) then hits = hits.prepend(next) else misses = misses.prepend(next) end 498 | cur = cons.tail() 499 | else 500 | break 501 | end 502 | end 503 | (hits.reverse(), misses.reverse()) 504 | 505 | fun val drop(n: U64): List[A] => 506 | """ 507 | Builds a list by dropping the first n elements. 508 | """ 509 | var cur: List[A] = this 510 | if cur.size() <= n then return Nil[A] end 511 | var count = n 512 | while(count > 0) do 513 | match cur 514 | | let cons: Cons[A] => 515 | cur = cons.tail() 516 | count = count - 1 517 | end 518 | end 519 | cur 520 | 521 | fun val drop_while(f: {(val->A): Bool} box): List[A] => 522 | """ 523 | Builds a list by dropping elements from the front of the list until one 524 | fails to satisfy the provided predicate. 525 | """ 526 | var cur: List[A] = this 527 | while(true) do 528 | match cur 529 | | let cons: Cons[A] => 530 | if f(cons.head()) then cur = cons.tail() else break end 531 | else 532 | return Nil[A] 533 | end 534 | end 535 | cur 536 | 537 | fun val take(n: U64): List[A] => 538 | """ 539 | Builds a list of the first n elements. 540 | """ 541 | var cur: List[A] = this 542 | if cur.size() <= n then return cur end 543 | var count = n 544 | var res: List[A] = Nil[A] 545 | while(count > 0) do 546 | match cur 547 | | let cons: Cons[A] => 548 | res = res.prepend(cons.head()) 549 | cur = cons.tail() 550 | else 551 | return res.reverse() 552 | end 553 | count = count - 1 554 | end 555 | res.reverse() 556 | 557 | fun val take_while(f: {(val->A): Bool} box): List[A] => 558 | """ 559 | Builds a list of elements satisfying the provided predicate until one does 560 | not. 561 | """ 562 | var cur: List[A] = this 563 | var res: List[A] = Nil[A] 564 | while(true) do 565 | match cur 566 | | let cons: Cons[A] => 567 | if f(cons.head()) then 568 | res = res.prepend(cons.head()) 569 | cur = cons.tail() 570 | else 571 | break 572 | end 573 | else 574 | return res.reverse() 575 | end 576 | end 577 | res.reverse() 578 | 579 | // fun string[T: Stringable #read = A](): String => 580 | // try 581 | // let h = (_head as T).string() 582 | // "List(" + h + (_tail as List[T])._string() + ")" 583 | // else 584 | // "List()" 585 | // end 586 | // 587 | // fun _string[T: Stringable #read = A](): String => 588 | // try 589 | // let h = (_head as T).string() 590 | // ", " + h + _tail._string() 591 | // else 592 | // "" 593 | // end 594 | 595 | // fun eq[T: Equatable[T] val = A](other: List[T]): Bool => 596 | // """ 597 | // Checks whether two lists are equal. 598 | // """ 599 | // match other 600 | // | let cons: Cons[T] => 601 | // _head as T == other.head() and _tail as List[T] == other.tail() 602 | // else 603 | // false 604 | // end 605 | 606 | // fun val contains[T: (A & HasEq[A!] #read) = A](a: val->T): Bool => 607 | // try 608 | // _contains[T](this as List[T], a) 609 | // else 610 | // false 611 | // end 612 | // 613 | // fun val _contains[T: (A & HasEq[A] #read) = A](l: List[T], a: val->T): Bool => 614 | // match l 615 | // | let cons: Cons[T] => 616 | // if l.head() == a then 617 | // true 618 | // else 619 | // _contains(l.tail(), a) 620 | // end 621 | // else 622 | // false 623 | // end 624 | -------------------------------------------------------------------------------- /src/persistent-data/map.pony: -------------------------------------------------------------------------------- 1 | use mut = "collections" 2 | 3 | interface Hashable 4 | """ 5 | Anything with a hash method is hashable. 6 | """ 7 | fun hash(): U64 8 | 9 | type Map[K: (Hashable val & Equatable[K] val), V] is (LeafNode[K, V] | MultiLeafNode[K, V] | MapNode[K, V]) 10 | """ 11 | A persistent map based on Bagwell's hash array mapped trie algorithm. 12 | 13 | ## Usage 14 | 15 | ``` 16 | let empty: Map[String,U32] = Maps.empty[String,U32]() // {} 17 | // Update returns a new map with the provided key set 18 | // to the provided value. The old map is unchanged. 19 | let m2 = m1.update("a", 5) // {a: 5} 20 | let m3 = m2.update("b", 10) // {a: 5, b: 10} 21 | let m4 = m3.remove("a") // {b: 10} 22 | 23 | // You can create a new map from key value pairs. 24 | let map = Maps.from[String,U32]([("a", 2), ("b", 3)]) // {a: 2, b: 3} 25 | ``` 26 | """ 27 | 28 | primitive Maps 29 | fun val empty[K: (Hashable val & Equatable[K] val), V](): Map[K, V] => 30 | """ 31 | Return an empty map. 32 | """ 33 | MapNode[K, V].empty() 34 | 35 | fun val from[K: (Hashable val & Equatable[K] val), V](pairs: Array[(K, val->V)]): Map[K, V] ? => 36 | """ 37 | Return a map containing the provided key-value pairs. 38 | """ 39 | var newMap = empty[K, V]() 40 | for pair in pairs.values() do 41 | (let k, let v) = pair 42 | newMap = newMap.update(k, v) 43 | end 44 | newMap 45 | 46 | fun _last_level(): U32 => 47 | """ 48 | The maximum depth of the tree used for the map (counting from zero) 49 | """ 50 | 4 51 | 52 | class val LeafNode[K: (Hashable val & Equatable[K] val), V] 53 | let _key: K 54 | let _value: val->V 55 | 56 | new val create(k: K, v: val->V) => 57 | _key = k 58 | _value = v 59 | 60 | fun size(): U64 => 61 | """ 62 | Return the size of the node. 63 | """ 64 | 1 65 | 66 | fun _is_leaf(): Bool => 67 | """ 68 | Predicate testing if the node is a leaf. 69 | """ 70 | true 71 | 72 | fun apply(k: K): val->V ? => 73 | """ 74 | Attempt to get the value corresponding to k. 75 | """ 76 | if k == _key then _value else error end 77 | 78 | fun _getWithHash(k: K, hash: U32, level: U32): val->V ? => 79 | """ 80 | Attempt to get the value corresponding to k, using hash and level. 81 | """ 82 | apply(k) 83 | 84 | fun update(k: K, v: val->V): Map[K, V] ? => 85 | """ 86 | Update the value associated with the provided key. 87 | """ 88 | if k == _key then 89 | LeafNode[K, V](k, v) as Map[K, V] 90 | else 91 | let mapNode = MapNode[K, V].empty().update(_key, _value) 92 | mapNode.update(k, v) 93 | end 94 | 95 | fun _putWithHash(k: K, v: val->V, hash: U32, level: U32): Map[K, V] ? => 96 | """ 97 | Update the value associated with the provided key, using hash and level. 98 | """ 99 | if k == _key then 100 | LeafNode[K, V](k, v) as Map[K, V] 101 | else 102 | let mapNode = MapNode[K, V].empty()._putWithHash(_key, _value, MapHelpers._hash[K](_key), level) 103 | mapNode._putWithHash(k, v, hash, level) 104 | end 105 | 106 | fun remove(k: K): Map[K, V] ? => 107 | """ 108 | Try to remove the provided key from the node. 109 | """ 110 | error 111 | 112 | fun _removeWithHash(k: K, hash: U32, level: U32): Map[K, V] ? => 113 | """ 114 | Try to remove the prodied key form the node, using hash and level. 115 | """ 116 | error 117 | 118 | fun getOrElse(k: K, alt: val->V): val->V => 119 | """ 120 | Get the value associated with provided key if present. Otherwise, 121 | return the provided alternate value. 122 | """ 123 | try 124 | apply(k) 125 | else 126 | alt 127 | end 128 | 129 | fun contains(k: K): Bool => 130 | """ 131 | Check whether the node contains the provided key. 132 | """ 133 | try 134 | apply(k) 135 | true 136 | else 137 | false 138 | end 139 | 140 | class val Entry[K: (Hashable val & Equatable[K] val), V] 141 | let key: K 142 | let value: val->V 143 | 144 | new val create(k: K, v: val->V) => 145 | key = k 146 | value = v 147 | 148 | class val MultiLeafNode[K: (Hashable val & Equatable[K] val), V] 149 | let _entries: List[Entry[K, V]] 150 | 151 | new val create() => 152 | _entries = Lists[Entry[K, V]].empty() 153 | 154 | new val from(es: List[Entry[K, V]]) => 155 | """ 156 | Create a new MultiLeafNode from the provided list of entries. 157 | """ 158 | _entries = es 159 | 160 | fun size(): U64 => 161 | """ 162 | Return the size of the node. 163 | """ 164 | _entries.size() 165 | 166 | fun _is_leaf(): Bool => 167 | """ 168 | Predicate testing if the node is a leaf. 169 | """ 170 | true 171 | 172 | fun apply(k: K): val->V ? => 173 | """ 174 | Attempt to get the value corresponding to k. 175 | """ 176 | try 177 | var cur = _entries 178 | while(cur.is_non_empty()) do 179 | let next = cur.head() 180 | if (next.key == k) then return next.value end 181 | cur = cur.tail() 182 | end 183 | error 184 | else 185 | error 186 | end 187 | 188 | fun _getWithHash(k: K, hash: U32, level: U32): val->V ? => 189 | """ 190 | Attempt to get the value corresponding to k, using hash and level. 191 | """ 192 | apply(k) 193 | 194 | fun update(k: K, v: val->V): Map[K, V] => 195 | """ 196 | Update the value associated with the provided key. 197 | """ 198 | let test = 199 | object 200 | let key: K = k 201 | fun apply(e: Entry[K, V]): Bool => e.key == key 202 | end 203 | 204 | if _entries.exists(test) then 205 | _updateEntry(k, v, _entries, Lists[Entry[K, V]].empty()) 206 | else 207 | let newEntries = _entries.prepend(Entry[K, V](k,v)) 208 | MultiLeafNode[K, V].from(newEntries) 209 | end 210 | 211 | fun _putWithHash(k: K, v: val->V, hash: U32, level: U32): Map[K, V] => 212 | """ 213 | Update the value associated with the provided key, using hash and level. 214 | """ 215 | update(k, v) 216 | 217 | fun _updateEntry(k: K, v: val->V, es: List[Entry[K, V]], acc: List[Entry[K, V]]): Map[K, V] => 218 | """ 219 | Return a new MultiLeafNode with the entry corresponding to k updated. 220 | """ 221 | try 222 | let next = es.head() 223 | if next.key == k then 224 | let newEntry = Entry[K, V](k, v) 225 | let newEntries = acc.prepend(newEntry).concat(es.tail()) 226 | MultiLeafNode[K, V].from(newEntries) 227 | else 228 | _updateEntry(k, v, es.tail(), acc.prepend(next)) 229 | end 230 | else 231 | MultiLeafNode[K, V].from(acc) 232 | end 233 | 234 | fun _removeEntry(k: K, es: List[Entry[K, V]], acc: List[Entry[K, V]]): Map[K, V] => 235 | """ 236 | Return a new MultiLeafNode with the entry corresponding to k removed. 237 | """ 238 | try 239 | let next = es.head() 240 | if next.key == k then 241 | let newEntries = acc.concat(es.tail()) 242 | MultiLeafNode[K, V].from(newEntries) 243 | else 244 | _removeEntry(k, es.tail(), acc.prepend(next)) 245 | end 246 | else 247 | MultiLeafNode[K, V].from(acc) 248 | end 249 | 250 | fun remove(k: K): Map[K, V] => 251 | """ 252 | Try to remove the provided key from the node. 253 | """ 254 | _removeEntry(k, _entries, Lists[Entry[K, V]].empty()) 255 | 256 | fun _removeWithHash(k: K, hash: U32, level: U32): Map[K, V] => 257 | """ 258 | Try to remove the prodied key form the node, using hash and level. 259 | """ 260 | remove(k) 261 | 262 | fun getOrElse(k: K, alt: val->V): val->V => 263 | """ 264 | Get the value associated with provided key if present. Otherwise, 265 | return the provided alternate value. 266 | """ 267 | try 268 | apply(k) 269 | else 270 | alt 271 | end 272 | 273 | fun contains(k: K): Bool => 274 | """ 275 | Check whether the node contains the provided key. 276 | """ 277 | try 278 | apply(k) 279 | true 280 | else 281 | false 282 | end 283 | 284 | class val MapNode[K: (Hashable val & Equatable[K] val), V] 285 | let _size: U64 286 | //Currently, 32-bit bitmap 287 | let _bitmap: U32 288 | let _pointers: Array[Map[K, V]] val 289 | 290 | new val create(bmap: U32, ps: Array[Map[K, V]] val) => 291 | _bitmap = bmap 292 | _pointers = ps 293 | _size = MapHelpers.sumArraySizes[K, V](_pointers) 294 | 295 | new val empty() => 296 | _bitmap = 0 297 | _pointers = recover val Array[Map[K, V]] end 298 | _size = 0 299 | 300 | fun size(): U64 => 301 | """ 302 | Return the size of the node. 303 | """ 304 | _size 305 | 306 | fun _is_leaf(): Bool => 307 | """ 308 | Predicate testing if the node is a leaf. 309 | """ 310 | false 311 | 312 | fun apply(k: K): val->V ? => 313 | """ 314 | Attempt to get the value corresponding to k. 315 | """ 316 | let hash = MapHelpers._hash[K](k) 317 | let level: U32 = 0 318 | _getWithHash(k, hash, level) 319 | 320 | fun _getWithHash(k: K, hash: U32, level: U32): val->V ? => 321 | """ 322 | Attempt to get the value corresponding to k, using hash and level. 323 | """ 324 | let bmapIdx = _BitOps.bitmapIdxFor(hash, level) 325 | if _BitOps.checkIdxBit(_bitmap, bmapIdx) then 326 | let arrayIdx = _BitOps.arrayIdxFor(_bitmap, bmapIdx) 327 | _pointers(arrayIdx)._getWithHash(k, hash, level + 1) 328 | else 329 | error 330 | end 331 | 332 | fun update(k: K, v: val->V): Map[K, V] ? => 333 | """ 334 | Update the value associated with the provided key. 335 | """ 336 | let hash = MapHelpers._hash[K](k) 337 | let level: U32 = 0 338 | _putWithHash(k, v, hash, level) 339 | 340 | fun _putWithHash(k: K, v: val->V, hash: U32, level: U32): Map[K, V] ? => 341 | """ 342 | Update the value associated with the provided key, using hash and level. 343 | """ 344 | if (level >= Maps._last_level()) then return _lastLevelPutWithHash(k, v, hash) end 345 | let bmapIdx = _BitOps.bitmapIdxFor(hash, level) 346 | let arrayIdx: USize = _BitOps.arrayIdxFor(_bitmap, bmapIdx) 347 | if _BitOps.checkIdxBit(_bitmap, bmapIdx) then 348 | let newNode = _pointers(arrayIdx)._putWithHash(k, v, hash, level + 1) 349 | let newArray = _overwriteInArrayAt(_pointers, newNode, arrayIdx) 350 | MapNode[K, V](_bitmap, newArray) 351 | else 352 | let newBitMap = _BitOps.flipIndexedBitOn(_bitmap, bmapIdx) 353 | let newNode = LeafNode[K, V](k, v) 354 | let newArray = _insertInArrayAt(_pointers, newNode, arrayIdx) 355 | MapNode[K, V](newBitMap, newArray) 356 | end 357 | 358 | fun _lastLevelPutWithHash(k: K, v: val->V, hash: U32): Map[K, V] ? => 359 | """ 360 | Update the value associated with the provided key, using the hash on 361 | the last level. 362 | """ 363 | let bmapIdx = _BitOps.bitmapIdxFor(hash, Maps._last_level()) 364 | let arrayIdx: USize = _BitOps.arrayIdxFor(_bitmap, bmapIdx) 365 | if _BitOps.checkIdxBit(_bitmap, bmapIdx) then 366 | let newNode = _pointers(arrayIdx).update(k, v) 367 | let newArray = _overwriteInArrayAt(_pointers, newNode, arrayIdx) 368 | MapNode[K, V](_bitmap, newArray) 369 | else 370 | let newBitMap = _BitOps.flipIndexedBitOn(_bitmap, bmapIdx) 371 | let newNode = MultiLeafNode[K, V].update(k,v) 372 | let newArray = _insertInArrayAt(_pointers, newNode, arrayIdx) 373 | MapNode[K, V](newBitMap, newArray) 374 | end 375 | 376 | fun _insertInArrayAt(arr: Array[Map[K, V]] val, node: Map[K, V], idx: USize): Array[Map[K, V]] val ? => 377 | """ 378 | Return a new array with the provided node inserted at provided index, shifting the 379 | old values at and above the index up one. 380 | """ 381 | var belowArr: USize = 0 382 | var aboveArr = idx 383 | let newArray: Array[Map[K, V]] trn = recover trn Array[Map[K, V]] end 384 | while(belowArr < idx) do 385 | newArray.push(arr(belowArr)) 386 | belowArr = belowArr + 1 387 | end 388 | newArray.push(node) 389 | while(aboveArr < arr.size()) do 390 | newArray.push(arr(aboveArr)) 391 | aboveArr = aboveArr + 1 392 | end 393 | newArray 394 | 395 | fun _overwriteInArrayAt(arr: Array[Map[K, V]] val, node: Map[K, V], idx: USize): Array[Map[K, V]] val ? => 396 | """ 397 | Return a new array with the provided node inserted at provided index. 398 | """ 399 | var belowArr: USize = 0 400 | var aboveArr = idx + 1 401 | let newArray: Array[Map[K, V]] trn = recover trn Array[Map[K, V]] end 402 | while(belowArr < idx) do 403 | newArray.push(arr(belowArr)) 404 | belowArr = belowArr + 1 405 | end 406 | newArray.push(node) 407 | while(aboveArr < arr.size()) do 408 | newArray.push(arr(aboveArr)) 409 | aboveArr = aboveArr + 1 410 | end 411 | newArray 412 | 413 | fun _removeInArrayAt(arr: Array[Map[K, V]] val, idx: USize): Array[Map[K, V]] val ? => 414 | """ 415 | Return a new array with the value at the provided index removed and values above the index 416 | shifted down one. 417 | """ 418 | var belowArr: USize = 0 419 | var aboveArr = idx + 1 420 | let newArray: Array[Map[K, V]] trn = recover trn Array[Map[K, V]] end 421 | while(belowArr < idx) do 422 | newArray.push(arr(belowArr)) 423 | belowArr = belowArr + 1 424 | end 425 | while(aboveArr < arr.size()) do 426 | newArray.push(arr(aboveArr)) 427 | aboveArr = aboveArr + 1 428 | end 429 | newArray 430 | 431 | fun val remove(k: K): Map[K, V] ? => 432 | """ 433 | Try to remove the provided key from the node. 434 | """ 435 | if contains(k) then _removeWithHash(k, MapHelpers._hash[K](k), 0) else this as Map[K, V] end 436 | 437 | fun _removeWithHash(k: K, hash: U32, level: U32): Map[K, V] ? => 438 | """ 439 | Try to remove the prodied key form the node, using hash and level. 440 | """ 441 | let bmapIdx = _BitOps.bitmapIdxFor(hash, level) 442 | let arrayIdx: USize = _BitOps.arrayIdxFor(_bitmap, bmapIdx) 443 | if level >= Maps._last_level() then 444 | let newNode = _pointers(arrayIdx).remove(k) 445 | MapNode[K, V](_bitmap, _overwriteInArrayAt(_pointers, newNode, arrayIdx)) 446 | else 447 | let target = _pointers(arrayIdx) 448 | if target._is_leaf() then 449 | let newBMap = _BitOps.flipIndexedBitOff(_bitmap, bmapIdx) 450 | let newArray = _removeInArrayAt(_pointers, arrayIdx) 451 | MapNode[K, V](newBMap, newArray) 452 | else 453 | let newNode = target._removeWithHash(k, hash, level + 1) 454 | let newArray = _overwriteInArrayAt(_pointers, newNode, arrayIdx) 455 | MapNode[K, V](_bitmap, newArray) 456 | end 457 | end 458 | 459 | fun getOrElse(k: K, alt: val->V): val->V => 460 | """ 461 | Get the value associated with provided key if present. Otherwise, 462 | return the provided alternate value. 463 | """ 464 | try 465 | apply(k) 466 | else 467 | alt 468 | end 469 | 470 | fun contains(k: K): Bool => 471 | """ 472 | Check whether the node contains the provided key. 473 | """ 474 | try 475 | apply(k) 476 | true 477 | else 478 | false 479 | end 480 | 481 | // fun val keys(): MapKeys[K, V] => MapKeys[K, V](this) 482 | 483 | // fun val values(): MapValues[K, V] => MapValues[K, V](this) 484 | 485 | // fun val pairs(): MapPairs[K, V] => MapPairs[K, V](this) 486 | 487 | // class MapKeys[K: (Hashable val & Equatable[K] val), V] 488 | // embed _pairs: MapPairs[K, V] 489 | 490 | // new create(m: Map[K, V]) => _pairs = MapPairs[K, V](m) 491 | 492 | // fun has_next(): Bool => _pairs.has_next() 493 | 494 | // fun ref next(): K ? => _pairs.next()._1 495 | 496 | // class MapValues[K: (Hashable val & Equatable[K] val), V] 497 | // embed _pairs: MapPairs[K, V] 498 | 499 | // new create(m: Map[K, V]) => _pairs = MapPairs[K, V](m) 500 | 501 | // fun has_next(): Bool => _pairs.has_next() 502 | 503 | // fun ref next(): val->V ? => _pairs.next()._2 504 | 505 | // class MapPairs[K: (Hashable val & Equatable[K] val), V] 506 | 507 | // new create(m: Map[K, V]) => 508 | 509 | // fun has_next(): Bool => _i < _size 510 | 511 | // fun ref next(): (K, val->V) ? => 512 | 513 | // For 32-bit operations 514 | primitive _BitOps 515 | fun maskLow(n: U32): U32 => 516 | """ 517 | Mask the low five bits 518 | """ 519 | n and 0x1F 520 | 521 | fun bitmapIdxFor(hash: U32, level: U32): U32 => 522 | """ 523 | Calculate the bitmap index from the next five bits of the hash 524 | """ 525 | maskLow(hash >> (level * 5)) 526 | 527 | fun checkIdxBit(bmap: U32, idx: U32): Bool => 528 | """ 529 | Check the bit corresponding to the provided index 530 | """ 531 | let bit = (bmap >> idx) and 1 532 | if bit == 0 then false else true end 533 | 534 | fun arrayIdxFor(bmap: U32, idx: U32): USize => 535 | """ 536 | Check the array index for the map node corresponding to 537 | the provided bitmap index. Since the pointers array only includes 538 | map nodes that are present, we use the popcount of the bits 539 | below the bitmap index. 540 | """ 541 | let mask = not(0xFFFF_FFFF << idx) 542 | ((mask and bmap).popcount()).usize() 543 | 544 | fun flipIndexedBitOn(bmap: U32, idx: U32): U32 => 545 | """ 546 | Return a new bitmap with the index bit set to 1. 547 | """ 548 | (1 << idx) or bmap 549 | 550 | fun flipIndexedBitOff(bmap: U32, idx: U32): U32 => 551 | """ 552 | Return a new bitmap with the index bit set to 0. 553 | """ 554 | not(1 << idx) and bmap 555 | 556 | primitive MapHelpers 557 | fun sumArraySizes[K: (Hashable val & Equatable[K] val), V](arr: Array[Map[K, V]] val): U64 => 558 | """ 559 | Sum the sizes of all maps in the provided array. 560 | """ 561 | var sum: U64 = 0 562 | for m in arr.values() do 563 | sum = sum + m.size() 564 | end 565 | sum 566 | 567 | fun _hash[K: Hashable val](k: K): U32 => 568 | """ 569 | Return a 32-bit hash of the key. 570 | """ 571 | k.hash().u32() 572 | -------------------------------------------------------------------------------- /src/persistent-data/persistent-data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jtfmumm/pony-functional-data/182f4dc9f49c8946aedf48cacb2226b684627987/src/persistent-data/persistent-data -------------------------------------------------------------------------------- /src/persistent-data/persistent.pony: -------------------------------------------------------------------------------- 1 | """ 2 | # Persistent Collections Package 3 | 4 | List - A persistent list with functional transformations. 5 | 6 | Map - A persistent map based on Bagwell's hash array mapped trie algorithm. 7 | """ -------------------------------------------------------------------------------- /src/persistent-data/test.pony: -------------------------------------------------------------------------------- 1 | use "ponytest" 2 | use "../function-types" 3 | use mut = "collections" 4 | use "random" 5 | use "debug" 6 | use "benchmarks" 7 | 8 | actor Main is TestList 9 | new create(env: Env) => 10 | PonyTest(env, this) 11 | 12 | new make() => None 13 | 14 | fun tag tests(test: PonyTest) => 15 | run_tests(test) 16 | // run_benchmarks(test) 17 | 18 | fun tag run_benchmarks(test: PonyTest) => 19 | test(_Benchmark) 20 | 21 | fun tag run_tests(test: PonyTest) => 22 | test(_TestPrepend) 23 | test(_TestFrom) 24 | test(_TestConcat) 25 | test(_TestMap) 26 | test(_TestFlatMap) 27 | test(_TestFilter) 28 | test(_TestFold) 29 | test(_TestEveryExists) 30 | test(_TestPartition) 31 | test(_TestDrop) 32 | test(_TestDropWhile) 33 | test(_TestTake) 34 | test(_TestTakeWhile) 35 | // test(_TestContains) 36 | test(_TestBitOps) 37 | test(_TestHAMTMap) 38 | test(_TestMapVsMap) 39 | 40 | class iso _Benchmark is UnitTest 41 | fun name(): String => "benchmarks" 42 | 43 | fun apply(h: TestHelper) ? => 44 | let iterations: USize = 100000 45 | let keys: U64 = 100000 46 | Bench.bench(iterations, keys) 47 | // Bench.list_try_bench(iterations) 48 | 49 | true 50 | 51 | class iso _TestPrepend is UnitTest 52 | fun name(): String => "persistent-data/List/prepend()" 53 | 54 | fun apply(h: TestHelper) ? => 55 | let a = Lists[U32].empty() 56 | let b = Lists[U32].cons(1, Lists[U32].empty()) 57 | let c = Lists[U32].cons(2, b) 58 | let d = c.prepend(3) 59 | let e = a.prepend(10) 60 | 61 | h.assert_eq[U64](a.size(), 0) 62 | h.assert_eq[U64](b.size(), 1) 63 | h.assert_eq[U64](c.size(), 2) 64 | h.assert_eq[U64](d.size(), 3) 65 | h.assert_eq[U64](e.size(), 1) 66 | 67 | h.assert_eq[U32](b.head(), 1) 68 | h.assert_eq[U64](b.tail().size(), 0) 69 | h.assert_eq[U32](c.head(), 2) 70 | h.assert_eq[U64](c.tail().size(), 1) 71 | h.assert_eq[U32](d.head(), 3) 72 | h.assert_eq[U64](d.tail().size(), 2) 73 | h.assert_eq[U32](e.head(), 10) 74 | h.assert_eq[U64](e.tail().size(), 0) 75 | 76 | true 77 | 78 | class iso _TestFrom is UnitTest 79 | fun name(): String => "persistent-data/Lists/from()" 80 | 81 | fun apply(h: TestHelper) ? => 82 | let l1 = Lists[U32]([1; 2; 3]) 83 | h.assert_eq[U64](l1.size(), 3) 84 | h.assert_eq[U32](l1.head(), 1) 85 | 86 | true 87 | 88 | class iso _TestConcat is UnitTest 89 | fun name(): String => "persistent-data/List/concat()" 90 | 91 | fun apply(h: TestHelper) ? => 92 | let l1 = Lists[U32]([1; 2; 3]) 93 | let l2 = Lists[U32]([4; 5; 6]) 94 | let l3 = l1.concat(l2) 95 | let l4 = l3.reverse() 96 | h.assert_eq[U64](l3.size(), 6) 97 | h.assert_true(Lists[U32].eq(l3, Lists[U32]([1;2;3;4;5;6]))) 98 | h.assert_true(Lists[U32].eq(l4, Lists[U32]([6;5;4;3;2;1]))) 99 | 100 | let l5 = Lists[U32].empty() 101 | let l6 = l5.reverse() 102 | let l7 = l6.concat(l1) 103 | h.assert_eq[U64](l6.size(), 0) 104 | h.assert_true(Lists[U32].eq(l7, Lists[U32]([1;2;3]))) 105 | 106 | let l8 = Lists[U32]([1]) 107 | let l9 = l8.reverse() 108 | h.assert_true(Lists[U32].eq(l9, Lists[U32]([1]))) 109 | 110 | let l10 = l6 + l1 111 | h.assert_true(Lists[U32].eq(l10, Lists[U32]([1;2;3]))) 112 | 113 | true 114 | 115 | class iso _TestMap is UnitTest 116 | fun name(): String => "persistent-data/Lists/map()" 117 | 118 | fun apply(h: TestHelper) ? => 119 | let l5 = Lists[U32]([1; 2; 3]).map[U32]({(x: U32): U32 => x * 2}) 120 | h.assert_true(Lists[U32].eq(l5, Lists[U32]([2;4;6]))) 121 | 122 | true 123 | 124 | class iso _TestFlatMap is UnitTest 125 | fun name(): String => "persistent-data/Lists/flat_map()" 126 | 127 | fun apply(h: TestHelper) ? => 128 | let f = {(x: U32): List[U32] => Lists[U32]([x - 1; x; x + 1])} 129 | let l6 = Lists[U32]([2; 5; 8]).flat_map[U32](f) 130 | h.assert_true(Lists[U32].eq(l6, Lists[U32]([1;2;3;4;5;6;7;8;9]))) 131 | 132 | true 133 | 134 | class iso _TestFilter is UnitTest 135 | fun name(): String => "persistent-data/Lists/filter()" 136 | 137 | fun apply(h: TestHelper) ? => 138 | let is_even = {(x: U32): Bool => x % 2 == 0} 139 | let l7 = Lists[U32]([1;2;3;4;5;6;7;8]).filter(is_even) 140 | h.assert_true(Lists[U32].eq(l7, Lists[U32]([2;4;6;8]))) 141 | 142 | true 143 | 144 | class iso _TestFold is UnitTest 145 | fun name(): String => "persistent-data/Lists/fold()" 146 | 147 | fun apply(h: TestHelper) ? => 148 | let add = {(acc: U32, x: U32): U32 => acc + x} 149 | let value = Lists[U32]([1;2;3]).fold[U32](add, 0) 150 | h.assert_eq[U32](value, 6) 151 | 152 | let doubleAndPrepend = {(acc: List[U32], x: U32): List[U32] => acc.prepend(x * 2)} 153 | let l8 = Lists[U32]([1;2;3]).fold[List[U32]](doubleAndPrepend, Lists[U32].empty()) 154 | h.assert_true(Lists[U32].eq(l8, Lists[U32]([6;4;2]))) 155 | 156 | true 157 | 158 | class iso _TestEveryExists is UnitTest 159 | fun name(): String => "persistent-data/Lists/every()exists()" 160 | 161 | fun apply(h: TestHelper) => 162 | let is_even = {(x: U32): Bool => x % 2 == 0} 163 | let l9 = Lists[U32]([4;2;10]) 164 | let l10 = Lists[U32]([1;1;3]) 165 | let l11 = Lists[U32]([1;1;2]) 166 | let l12 = Lists[U32]([2;2;3]) 167 | let l13 = Lists[U32].empty() 168 | h.assert_eq[Bool](l9.every(is_even), true) 169 | h.assert_eq[Bool](l10.every(is_even), false) 170 | h.assert_eq[Bool](l11.every(is_even), false) 171 | h.assert_eq[Bool](l12.every(is_even), false) 172 | h.assert_eq[Bool](l13.every(is_even), true) 173 | h.assert_eq[Bool](l9.exists(is_even), true) 174 | h.assert_eq[Bool](l10.exists(is_even), false) 175 | h.assert_eq[Bool](l11.exists(is_even), true) 176 | h.assert_eq[Bool](l12.exists(is_even), true) 177 | h.assert_eq[Bool](l13.exists(is_even), false) 178 | 179 | true 180 | 181 | class iso _TestPartition is UnitTest 182 | fun name(): String => "persistent-data/Lists/partition()" 183 | 184 | fun apply(h: TestHelper) ? => 185 | let is_even = {(x: U32): Bool => x % 2 == 0} 186 | let l = Lists[U32]([1;2;3;4;5;6]) 187 | (let hits, let misses) = l.partition(is_even) 188 | h.assert_true(Lists[U32].eq(hits, Lists[U32]([2;4;6]))) 189 | h.assert_true(Lists[U32].eq(misses, Lists[U32]([1;3;5]))) 190 | 191 | true 192 | 193 | class iso _TestDrop is UnitTest 194 | fun name(): String => "persistent-data/List/drop()" 195 | 196 | fun apply(h: TestHelper) ? => 197 | let l = Lists[String](["a";"b";"c";"d";"e"]) 198 | let l2 = Lists[U32]([1;2]) 199 | let empty = Lists[String].empty() 200 | h.assert_true(Lists[String].eq(l.drop(3), Lists[String](["d";"e"]))) 201 | h.assert_true(Lists[U32].eq(l2.drop(3), Lists[U32].empty())) 202 | h.assert_true(Lists[String].eq(empty.drop(3), Lists[String].empty())) 203 | true 204 | 205 | class iso _TestDropWhile is UnitTest 206 | fun name(): String => "persistent-data/List/drop_while()" 207 | 208 | fun apply(h: TestHelper) ? => 209 | let is_even = {(x: U32): Bool => x % 2 == 0} 210 | let l = Lists[U32]([4;2;6;1;3;4;6]) 211 | let empty = Lists[U32].empty() 212 | h.assert_true(Lists[U32].eq(l.drop_while(is_even), Lists[U32]([1;3;4;6]))) 213 | h.assert_true(Lists[U32].eq(empty.drop_while(is_even), Lists[U32].empty())) 214 | true 215 | 216 | class iso _TestTake is UnitTest 217 | fun name(): String => "persistent-data/List/take()" 218 | 219 | fun apply(h: TestHelper) ? => 220 | let l = Lists[String](["a";"b";"c";"d";"e"]) 221 | let l2 = Lists[U32]([1;2]) 222 | let empty = Lists[String].empty() 223 | h.assert_true(Lists[String].eq(l.take(3), Lists[String](["a";"b";"c"]))) 224 | h.assert_true(Lists[U32].eq(l2.take(3), Lists[U32]([1;2]))) 225 | h.assert_true(Lists[String].eq(empty.take(3), Lists[String].empty())) 226 | true 227 | 228 | class iso _TestTakeWhile is UnitTest 229 | fun name(): String => "persistent-data/List/take_while()" 230 | 231 | fun apply(h: TestHelper) ? => 232 | let is_even = {(x: U32): Bool => x % 2 == 0} 233 | let l = Lists[U32]([4;2;6;1;3;4;6]) 234 | let empty = Lists[U32].empty() 235 | h.assert_true(Lists[U32].eq(l.take_while(is_even), Lists[U32]([4;2;6]))) 236 | h.assert_true(Lists[U32].eq(empty.take_while(is_even), Lists[U32].empty())) 237 | 238 | true 239 | 240 | //class iso _TestContains is UnitTest 241 | // fun name(): String => "persistent-data/List/contains()" 242 | // 243 | // fun apply(h: TestHelper) => 244 | // let l = Lists[String](["a","b","c"]) 245 | // 246 | // h.assert_true(l.contains("a")) 247 | // h.assert_true(l.contains("b")) 248 | // h.assert_true(l.contains("c")) 249 | // h.assert_false(l.contains("d")) 250 | // h.assert_false(l.contains("e")) 251 | // 252 | // let empty = Lists[String].empty() 253 | // 254 | // h.assert_false(empty.contains("a")) 255 | // true 256 | 257 | class iso _TestBitOps is UnitTest 258 | fun name(): String => "persistent-data/_BitOps" 259 | 260 | fun apply(h: TestHelper) => 261 | let a = _BitOps.maskLow(845) 262 | let b = _BitOps.maskLow(968) 263 | let c = _BitOps.maskLow(875) 264 | let d = _BitOps.maskLow(559) 265 | let e = _BitOps.maskLow(618) 266 | h.assert_eq[U32](a, 13) 267 | h.assert_eq[U32](b, 8) 268 | h.assert_eq[U32](c, 11) 269 | h.assert_eq[U32](d, 15) 270 | h.assert_eq[U32](e, 10) 271 | 272 | //1100 00011 11101 01001 10111 or 12711223 273 | let b0 = _BitOps.bitmapIdxFor(12711223, 0) 274 | let b1 = _BitOps.bitmapIdxFor(12711223, 1) 275 | let b2 = _BitOps.bitmapIdxFor(12711223, 2) 276 | let b3 = _BitOps.bitmapIdxFor(12711223, 3) 277 | let b4 = _BitOps.bitmapIdxFor(12711223, 4) 278 | h.assert_eq[U32](b0, 23) 279 | h.assert_eq[U32](b1, 9) 280 | h.assert_eq[U32](b2, 29) 281 | h.assert_eq[U32](b3, 3) 282 | h.assert_eq[U32](b4, 12) 283 | 284 | let c0 = _BitOps.checkIdxBit(13, 0) 285 | let c1 = _BitOps.checkIdxBit(13, 1) 286 | let c2 = _BitOps.checkIdxBit(13, 2) 287 | let c3 = _BitOps.checkIdxBit(13, 3) 288 | let c4 = _BitOps.checkIdxBit(13, 4) 289 | let c5 = _BitOps.checkIdxBit(26, 0) 290 | let c6 = _BitOps.checkIdxBit(26, 1) 291 | let c7 = _BitOps.checkIdxBit(26, 2) 292 | let c8 = _BitOps.checkIdxBit(26, 3) 293 | let c9 = _BitOps.checkIdxBit(26, 4) 294 | h.assert_eq[Bool](c0, true) 295 | h.assert_eq[Bool](c1, false) 296 | h.assert_eq[Bool](c2, true) 297 | h.assert_eq[Bool](c3, true) 298 | h.assert_eq[Bool](c4, false) 299 | h.assert_eq[Bool](c5, false) 300 | h.assert_eq[Bool](c6, true) 301 | h.assert_eq[Bool](c7, false) 302 | h.assert_eq[Bool](c8, true) 303 | h.assert_eq[Bool](c9, true) 304 | 305 | let d0 = _BitOps.flipIndexedBitOn(8, 0) 306 | let d1 = _BitOps.flipIndexedBitOn(8, 1) 307 | let d2 = _BitOps.flipIndexedBitOn(8, 2) 308 | let d3 = _BitOps.flipIndexedBitOn(8, 3) 309 | let d4 = _BitOps.flipIndexedBitOn(8, 4) 310 | h.assert_eq[U32](d0, 9) 311 | h.assert_eq[U32](d1, 10) 312 | h.assert_eq[U32](d2, 12) 313 | h.assert_eq[U32](d3, 8) 314 | h.assert_eq[U32](d4, 24) 315 | 316 | //1100 00011 11101 01001 10111 or 12711223 317 | let f0 = _BitOps.arrayIdxFor(12711223, 0) 318 | let f1 = _BitOps.arrayIdxFor(12711223, 5) 319 | let f2 = _BitOps.arrayIdxFor(12711223, 10) 320 | let f3 = _BitOps.arrayIdxFor(12711223, 25) 321 | h.assert_eq[USize](f0, 0) 322 | h.assert_eq[USize](f1, 4) 323 | h.assert_eq[USize](f2, 6) 324 | h.assert_eq[USize](f3, 14) 325 | 326 | true 327 | 328 | class iso _TestHAMTMap is UnitTest 329 | fun name(): String => "persistent-data/Map" 330 | 331 | fun apply(h: TestHelper) => 332 | let m1: Map[String,U32] = Maps.empty[String,U32]() 333 | h.assert_error({()(m1)? => m1("a")}) 334 | let s1 = m1.size() 335 | h.assert_eq[U64](s1, 0) 336 | 337 | try 338 | let m2 = m1.update("a", 5) 339 | let m3 = m2.update("b", 10) 340 | let m4 = m3.update("a", 4) 341 | let m5 = m4.update("c", 0) 342 | h.assert_eq[U32](m2("a"), 5) 343 | h.assert_eq[U32](m3("b"), 10) 344 | h.assert_eq[U32](m4("a"), 4) 345 | h.assert_eq[U32](m5("c"), 0) 346 | else 347 | h.complete(false) 348 | end 349 | 350 | try 351 | let m6 = Maps.from[String,U32]([("a", 2); ("b", 3); ("d", 4); ("e", 5)]) 352 | let m7 = m6.update("a", 10) 353 | h.assert_eq[U32](m6("a"), 2) 354 | h.assert_eq[U32](m6("b"), 3) 355 | h.assert_eq[U32](m6("d"), 4) 356 | h.assert_eq[U32](m6("e"), 5) 357 | h.assert_eq[U32](m7("a"), 10) 358 | h.assert_eq[U32](m7("b"), 3) 359 | h.assert_eq[U32](m7("a"), 10) 360 | let m8 = m7.remove("a") 361 | h.assert_error({()(m8 = m8)? => m8("a")}) 362 | h.assert_eq[U32](m8("b"), 3) 363 | h.assert_eq[U32](m8("d"), 4) 364 | h.assert_eq[U32](m8("e"), 5) 365 | let m9 = m7.remove("e") 366 | h.assert_error({()(m9 = m9)? => m9("e")}) 367 | h.assert_eq[U32](m9("b"), 3) 368 | h.assert_eq[U32](m9("d"), 4) 369 | let m10 = m9.remove("b").remove("d") 370 | h.assert_error({()(m10 = m10)? => m10("b")}) 371 | h.assert_error({()(m10 = m10)? => m10("d")}) 372 | else 373 | h.complete(false) 374 | end 375 | 376 | true 377 | 378 | class iso _TestMapVsMap is UnitTest 379 | fun name(): String => "persistent-data/Map vs. collections Map" 380 | 381 | fun apply(h: TestHelper) ? => 382 | var pMap: Map[String,U64] = Maps.empty[String,U64]() 383 | let mMap: mut.Map[String,U64] = mut.Map[String,U64]() 384 | let kvs = Array[(String,U64)]() 385 | let dice = Dice(MT) 386 | var count: USize = 0 387 | let iterations: USize = 100000 388 | let keys: U64 = 10000 389 | 390 | while(count < iterations) do 391 | let k0 = dice(1,keys).string() 392 | let k: String val = consume k0 393 | let v = dice(1,100000) 394 | kvs.push((k, v)) 395 | count = count + 1 396 | end 397 | 398 | count = 0 399 | while(count < iterations) do 400 | let k = kvs(count)._1 401 | let v = kvs(count)._2 402 | pMap = pMap.update(k, v) 403 | mMap.update(k, v) 404 | count = count + 1 405 | end 406 | count = 0 407 | while(count < iterations) do 408 | let pmv = pMap(kvs(count)._1) 409 | let mmv = mMap(kvs(count)._1) 410 | h.assert_eq[Bool](H.equalMapU64Values(pmv, mmv), true) 411 | count = count + 1 412 | end 413 | 414 | true 415 | 416 | //class iso _TestMapOption is UnitTest 417 | // fun name(): String => "persistent-data/Map returning Option" 418 | // 419 | // fun apply(h: TestHelper) ? => 420 | // var pMap: Map[String,U64] = Maps.empty[String,U64]() 421 | // pMap = pMap.update("a", 1) 422 | // let some: Option[U64] = pMap.getOption("a") 423 | // let none: Option[U64] = pMap.getOption("b") 424 | // h.assert_eq[U64](some.value(), 1) 425 | // h.assert_eq[Bool](some.is_empty(), false) 426 | // h.assert_eq[Bool](none.is_empty(), true) 427 | // 428 | // true 429 | 430 | 431 | primitive H 432 | fun isValue(v: (U32 | None), value: U32): Bool => 433 | match v 434 | | None => false 435 | | let x: U32 => x == value 436 | else 437 | false 438 | end 439 | 440 | fun isNone(v: (U32 | None)): Bool => 441 | match v 442 | | None => true 443 | else 444 | false 445 | end 446 | 447 | fun equalMapU64Values(a: (U64 | None), b: (U64 | None)): Bool => 448 | match (a,b) 449 | | (None,None) => true 450 | | (None,_) => 451 | Debug.out(a.string() + " != " + b.string()) 452 | false 453 | | (_,None) => 454 | Debug.out(a.string() + " != " + b.string()) 455 | false 456 | | (let x: U64, let y: U64) => 457 | if (x != y) then Debug.out(a.string() + " != " + b.string()) end 458 | x == y 459 | else 460 | Debug.out(a.string() + " != " + b.string()) 461 | false 462 | end 463 | --------------------------------------------------------------------------------