├── .gitignore ├── LICENSE ├── README.md ├── bsconfig.json ├── package-lock.json ├── package.json ├── src ├── Affect.re └── Effect.re ├── test ├── Fs.re ├── Test_Affect.re └── Test_Effect.re └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/bs 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | node_modules 27 | lib 28 | .bsb.lock 29 | sample 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Risto Stevcev 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bs-effects 2 | 3 | Bucklescript data structures for effectful sync and async programming 4 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bs-effects", 3 | "version": "0.10.0", 4 | "refmt": 3, 5 | "namespace": true, 6 | "warnings": { "number": "-102", "error": "+8+27" }, 7 | "sources": [{ 8 | "dir": "src", 9 | "files": [ "Effect.re", "Affect.re" ] 10 | }, { 11 | "dir": "test", 12 | "type": "dev" 13 | }], 14 | "package-specs": { 15 | "module": "commonjs", 16 | "in-source": false 17 | }, 18 | "suffix": ".bs.js", 19 | "bs-dev-dependencies": [ 20 | "bs-chai", 21 | "bs-mocha", 22 | "bs-jsverify" 23 | ], 24 | "bs-dependencies": [ 25 | "bs-errors", 26 | "bs-abstract" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bs-effects", 3 | "version": "0.9.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "assertion-error": { 8 | "version": "1.1.0", 9 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 10 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 11 | "dev": true 12 | }, 13 | "balanced-match": { 14 | "version": "1.0.0", 15 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 16 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 17 | "dev": true 18 | }, 19 | "brace-expansion": { 20 | "version": "1.1.11", 21 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 22 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 23 | "dev": true, 24 | "requires": { 25 | "balanced-match": "^1.0.0", 26 | "concat-map": "0.0.1" 27 | } 28 | }, 29 | "browser-stdout": { 30 | "version": "1.3.1", 31 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 32 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 33 | "dev": true 34 | }, 35 | "bs-abstract": { 36 | "version": "0.16.0", 37 | "resolved": "https://registry.npmjs.org/bs-abstract/-/bs-abstract-0.16.0.tgz", 38 | "integrity": "sha512-b1/qOWNBqL44lFciqEs73UxOwxAfnXeQgJtXGlfVwKYMZ6qNKgNrQe3Wokg8awQHs2EkmAJgtxku2zrGlwd/Jw==" 39 | }, 40 | "bs-chai": { 41 | "version": "0.4.0", 42 | "resolved": "https://registry.npmjs.org/bs-chai/-/bs-chai-0.4.0.tgz", 43 | "integrity": "sha512-DThj/Kv4rRHTaIeQMP1RclrPmb85c8Su/aNs3X3kDCmtDacQCIt2H7XXJuWLO1+z+wJu2e68/hUZ9YC0rtN9OA==", 44 | "dev": true, 45 | "requires": { 46 | "chai": "^4.1.2", 47 | "mocha": "^5.0.1" 48 | } 49 | }, 50 | "bs-errors": { 51 | "version": "0.5.0", 52 | "resolved": "https://registry.npmjs.org/bs-errors/-/bs-errors-0.5.0.tgz", 53 | "integrity": "sha512-boxMQF/eksRyJeNLlk27Hb77ywpJWbv5j7cp2qEbRDECWsUqVXdBkKbRY/OlsYZTBWmtBjCoWtbIPvmiw6w/kA==" 54 | }, 55 | "bs-jsverify": { 56 | "version": "0.10.0", 57 | "resolved": "https://registry.npmjs.org/bs-jsverify/-/bs-jsverify-0.10.0.tgz", 58 | "integrity": "sha512-egb28RqdX9P01V6EQfOxCEPTgiSse+gh8JbKLqcj9P/6zWE51Z3NIX4L/s5/EPL97U4pJTxEsMnKjRdAW4Porg==", 59 | "dev": true, 60 | "requires": { 61 | "jsverify": "^0.8.3" 62 | } 63 | }, 64 | "bs-mocha": { 65 | "version": "0.4.0", 66 | "resolved": "https://registry.npmjs.org/bs-mocha/-/bs-mocha-0.4.0.tgz", 67 | "integrity": "sha512-ntUKImTAK3oGAFXoeUgpBGIzzfLs10Y8hwEGP+/k50v++8nffcoEtuq4t3Pb3Fh/R8qjZ46M1eiNZkydtUnD/Q==", 68 | "dev": true, 69 | "requires": { 70 | "mocha": "^5.0.0" 71 | } 72 | }, 73 | "bs-platform": { 74 | "version": "4.0.3", 75 | "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-4.0.3.tgz", 76 | "integrity": "sha1-RRDByRXMWxabVxflwK2lLT+Z/5g=", 77 | "dev": true 78 | }, 79 | "chai": { 80 | "version": "4.1.2", 81 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 82 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 83 | "dev": true, 84 | "requires": { 85 | "assertion-error": "^1.0.1", 86 | "check-error": "^1.0.1", 87 | "deep-eql": "^3.0.0", 88 | "get-func-name": "^2.0.0", 89 | "pathval": "^1.0.0", 90 | "type-detect": "^4.0.0" 91 | } 92 | }, 93 | "check-error": { 94 | "version": "1.0.2", 95 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 96 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 97 | "dev": true 98 | }, 99 | "commander": { 100 | "version": "2.15.1", 101 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 102 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 103 | "dev": true 104 | }, 105 | "concat-map": { 106 | "version": "0.0.1", 107 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 108 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 109 | "dev": true 110 | }, 111 | "debug": { 112 | "version": "3.1.0", 113 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 114 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 115 | "dev": true, 116 | "requires": { 117 | "ms": "2.0.0" 118 | } 119 | }, 120 | "deep-eql": { 121 | "version": "3.0.1", 122 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 123 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 124 | "dev": true, 125 | "requires": { 126 | "type-detect": "^4.0.0" 127 | } 128 | }, 129 | "diff": { 130 | "version": "3.5.0", 131 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 132 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 133 | "dev": true 134 | }, 135 | "escape-string-regexp": { 136 | "version": "1.0.5", 137 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 138 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 139 | "dev": true 140 | }, 141 | "fs.realpath": { 142 | "version": "1.0.0", 143 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 144 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 145 | "dev": true 146 | }, 147 | "get-func-name": { 148 | "version": "2.0.0", 149 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 150 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 151 | "dev": true 152 | }, 153 | "glob": { 154 | "version": "7.1.2", 155 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 156 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 157 | "dev": true, 158 | "requires": { 159 | "fs.realpath": "^1.0.0", 160 | "inflight": "^1.0.4", 161 | "inherits": "2", 162 | "minimatch": "^3.0.4", 163 | "once": "^1.3.0", 164 | "path-is-absolute": "^1.0.0" 165 | } 166 | }, 167 | "growl": { 168 | "version": "1.10.5", 169 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 170 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 171 | "dev": true 172 | }, 173 | "has-flag": { 174 | "version": "3.0.0", 175 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 176 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 177 | "dev": true 178 | }, 179 | "he": { 180 | "version": "1.1.1", 181 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 182 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 183 | "dev": true 184 | }, 185 | "inflight": { 186 | "version": "1.0.6", 187 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 188 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 189 | "dev": true, 190 | "requires": { 191 | "once": "^1.3.0", 192 | "wrappy": "1" 193 | } 194 | }, 195 | "inherits": { 196 | "version": "2.0.3", 197 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 198 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 199 | "dev": true 200 | }, 201 | "jsverify": { 202 | "version": "0.8.3", 203 | "resolved": "https://registry.npmjs.org/jsverify/-/jsverify-0.8.3.tgz", 204 | "integrity": "sha1-AukXjhkktar5WcAjmvhO1cbQc4w=", 205 | "dev": true, 206 | "requires": { 207 | "lazy-seq": "^1.0.0", 208 | "rc4": "~0.1.5", 209 | "trampa": "^1.0.0", 210 | "typify-parser": "^1.1.0" 211 | } 212 | }, 213 | "lazy-seq": { 214 | "version": "1.0.0", 215 | "resolved": "https://registry.npmjs.org/lazy-seq/-/lazy-seq-1.0.0.tgz", 216 | "integrity": "sha1-iAy4qrJWAmOC4C9T7AiWgqdMW2o=", 217 | "dev": true 218 | }, 219 | "minimatch": { 220 | "version": "3.0.4", 221 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 222 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 223 | "dev": true, 224 | "requires": { 225 | "brace-expansion": "^1.1.7" 226 | } 227 | }, 228 | "minimist": { 229 | "version": "0.0.8", 230 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 231 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 232 | "dev": true 233 | }, 234 | "mkdirp": { 235 | "version": "0.5.1", 236 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 237 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 238 | "dev": true, 239 | "requires": { 240 | "minimist": "0.0.8" 241 | } 242 | }, 243 | "mocha": { 244 | "version": "5.2.0", 245 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 246 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 247 | "dev": true, 248 | "requires": { 249 | "browser-stdout": "1.3.1", 250 | "commander": "2.15.1", 251 | "debug": "3.1.0", 252 | "diff": "3.5.0", 253 | "escape-string-regexp": "1.0.5", 254 | "glob": "7.1.2", 255 | "growl": "1.10.5", 256 | "he": "1.1.1", 257 | "minimatch": "3.0.4", 258 | "mkdirp": "0.5.1", 259 | "supports-color": "5.4.0" 260 | } 261 | }, 262 | "ms": { 263 | "version": "2.0.0", 264 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 265 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 266 | "dev": true 267 | }, 268 | "once": { 269 | "version": "1.4.0", 270 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 271 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 272 | "dev": true, 273 | "requires": { 274 | "wrappy": "1" 275 | } 276 | }, 277 | "path-is-absolute": { 278 | "version": "1.0.1", 279 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 280 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 281 | "dev": true 282 | }, 283 | "pathval": { 284 | "version": "1.1.0", 285 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 286 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 287 | "dev": true 288 | }, 289 | "rc4": { 290 | "version": "0.1.5", 291 | "resolved": "https://registry.npmjs.org/rc4/-/rc4-0.1.5.tgz", 292 | "integrity": "sha1-CMbgSgFo9utiHCKrbLEVG9n0pk0=", 293 | "dev": true 294 | }, 295 | "supports-color": { 296 | "version": "5.4.0", 297 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 298 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 299 | "dev": true, 300 | "requires": { 301 | "has-flag": "^3.0.0" 302 | } 303 | }, 304 | "trampa": { 305 | "version": "1.0.0", 306 | "resolved": "https://registry.npmjs.org/trampa/-/trampa-1.0.0.tgz", 307 | "integrity": "sha1-Ukc0esM0gH+mwAAERMuRtjmECtU=", 308 | "dev": true 309 | }, 310 | "type-detect": { 311 | "version": "4.0.8", 312 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 313 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 314 | "dev": true 315 | }, 316 | "typify-parser": { 317 | "version": "1.1.0", 318 | "resolved": "https://registry.npmjs.org/typify-parser/-/typify-parser-1.1.0.tgz", 319 | "integrity": "sha1-rHO/pfJTQ0aOLQ8zRsYRe8A9PJk=", 320 | "dev": true 321 | }, 322 | "wrappy": { 323 | "version": "1.0.2", 324 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 325 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 326 | "dev": true 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bs-effects", 3 | "version": "0.10.0", 4 | "description": "Bucklescript data structures for effectful sync and async programming", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/Risto-Stevcev/bs-effects.git" 8 | }, 9 | "scripts": { 10 | "clean": "bsb -clean-world", 11 | "build": "bsb -make-world", 12 | "watch": "bsb -make-world -w", 13 | "test": "mocha lib/js/test/Test*.js", 14 | "test:watch": "mocha -w lib/js/test/Test*.js" 15 | }, 16 | "homepage": "https://github.com/Risto-Stevcev/bs-effects", 17 | "bugs": "https://github.com/Risto-Stevcev/bs-effects/issues", 18 | "keywords": [ 19 | "BuckleScript", 20 | "reason" 21 | ], 22 | "author": "Risto Stevcev", 23 | "license": "BSD-3-Clause", 24 | "devDependencies": { 25 | "bs-chai": "^0.4.0", 26 | "bs-jsverify": "^0.10.0", 27 | "bs-mocha": "^0.4.0", 28 | "bs-platform": "^4.0.0" 29 | }, 30 | "dependencies": { 31 | "bs-abstract": "^0.16.0", 32 | "bs-errors": "^0.5.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Affect.re: -------------------------------------------------------------------------------- 1 | open BsAbstract.Interface; 2 | 3 | type affect('a) = (option(Js.Exn.t) => unit, 'a => unit) => unit; 4 | 5 | let pure: 'a => affect('a) = a => (_, success) => success(a) 6 | 7 | and flat_map: (affect('a), 'a => affect('b)) => affect('b) = (callback, f) => 8 | (error, success) => callback(error, x => f(x)(error, success)); 9 | 10 | let apply: (affect('a => 'b), affect('a)) => affect('b) = (f, a) => 11 | flat_map(f, (f') => { 12 | flat_map(a, (a') => pure(f'(a'))) 13 | }); 14 | 15 | let map: ('a => 'b, affect('a)) => affect('b) = (f, a) => apply(pure(f), a) 16 | 17 | and run_affect: affect(unit) => unit = callback => callback((_) => (), () => ()) 18 | 19 | and to_effect: affect(unit) => Effect.effect(unit) = 20 | callback => callback((_) => (), () => ()) |> Effect.pure 21 | 22 | and throw: affect('a) => affect(unit) = callback => 23 | (_, _) => 24 | callback( 25 | error => 26 | switch (BsAbstract.Option.Monad.flat_map(error, Js.Exn.message)) { 27 | | Some(message) => Js.Exn.raiseError(message) 28 | | _ => () 29 | }, 30 | (_) => () 31 | ) 32 | 33 | and parallel: (affect('a), affect('b)) => affect(unit) = (aff_a, aff_b) => 34 | (error, success) => { 35 | flat_map(aff_a, (_) => pure())(error, success); 36 | flat_map(aff_b, (_) => pure())(error, success); 37 | }; 38 | 39 | let parallel': list(affect('a)) => affect(unit) = affects => 40 | (error, success) => List.iter(affect => affect(error, success), affects); 41 | 42 | [@bs.send.pipe: Js.Promise.t('a)] external promise_then_ : 43 | ('a => Js.Promise.t('b), Js.Exn.t => Js.Promise.t('b)) => Js.Promise.t('b) = "then"; 44 | 45 | let to_promise: affect('a) => Js.Promise.t('a) = callback => 46 | Js.Promise.make((~resolve, ~reject) => { 47 | callback( 48 | error => switch error { 49 | | Some(error') => [@bs] reject(BsErrors.Error.unsafe_to_exception(error')) 50 | | None => () 51 | }, 52 | success => [@bs] resolve(success) 53 | ) 54 | }) 55 | 56 | and from_promise: Js.Promise.t('a) => affect('a) = promise => 57 | (error, success) => ( 58 | promise_then_( 59 | success' => { 60 | success(success'); 61 | promise; 62 | }, 63 | error' => { 64 | error(Some(error')); 65 | promise; 66 | }, 67 | promise 68 | ) |> ignore 69 | ); 70 | 71 | module Functor: FUNCTOR with type t('a) = affect('a) = { 72 | type t('a) = affect('a); 73 | let map = map 74 | }; 75 | 76 | module Apply: APPLY with type t('a) = affect('a) = { 77 | include Functor; 78 | let apply = apply 79 | }; 80 | 81 | module Applicative: APPLICATIVE with type t('a) = affect('a) = { 82 | include Apply; 83 | let pure = pure 84 | }; 85 | 86 | module Monad: MONAD with type t('a) = affect('a) = { 87 | include Applicative; 88 | let flat_map = flat_map 89 | }; 90 | 91 | module Infix = { 92 | include BsAbstract.Infix.Monad(Monad) 93 | } 94 | -------------------------------------------------------------------------------- /src/Effect.re: -------------------------------------------------------------------------------- 1 | open BsAbstract.Interface; 2 | 3 | type effect('a) = unit => 'a; 4 | 5 | let pure: 'a => effect('a) = (a) => () => a 6 | 7 | and flat_map: (effect('a), 'a => effect('b)) => effect('b) = (a, f) => () => f(a())(); 8 | 9 | let apply: (effect('a => 'b), effect('a)) => effect('b) = (f, a) => 10 | flat_map(f, (f') => { 11 | flat_map(a, (a') => pure(f'(a'))) 12 | }); 13 | 14 | let map: ('a => 'b, effect('a)) => effect('b) = (f, a) => apply(pure(f), a) 15 | 16 | and run_effect: effect('a) => 'a = (a) => a(); 17 | 18 | module Functor: FUNCTOR with type t('a) = effect('a) = { 19 | type t('a) = effect('a); 20 | let map = map 21 | }; 22 | 23 | module Apply: APPLY with type t('a) = effect('a) = { 24 | include Functor; 25 | let apply = apply 26 | }; 27 | 28 | module Applicative: APPLICATIVE with type t('a) = effect('a) = { 29 | include Apply; 30 | let pure = pure 31 | }; 32 | 33 | module Monad: MONAD with type t('a) = effect('a) = { 34 | include Applicative; 35 | let flat_map = flat_map 36 | }; 37 | 38 | module Infix = { 39 | include BsAbstract.Infix.Monad(Monad) 40 | } 41 | -------------------------------------------------------------------------------- /test/Fs.re: -------------------------------------------------------------------------------- 1 | [@bs.val] [@bs.module "fs"] 2 | external read_file_sync : 3 | (string, ([@bs.string] [ `hex | `utf8 | `ascii ])) => string = "readFileSync"; 4 | 5 | [@bs.val] [@bs.module "fs"] 6 | external write_file_sync : 7 | (string, string, ([@bs.string] [ `hex | `utf8 | `ascii ])) => unit = "writeFileSync"; 8 | 9 | [@bs.val] [@bs.module "fs"] 10 | external read_file_async : 11 | ( string 12 | , ([@bs.string] [ `hex | `utf8 | `ascii ]) 13 | , (Js.null(Js.Exn.t), string) => unit 14 | ) => unit = "readFile"; 15 | 16 | [@bs.val] [@bs.module "fs"] 17 | external write_file_async : 18 | ( string 19 | , string 20 | , ([@bs.string] [ `hex | `utf8 | `ascii ]) 21 | , Js.null(Js.Exn.t) => unit 22 | ) => unit = "writeFile"; 23 | 24 | -------------------------------------------------------------------------------- /test/Test_Affect.re: -------------------------------------------------------------------------------- 1 | open Affect; 2 | let (describe, before) = BsMocha.Mocha.(describe, before) 3 | and fail = BsMocha.Assert.fail 4 | and it = BsMocha.Async.it; 5 | open BsChai.Expect.Expect; 6 | open BsChai.Expect.Combos.End; 7 | open BsJsverify.Verify.Arbitrary; 8 | open BsJsverify.Verify.Property; 9 | open BsAbstract; 10 | module Fn = BsAbstract.Functions.Monad(Affect.Monad); 11 | let (flip, const) = Function.(flip, const); 12 | let ((<.), (>.)) = Function.Infix.((<.), (>.)); 13 | let ((>>=), (<#>), (>=>)) = Affect.Infix.((>>=), (<#>), (>=>)); 14 | 15 | 16 | module CompareAffect = { 17 | let (>>=) = Affect.Infix.(>>=); 18 | type t('a) = affect('a); 19 | let eq = (a, b) => run_affect(a >>= (_) => pure()) == run_affect(b >>= (_) => pure()) 20 | }; 21 | 22 | let dirname : option(string) = [%bs.node __dirname]; 23 | let dirname' = Js.Option.getWithDefault(".", dirname); 24 | 25 | let read_file : string => affect(string) = path => (error, success) => 26 | Fs.read_file_async(path, `utf8, (err, content) => { 27 | err != Js.null ? error(Js.Null.toOption(err)) : success(content) 28 | }) 29 | 30 | and read_file' : string => Js.Promise.t(string) = path => 31 | Js.Promise.make((~resolve, ~reject) => { 32 | Fs.read_file_async(path, `utf8, (err, content) => { 33 | switch (Js.Null.toOption(err), content) { 34 | | (Some(err'), _) => [@bs] reject(BsErrors.Error.unsafe_to_exception(err')) 35 | | (_, content) => [@bs] resolve(content) 36 | } 37 | }) 38 | }) 39 | 40 | and write_file : (string, string) => affect(unit) = (path, content) => (error, success) => 41 | Fs.write_file_async(path, content, `utf8, err => { 42 | err != Js.null ? error(Js.Null.toOption(err)) : success() 43 | }) 44 | 45 | and delay : int => affect(unit) = ms => (_, success) => 46 | Js.Global.setTimeout(() => success(), ms) |> ignore; 47 | 48 | 49 | describe("Affect", () => { 50 | before(() => Fs.write_file_sync("sample", "hello", `utf8)); 51 | 52 | describe("Sanity check", () => { 53 | describe("Monoid in the category of endofunctors", () => { 54 | property1("should satisfy associativity and identity", arb_nat, x => { 55 | module Monoid: BsAbstract.Interface.MONOID with type t = int => affect(int) = { 56 | type t = int => affect(int); 57 | let append = (>=>) 58 | and empty = a => Affect.Monad.pure(a) 59 | }; 60 | module Compare: BsAbstract.Interface.EQ with type t = int => affect(int) = { 61 | type t = int => affect(int); 62 | let eq = (a, b) => run_affect(a(x) >>= (_) => pure()) == run_affect(b(x) >>= (_) => pure()) 63 | }; 64 | 65 | module Verify_Semigroup = Verify.Compare.Semigroup(Monoid, Compare); 66 | module Verify_Monoid = Verify.Compare.Monoid(Monoid, Compare); 67 | Verify_Semigroup.associativity(pure <. (*)(3), pure <. (+)(2), pure <. (-)(1)) && 68 | Verify_Monoid.identity(pure) 69 | }) 70 | }); 71 | 72 | describe("Chaining effects", () => { 73 | it("should chain the async effects correctly", done_ => { 74 | read_file("sample") 75 | >>= (content => write_file("sample", content ++ " world!")) 76 | >>= ((_) => read_file("sample")) 77 | >>= (content => { 78 | expect(content) |> to_be("hello world!"); 79 | done_() |> pure 80 | }) 81 | |> run_affect 82 | }) 83 | }); 84 | 85 | describe("Throwing effects", () => { 86 | it("should not throw if the async effect didn't fail", done_ => { 87 | try ({ 88 | Affect.throw(read_file("sample")) |> ignore; 89 | /* Successfully didn't throw */ 90 | done_() 91 | }) { | Js.Exn.Error(_) => fail("Async effect should not have failed") } 92 | }); 93 | 94 | it("should throw if the async effect failed", done_ => { 95 | try ({ 96 | Affect.throw(read_file("non-existent-name")) |> ignore; 97 | /* Did not throw */ 98 | fail("Async effect should have failed"); 99 | done_() 100 | }) { | Js.Exn.Error(_) => done_() } 101 | }) 102 | }); 103 | 104 | describe("Parallel effects", () => { 105 | it("should run effects in parallel (parallel)", done_ => { 106 | let x = ref("") and y = ref(0); 107 | let delay_ms = 500; 108 | let delay_x = delay(delay_ms) >>= () => { x := "foo"; pure(x^) } 109 | and delay_y = delay(delay_ms) >>= () => { y := 123; pure(y^) }; 110 | 111 | parallel(delay_x, delay_y) >>= (() => pure()) |> run_affect; 112 | 113 | Js.Global.setTimeout(() => { 114 | expect(x^) |> to_be("foo"); 115 | expect(y^) |> to_be(123); 116 | done_() 117 | }, delay_ms) |> ignore 118 | }); 119 | it("should run effects in parallel (parallel')", done_ => { 120 | let x = ref("") and y = ref(""); 121 | let delay_ms = 500; 122 | let delay_x = delay(delay_ms) >>= () => { x := "foo"; pure() } 123 | and delay_y = delay(delay_ms) >>= () => { y := "bar"; pure() }; 124 | 125 | parallel'([delay_x, delay_y]) |> run_affect; 126 | 127 | Js.Global.setTimeout(() => { 128 | expect(x^) |> to_be("foo"); 129 | expect(y^) |> to_be("bar"); 130 | done_() 131 | }, delay_ms) |> ignore 132 | }) 133 | }) 134 | }); 135 | 136 | describe("Functor", () => { 137 | module V = BsAbstract.Verify.Compare.Functor(Affect.Functor, CompareAffect); 138 | property1("should satisfy identity", arb_nat, pure >. V.identity); 139 | property1("should satisfy composition", arb_nat, a => { 140 | V.composition((++)("!"), string_of_int, pure(a)) 141 | }) 142 | }); 143 | 144 | describe("Apply", () => { 145 | module V = Verify.Compare.Apply(Affect.Monad, CompareAffect); 146 | property1( 147 | "should satisfy associative composition", 148 | arb_nat, 149 | pure >. V.associative_composition(pure((++)("!")), pure(string_of_int)) 150 | ) 151 | }); 152 | 153 | describe("Applicative", () => { 154 | module V = Verify.Compare.Applicative(Affect.Applicative, CompareAffect); 155 | property1("should satisfy identity", arb_nat, pure >. V.identity); 156 | property1( 157 | "should satisfy homomorphism", 158 | arb_array(arb_nat), 159 | V.homomorphism(Array.Functor.map(string_of_int)) 160 | ); 161 | property1("should satisfy interchange", arb_nat, V.interchange(pure(string_of_int))) 162 | }); 163 | 164 | describe("Monad", () => { 165 | module V = Verify.Compare.Monad(Affect.Monad, CompareAffect); 166 | property1( 167 | "should satisfy associativity", 168 | arb_nat, 169 | pure >. V.associativity(pure <. string_of_int, pure <. (++)("!")) 170 | ); 171 | property1("should satisfy identity", arb_nat, V.identity(pure <. string_of_int)) 172 | }); 173 | 174 | describe("to_promise", () => { 175 | it("should convert to a promise", done_ => { 176 | read_file("sample") 177 | |> to_promise 178 | |> Js.Promise.then_(result => { 179 | expect(result) |> to_be("hello world!"); 180 | Js.Promise.resolve(done_()) 181 | }) 182 | |> ignore 183 | }) 184 | }); 185 | 186 | describe("from_promise", () => { 187 | it("should convert from a promise", done_ => { 188 | read_file'("sample") 189 | |> from_promise 190 | >>= (result => { 191 | expect(result) |> to_be("hello world!"); 192 | pure(done_()) 193 | }) 194 | |> run_affect 195 | }) 196 | }) 197 | }) 198 | -------------------------------------------------------------------------------- /test/Test_Effect.re: -------------------------------------------------------------------------------- 1 | open Effect; 2 | open BsMocha.Mocha; 3 | open BsChai.Expect.Expect; 4 | open BsChai.Expect.Combos.End; 5 | open BsJsverify.Verify.Arbitrary; 6 | open BsJsverify.Verify.Property; 7 | open BsAbstract; 8 | let flip = Function.flip; 9 | let ((<.), (>.)) = Function.Infix.((<.), (>.)); 10 | let ((>>=), (<#>), (>=>)) = Effect.Infix.((>>=), (<#>), (>=>)); 11 | 12 | let read_file : string => effect(string) = path => () => 13 | Fs.read_file_sync(path, `utf8) 14 | 15 | and write_file : (string, string) => effect(unit) = (path, content) => () => 16 | Fs.write_file_sync(path, content, `utf8); 17 | 18 | describe("Effect", () => { 19 | before(() => Fs.write_file_sync("sample", "hello", `utf8)); 20 | 21 | let pure = Effect.Applicative.pure; 22 | 23 | module CompareEffect = { 24 | type t('a) = effect('a); 25 | let eq = (a, b) => run_effect(a) == run_effect(b) 26 | }; 27 | 28 | describe("Sanity check", () => { 29 | describe("Monoid in the category of endofunctors", () => { 30 | property1("should satisfy associativity and identity", arb_nat, x => { 31 | module Monoid: BsAbstract.Interface.MONOID with type t = int => effect(int) = { 32 | type t = int => effect(int); 33 | let append = (>=>) 34 | and empty = a => Effect.Monad.pure(a) 35 | }; 36 | module Compare: BsAbstract.Interface.EQ with type t = int => effect(int) = { 37 | type t = int => effect(int); 38 | let eq = (a, b) => run_effect(a(x)) == run_effect(b(x)) 39 | }; 40 | module Verify_Semigroup = Verify.Compare.Semigroup(Monoid, Compare); 41 | module Verify_Monoid = Verify.Compare.Monoid(Monoid, Compare); 42 | 43 | Verify_Semigroup.associativity(pure <. (*)(3), pure <. (+)(2), pure <. (-)(1)) && 44 | Verify_Monoid.identity(pure) 45 | }) 46 | }); 47 | 48 | describe("Example", () => { 49 | it("should read and write from files synchronously", () => { 50 | read_file("sample") 51 | >>= (content => write_file("sample", content ++ " world!")) 52 | >>= ((_) => read_file("sample")) 53 | >>= (content => expect(content) |> to_be("hello world!") |> pure) 54 | |> run_effect 55 | }); 56 | 57 | it("should flat_map correctly", () => { 58 | expect( 59 | pure(123) 60 | >>= (a => a == 123 ? pure(456) : pure(1)) 61 | >>= (b => b == 456 ? pure("foo") : pure("bar")) 62 | <#> flip((++))("!") 63 | |> run_effect 64 | ) 65 | |> to_be("foo!") 66 | }); 67 | 68 | it("should be idempotent", () => { 69 | let x = ref(123); 70 | let add_to_x: int => effect(int) = n => () => { x := x^ + n; x^ }; 71 | 72 | /* Calling it multiple times returns the same result -- the effect isn't run */ 73 | for (i in 0 to 10) { 74 | add_to_x(i) |> ignore; 75 | }; 76 | let result = add_to_x(0); 77 | expect(run_effect(result)) |> to_be(123); 78 | 79 | /* Doing a flat_map will chain the effects together */ 80 | expect(run_effect(pure(x) >>= x => add_to_x(x^))) |> to_be(246) 81 | }) 82 | }) 83 | }); 84 | 85 | describe("Functor", () => { 86 | module V = BsAbstract.Verify.Compare.Functor(Effect.Functor, CompareEffect); 87 | property1("should satisfy identity", arb_nat, pure >. V.identity); 88 | property1("should satisfy composition", arb_nat, a => { 89 | V.composition((++)("!"), string_of_int, pure(a)) 90 | }) 91 | }); 92 | 93 | describe("Apply", () => { 94 | module V = Verify.Compare.Apply(Effect.Monad, CompareEffect); 95 | property1( 96 | "should satisfy associative composition", 97 | arb_nat, 98 | pure >. V.associative_composition(pure((++)("!")), pure(string_of_int)) 99 | ) 100 | }); 101 | 102 | describe("Applicative", () => { 103 | module V = Verify.Compare.Applicative(Effect.Applicative, CompareEffect); 104 | property1("should satisfy identity", arb_nat, pure >. V.identity); 105 | property1( 106 | "should satisfy homomorphism", 107 | arb_array(arb_nat), 108 | V.homomorphism(Array.Functor.map(string_of_int)) 109 | ); 110 | property1("should satisfy interchange", arb_nat, V.interchange(pure(string_of_int))) 111 | }); 112 | 113 | describe("Monad", () => { 114 | module V = Verify.Compare.Monad(Effect.Monad, CompareEffect); 115 | property1( 116 | "should satisfy associativity", 117 | arb_nat, 118 | pure >. V.associativity(pure <. string_of_int, pure <. (++)("!")) 119 | ); 120 | property1("should satisfy identity", arb_nat, V.identity(pure <. string_of_int)) 121 | }) 122 | }); 123 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | assertion-error@^1.0.1: 6 | version "1.1.0" 7 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 8 | 9 | balanced-match@^1.0.0: 10 | version "1.0.0" 11 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 12 | 13 | brace-expansion@^1.1.7: 14 | version "1.1.8" 15 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 16 | dependencies: 17 | balanced-match "^1.0.0" 18 | concat-map "0.0.1" 19 | 20 | browser-stdout@1.3.0: 21 | version "1.3.0" 22 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 23 | 24 | browser-stdout@1.3.1: 25 | version "1.3.1" 26 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 27 | 28 | bs-abstract@^0.16.0: 29 | version "0.16.0" 30 | resolved "https://registry.yarnpkg.com/bs-abstract/-/bs-abstract-0.16.0.tgz#cb214e0b45710c28562ddb671a736b084b8aaf86" 31 | 32 | bs-chai@^0.4.0: 33 | version "0.4.0" 34 | resolved "https://registry.yarnpkg.com/bs-chai/-/bs-chai-0.4.0.tgz#3f1b334c135e613a884160cbb57044c42f0b0a0e" 35 | dependencies: 36 | chai "^4.1.2" 37 | mocha "^5.0.1" 38 | 39 | bs-errors@^0.5.0: 40 | version "0.5.0" 41 | resolved "https://registry.yarnpkg.com/bs-errors/-/bs-errors-0.5.0.tgz#8e9d907ac4c6dff1b49232428acb4085c63053b8" 42 | 43 | bs-jsverify@^0.10.0: 44 | version "0.10.0" 45 | resolved "https://registry.yarnpkg.com/bs-jsverify/-/bs-jsverify-0.10.0.tgz#caca0254669fe197599f2bcca074874b5fce1215" 46 | dependencies: 47 | jsverify "^0.8.3" 48 | 49 | bs-mocha@^0.4.0: 50 | version "0.4.0" 51 | resolved "https://registry.yarnpkg.com/bs-mocha/-/bs-mocha-0.4.0.tgz#11fc890a1faee974e0d21d72c8c1fd3dc56f552b" 52 | dependencies: 53 | mocha "^5.0.0" 54 | 55 | bs-platform@^4.0.0: 56 | version "4.0.3" 57 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-4.0.3.tgz#4510c1c915cc5b169b5717e5c0ada52d3f99ff98" 58 | 59 | chai@^4.1.2: 60 | version "4.1.2" 61 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" 62 | dependencies: 63 | assertion-error "^1.0.1" 64 | check-error "^1.0.1" 65 | deep-eql "^3.0.0" 66 | get-func-name "^2.0.0" 67 | pathval "^1.0.0" 68 | type-detect "^4.0.0" 69 | 70 | check-error@^1.0.1: 71 | version "1.0.2" 72 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 73 | 74 | commander@2.11.0: 75 | version "2.11.0" 76 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" 77 | 78 | commander@2.15.1: 79 | version "2.15.1" 80 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" 81 | 82 | concat-map@0.0.1: 83 | version "0.0.1" 84 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 85 | 86 | debug@3.1.0: 87 | version "3.1.0" 88 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 89 | dependencies: 90 | ms "2.0.0" 91 | 92 | deep-eql@^3.0.0: 93 | version "3.0.1" 94 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 95 | dependencies: 96 | type-detect "^4.0.0" 97 | 98 | diff@3.3.1: 99 | version "3.3.1" 100 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" 101 | 102 | diff@3.5.0: 103 | version "3.5.0" 104 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 105 | 106 | escape-string-regexp@1.0.5: 107 | version "1.0.5" 108 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 109 | 110 | fs.realpath@^1.0.0: 111 | version "1.0.0" 112 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 113 | 114 | get-func-name@^2.0.0: 115 | version "2.0.0" 116 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 117 | 118 | glob@7.1.2: 119 | version "7.1.2" 120 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 121 | dependencies: 122 | fs.realpath "^1.0.0" 123 | inflight "^1.0.4" 124 | inherits "2" 125 | minimatch "^3.0.4" 126 | once "^1.3.0" 127 | path-is-absolute "^1.0.0" 128 | 129 | growl@1.10.3: 130 | version "1.10.3" 131 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" 132 | 133 | growl@1.10.5: 134 | version "1.10.5" 135 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" 136 | 137 | has-flag@^2.0.0: 138 | version "2.0.0" 139 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" 140 | 141 | has-flag@^3.0.0: 142 | version "3.0.0" 143 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 144 | 145 | he@1.1.1: 146 | version "1.1.1" 147 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 148 | 149 | inflight@^1.0.4: 150 | version "1.0.6" 151 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 152 | dependencies: 153 | once "^1.3.0" 154 | wrappy "1" 155 | 156 | inherits@2: 157 | version "2.0.3" 158 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 159 | 160 | jsverify@^0.8.3: 161 | version "0.8.3" 162 | resolved "https://registry.yarnpkg.com/jsverify/-/jsverify-0.8.3.tgz#02e9178e1924b5aaf959c0239af84ed5c6d0738c" 163 | dependencies: 164 | lazy-seq "^1.0.0" 165 | rc4 "~0.1.5" 166 | trampa "^1.0.0" 167 | typify-parser "^1.1.0" 168 | 169 | lazy-seq@^1.0.0: 170 | version "1.0.0" 171 | resolved "https://registry.yarnpkg.com/lazy-seq/-/lazy-seq-1.0.0.tgz#880cb8aab256026382e02f53ec089682a74c5b6a" 172 | 173 | minimatch@3.0.4, minimatch@^3.0.4: 174 | version "3.0.4" 175 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 176 | dependencies: 177 | brace-expansion "^1.1.7" 178 | 179 | minimist@0.0.8: 180 | version "0.0.8" 181 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 182 | 183 | mkdirp@0.5.1: 184 | version "0.5.1" 185 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 186 | dependencies: 187 | minimist "0.0.8" 188 | 189 | mocha@^5.0.0: 190 | version "5.0.1" 191 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.0.1.tgz#759b62c836b0732382a62b6b1fb245ec1bc943ac" 192 | dependencies: 193 | browser-stdout "1.3.0" 194 | commander "2.11.0" 195 | debug "3.1.0" 196 | diff "3.3.1" 197 | escape-string-regexp "1.0.5" 198 | glob "7.1.2" 199 | growl "1.10.3" 200 | he "1.1.1" 201 | mkdirp "0.5.1" 202 | supports-color "4.4.0" 203 | 204 | mocha@^5.0.1: 205 | version "5.2.0" 206 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" 207 | dependencies: 208 | browser-stdout "1.3.1" 209 | commander "2.15.1" 210 | debug "3.1.0" 211 | diff "3.5.0" 212 | escape-string-regexp "1.0.5" 213 | glob "7.1.2" 214 | growl "1.10.5" 215 | he "1.1.1" 216 | minimatch "3.0.4" 217 | mkdirp "0.5.1" 218 | supports-color "5.4.0" 219 | 220 | ms@2.0.0: 221 | version "2.0.0" 222 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 223 | 224 | once@^1.3.0: 225 | version "1.4.0" 226 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 227 | dependencies: 228 | wrappy "1" 229 | 230 | path-is-absolute@^1.0.0: 231 | version "1.0.1" 232 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 233 | 234 | pathval@^1.0.0: 235 | version "1.1.0" 236 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 237 | 238 | rc4@~0.1.5: 239 | version "0.1.5" 240 | resolved "https://registry.yarnpkg.com/rc4/-/rc4-0.1.5.tgz#08c6e04a0168f6eb621c22ab6cb1151bd9f4a64d" 241 | 242 | supports-color@4.4.0: 243 | version "4.4.0" 244 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" 245 | dependencies: 246 | has-flag "^2.0.0" 247 | 248 | supports-color@5.4.0: 249 | version "5.4.0" 250 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 251 | dependencies: 252 | has-flag "^3.0.0" 253 | 254 | trampa@^1.0.0: 255 | version "1.0.0" 256 | resolved "https://registry.yarnpkg.com/trampa/-/trampa-1.0.0.tgz#5247347ac334807fa6c0000444cb91b639840ad5" 257 | 258 | type-detect@^4.0.0: 259 | version "4.0.8" 260 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 261 | 262 | typify-parser@^1.1.0: 263 | version "1.1.0" 264 | resolved "https://registry.yarnpkg.com/typify-parser/-/typify-parser-1.1.0.tgz#ac73bfa5f25343468e2d0f3346c6117bc03d3c99" 265 | 266 | wrappy@1: 267 | version "1.0.2" 268 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 269 | --------------------------------------------------------------------------------