├── .config ├── .eslintrc.json ├── .gitignore ├── .npmrc ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── figures ├── dependencies.dot └── dependencies.sh ├── implementations.md ├── index.d.ts ├── index.js ├── index.mjs ├── logo.png ├── names ├── package.json ├── scripts ├── generate-es ├── generate-js ├── generate-readme ├── generate-ts └── lint └── test ├── index.js └── mocha.opts /.config: -------------------------------------------------------------------------------- 1 | repo-owner = fantasyland 2 | repo-name = fantasy-land 3 | author-name = Fantasy Land 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["./node_modules/sanctuary-style/eslint-es3.json"], 4 | "overrides": [ 5 | { 6 | "files": "README.md", 7 | "rules": { 8 | "func-call-spacing": ["error", "never"], 9 | "max-len": ["off"], 10 | "no-undef": ["off"], 11 | "no-unused-vars": ["off"] 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.nyc_output/ 2 | /coverage/ 3 | /node_modules/ 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "6" 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Additions and modifications to the specification are best proposed in issues. 4 | If there is support for a proposal, the next step is to submit a pull request. 5 | 6 | When adding a type class to __README.md__, please update __names__ then run: 7 | 8 | ```console 9 | $ npm run generate-js 10 | $ npm run generate-ts 11 | $ npm run generate-es 12 | ``` 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Fantasy Land 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fantasy Land Specification 2 | 3 | [![Build Status](https://travis-ci.org/fantasyland/fantasy-land.svg?branch=master)](https://travis-ci.org/fantasyland/fantasy-land) [![Join the chat at https://gitter.im/fantasyland/fantasy-land](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fantasyland/fantasy-land?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | (aka "Algebraic JavaScript Specification") 6 | 7 | 8 | 9 | This project specifies interoperability of common algebraic 10 | structures: 11 | 12 |
 13 |  Setoid   Semigroupoid  Semigroup   Foldable        Functor      Contravariant  Filterable
 14 | (equals)    (compose)    (concat)   (reduce)         (map)        (contramap)    (filter)
 15 |     |           |           |           \         / | | | | \
 16 |     |           |           |            \       /  | | | |  \
 17 |     |           |           |             \     /   | | | |   \
 18 |     |           |           |              \   /    | | | |    \
 19 |     |           |           |               \ /     | | | |     \
 20 |    Ord      Category     Monoid         Traversable | | | |      \
 21 |   (lte)       (id)       (empty)        (traverse)  / | | \       \
 22 |                             |                      /  | |  \       \
 23 |                             |                     /   / \   \       \
 24 |                             |             Profunctor /   \ Bifunctor \
 25 |                             |              (promap) /     \ (bimap)   \
 26 |                             |                      /       \           \
 27 |                           Group                   /         \           \
 28 |                          (invert)               Alt        Apply      Extend
 29 |                                                (alt)        (ap)     (extend)
 30 |                                                 /           / \           \
 31 |                                                /           /   \           \
 32 |                                               /           /     \           \
 33 |                                              /           /       \           \
 34 |                                             /           /         \           \
 35 |                                           Plus    Applicative    Chain      Comonad
 36 |                                          (zero)       (of)      (chain)    (extract)
 37 |                                             \         / \         / \
 38 |                                              \       /   \       /   \
 39 |                                               \     /     \     /     \
 40 |                                                \   /       \   /       \
 41 |                                                 \ /         \ /         \
 42 |                                             Alternative    Monad     ChainRec
 43 |                                                                     (chainRec)
 44 | 
45 | 46 | ## General 47 | 48 | An algebra is a set of values, a set of operators that it is closed 49 | under and some laws it must obey. 50 | 51 | Each Fantasy Land algebra is a separate specification. An algebra may 52 | have dependencies on other algebras which must be implemented. 53 | 54 | ## Terminology 55 | 56 | 1. "value" is any JavaScript value, including any which have the 57 | structures defined below. 58 | 2. "equivalent" is an appropriate definition of equivalence for the given value. 59 | The definition should ensure that the two values can be safely swapped out in a program that respects abstractions. For example: 60 | - Two lists are equivalent if they are equivalent at all indices. 61 | - Two plain old JavaScript objects, interpreted as dictionaries, are equivalent when they are equivalent for all keys. 62 | - Two promises are equivalent when they yield equivalent values. 63 | - Two functions are equivalent if they yield equivalent outputs for equivalent inputs. 64 | 65 | ## Type signature notation 66 | 67 | The type signature notation used in this document is described below:[1](#sanctuary-types) 69 | 70 | * `::` _"is a member of"._ 71 | - `e :: t` can be read as: "the expression `e` is a member of type `t`". 72 | - `true :: Boolean` - "`true` is a member of type `Boolean`". 73 | - `42 :: Integer, Number` - "`42` is a member of the `Integer` and 74 | `Number` types". 75 | * _New types can be created via type constructors._ 76 | - Type constructors can take zero or more type arguments. 77 | - `Array` is a type constructor which takes one type argument. 78 | - `Array String` is the type of all arrays of strings. Each of the 79 | following has type `Array String`: `[]`, `['foo', 'bar', 'baz']`. 80 | - `Array (Array String)` is the type of all arrays of arrays of strings. 81 | Each of the following has type `Array (Array String)`: `[]`, `[ [], [] 82 | ]`, `[ [], ['foo'], ['bar', 'baz'] ]`. 83 | * _Lowercase letters stand for type variables._ 84 | - Type variables can take any type unless they have been restricted by 85 | means of type constraints (see fat arrow below). 86 | * `->` (arrow) _Function type constructor._ 87 | - `->` is an _infix_ type constructor that takes two type arguments where 88 | left argument is the input type and the right argument is the output type. 89 | - `->`'s input type can be a grouping of types to create the type of a 90 | function which accepts zero or more arguments. The syntax is: 91 | `() -> `, where `` comprises zero 92 | or more comma–space (`, `)-separated type representations and parens 93 | may be omitted for unary functions. 94 | - `String -> Array String` is a type satisfied by functions which take a 95 | `String` and return an `Array String`. 96 | - `String -> Array String -> Array String` is a type satisfied by functions 97 | which take a `String` and return a function which takes an `Array String` 98 | and returns an `Array String`. 99 | - `(String, Array String) -> Array String` is a type satisfied by functions 100 | which take a `String` and an `Array String` as arguments and return an 101 | `Array String`. 102 | - `() -> Number` is a type satisfied by functions 103 | which do not take arguments and return a `Number`. 104 | * `~>` (squiggly arrow) _Method type constructor._ 105 | - When a function is a property of an Object, it is called a method. All 106 | methods have an implicit parameter type - the type of which they are a 107 | property. 108 | - `a ~> a -> a` is a type satisfied by methods on Objects of type `a` which 109 | take a type `a` as an argument and return a value of type `a`. 110 | * `=>` (fat arrow) _Expresses constraints on type variables._ 111 | - In `a ~> a -> a` (see squiggly arrow above), `a` can be of any type. 112 | `Semigroup a => a ~> a -> a` adds a constraint such that the type `a` 113 | must now satisfy the `Semigroup` typeclass. To satisfy a typeclass means 114 | to lawfully implement all functions/methods specified by that typeclass. 115 | 116 | For example: 117 | 118 | ``` 119 | fantasy-land/traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b) 120 | '-------------------' '--------------------------' '-' '-------------------' '-----' 121 | ' ' ' ' ' 122 | ' ' - type constraints ' ' - argument types ' - return type 123 | ' ' 124 | '- method name ' - method target type 125 | ``` 126 | 127 | - - - 128 | 1. See the [Types](https://sanctuary.js.org/#types) 129 | section in Sanctuary's docs for more info. [↩](#sanctuary-types-return) 130 | 131 | ## Type representatives 132 | 133 | Certain behaviours are defined from the perspective of a member of a type. 134 | Other behaviours do not require a member. Thus certain algebras require a 135 | type to provide a value-level representative (with certain properties). The 136 | Identity type, for example, could provide `Id` as its type representative: 137 | `Id :: TypeRep Identity`. 138 | 139 | If a type provides a type representative, each member of the type must have 140 | a `constructor` property which is a reference to the type representative. 141 | 142 | ## Algebras 143 | 144 | ### Setoid 145 | 146 | 1. `a['fantasy-land/equals'](a) === true` (reflexivity) 147 | 2. `a['fantasy-land/equals'](b) === b['fantasy-land/equals'](a)` (symmetry) 148 | 3. If `a['fantasy-land/equals'](b)` and `b['fantasy-land/equals'](c)`, then `a['fantasy-land/equals'](c)` (transitivity) 149 | 150 | 151 | 152 | #### `fantasy-land/equals` method 153 | 154 | ```hs 155 | fantasy-land/equals :: Setoid a => a ~> a -> Boolean 156 | ``` 157 | 158 | A value which has a Setoid must provide a `fantasy-land/equals` method. The 159 | `fantasy-land/equals` method takes one argument: 160 | 161 | a['fantasy-land/equals'](b) 162 | 163 | 1. `b` must be a value of the same Setoid 164 | 165 | 1. If `b` is not the same Setoid, behaviour of `fantasy-land/equals` is 166 | unspecified (returning `false` is recommended). 167 | 168 | 2. `fantasy-land/equals` must return a boolean (`true` or `false`). 169 | 170 | ### Ord 171 | 172 | A value that implements the Ord specification must also implement 173 | the [Setoid](#setoid) specification. 174 | 175 | 1. `a['fantasy-land/lte'](b)` or `b['fantasy-land/lte'](a)` (totality) 176 | 2. If `a['fantasy-land/lte'](b)` and `b['fantasy-land/lte'](a)`, then `a['fantasy-land/equals'](b)` (antisymmetry) 177 | 3. If `a['fantasy-land/lte'](b)` and `b['fantasy-land/lte'](c)`, then `a['fantasy-land/lte'](c)` (transitivity) 178 | 179 | 180 | 181 | #### `fantasy-land/lte` method 182 | 183 | ```hs 184 | fantasy-land/lte :: Ord a => a ~> a -> Boolean 185 | ``` 186 | 187 | A value which has an Ord must provide a `fantasy-land/lte` method. The 188 | `fantasy-land/lte` method takes one argument: 189 | 190 | a['fantasy-land/lte'](b) 191 | 192 | 1. `b` must be a value of the same Ord 193 | 194 | 1. If `b` is not the same Ord, behaviour of `fantasy-land/lte` is 195 | unspecified (returning `false` is recommended). 196 | 197 | 2. `fantasy-land/lte` must return a boolean (`true` or `false`). 198 | 199 | ### Semigroupoid 200 | 201 | 1. `a['fantasy-land/compose'](b)['fantasy-land/compose'](c) === a['fantasy-land/compose'](b['fantasy-land/compose'](c))` (associativity) 202 | 203 | 204 | 205 | #### `fantasy-land/compose` method 206 | 207 | ```hs 208 | fantasy-land/compose :: Semigroupoid c => c i j ~> c j k -> c i k 209 | ``` 210 | 211 | A value which has a Semigroupoid must provide a `fantasy-land/compose` method. The 212 | `fantasy-land/compose` method takes one argument: 213 | 214 | a['fantasy-land/compose'](b) 215 | 216 | 1. `b` must be a value of the same Semigroupoid 217 | 218 | 1. If `b` is not the same semigroupoid, behaviour of `fantasy-land/compose` is 219 | unspecified. 220 | 221 | 2. `fantasy-land/compose` must return a value of the same Semigroupoid. 222 | 223 | ### Category 224 | 225 | A value that implements the Category specification must also implement 226 | the [Semigroupoid](#semigroupoid) specification. 227 | 228 | 1. `a['fantasy-land/compose'](C['fantasy-land/id']())` is equivalent to `a` (right identity) 229 | 2. `C['fantasy-land/id']()['fantasy-land/compose'](a)` is equivalent to `a` (left identity) 230 | 231 | 232 | 233 | #### `fantasy-land/id` method 234 | 235 | ```hs 236 | fantasy-land/id :: Category c => () -> c a a 237 | ``` 238 | 239 | A value which has a Category must provide a `fantasy-land/id` function on its 240 | [type representative](#type-representatives): 241 | 242 | C['fantasy-land/id']() 243 | 244 | Given a value `c`, one can access its type representative via the 245 | `constructor` property: 246 | 247 | c.constructor['fantasy-land/id']() 248 | 249 | 1. `fantasy-land/id` must return a value of the same Category 250 | 251 | ### Semigroup 252 | 253 | 1. `a['fantasy-land/concat'](b)['fantasy-land/concat'](c)` is equivalent to `a['fantasy-land/concat'](b['fantasy-land/concat'](c))` (associativity) 254 | 255 | 256 | 257 | #### `fantasy-land/concat` method 258 | 259 | ```hs 260 | fantasy-land/concat :: Semigroup a => a ~> a -> a 261 | ``` 262 | 263 | A value which has a Semigroup must provide a `fantasy-land/concat` method. The 264 | `fantasy-land/concat` method takes one argument: 265 | 266 | s['fantasy-land/concat'](b) 267 | 268 | 1. `b` must be a value of the same Semigroup 269 | 270 | 1. If `b` is not the same semigroup, behaviour of `fantasy-land/concat` is 271 | unspecified. 272 | 273 | 2. `fantasy-land/concat` must return a value of the same Semigroup. 274 | 275 | ### Monoid 276 | 277 | A value that implements the Monoid specification must also implement 278 | the [Semigroup](#semigroup) specification. 279 | 280 | 1. `m['fantasy-land/concat'](M['fantasy-land/empty']())` is equivalent to `m` (right identity) 281 | 2. `M['fantasy-land/empty']()['fantasy-land/concat'](m)` is equivalent to `m` (left identity) 282 | 283 | 284 | 285 | #### `fantasy-land/empty` method 286 | 287 | ```hs 288 | fantasy-land/empty :: Monoid m => () -> m 289 | ``` 290 | 291 | A value which has a Monoid must provide a `fantasy-land/empty` function on its 292 | [type representative](#type-representatives): 293 | 294 | M['fantasy-land/empty']() 295 | 296 | Given a value `m`, one can access its type representative via the 297 | `constructor` property: 298 | 299 | m.constructor['fantasy-land/empty']() 300 | 301 | 1. `fantasy-land/empty` must return a value of the same Monoid 302 | 303 | ### Group 304 | 305 | A value that implements the Group specification must also implement 306 | the [Monoid](#monoid) specification. 307 | 308 | 1. `g['fantasy-land/concat'](g['fantasy-land/invert']())` is equivalent to `g.constructor['fantasy-land/empty']()` (right inverse) 309 | 2. `g['fantasy-land/invert']()['fantasy-land/concat'](g)` is equivalent to `g.constructor['fantasy-land/empty']()` (left inverse) 310 | 311 | 312 | 313 | #### `fantasy-land/invert` method 314 | 315 | ```hs 316 | fantasy-land/invert :: Group g => g ~> () -> g 317 | ``` 318 | 319 | A value which has a Group must provide a `fantasy-land/invert` method. The 320 | `fantasy-land/invert` method takes no arguments: 321 | 322 | g['fantasy-land/invert']() 323 | 324 | 1. `fantasy-land/invert` must return a value of the same Group. 325 | 326 | ### Filterable 327 | 328 | 1. `v['fantasy-land/filter'](x => p(x) && q(x))` is equivalent to `v['fantasy-land/filter'](p)['fantasy-land/filter'](q)` (distributivity) 329 | 2. `v['fantasy-land/filter'](x => true)` is equivalent to `v` (identity) 330 | 3. `v['fantasy-land/filter'](x => false)` is equivalent to `w['fantasy-land/filter'](x => false)` 331 | if `v` and `w` are values of the same Filterable (annihilation) 332 | 333 | 334 | 335 | #### `fantasy-land/filter` method 336 | 337 | ```hs 338 | fantasy-land/filter :: Filterable f => f a ~> (a -> Boolean) -> f a 339 | ``` 340 | 341 | A value which has a Filterable must provide a `fantasy-land/filter` method. The `fantasy-land/filter` 342 | method takes one argument: 343 | 344 | v['fantasy-land/filter'](p) 345 | 346 | 1. `p` must be a function. 347 | 348 | 1. If `p` is not a function, the behaviour of `fantasy-land/filter` is unspecified. 349 | 2. `p` must return either `true` or `false`. If it returns any other value, 350 | the behaviour of `fantasy-land/filter` is unspecified. 351 | 352 | 2. `fantasy-land/filter` must return a value of the same Filterable. 353 | 354 | ### Functor 355 | 356 | 1. `u['fantasy-land/map'](a => a)` is equivalent to `u` (identity) 357 | 2. `u['fantasy-land/map'](x => f(g(x)))` is equivalent to `u['fantasy-land/map'](g)['fantasy-land/map'](f)` (composition) 358 | 359 | 360 | 361 | #### `fantasy-land/map` method 362 | 363 | ```hs 364 | fantasy-land/map :: Functor f => f a ~> (a -> b) -> f b 365 | ``` 366 | 367 | A value which has a Functor must provide a `fantasy-land/map` method. The `fantasy-land/map` 368 | method takes one argument: 369 | 370 | u['fantasy-land/map'](f) 371 | 372 | 1. `f` must be a function, 373 | 374 | 1. If `f` is not a function, the behaviour of `fantasy-land/map` is 375 | unspecified. 376 | 2. `f` can return any value. 377 | 3. No parts of `f`'s return value should be checked. 378 | 379 | 2. `fantasy-land/map` must return a value of the same Functor 380 | 381 | ### Contravariant 382 | 383 | 1. `u['fantasy-land/contramap'](a => a)` is equivalent to `u` (identity) 384 | 2. `u['fantasy-land/contramap'](x => f(g(x)))` is equivalent to `u['fantasy-land/contramap'](f)['fantasy-land/contramap'](g)` 385 | (composition) 386 | 387 | 388 | 389 | #### `fantasy-land/contramap` method 390 | 391 | ```hs 392 | fantasy-land/contramap :: Contravariant f => f a ~> (b -> a) -> f b 393 | ``` 394 | 395 | A value which has a Contravariant must provide a `fantasy-land/contramap` method. The 396 | `fantasy-land/contramap` method takes one argument: 397 | 398 | u['fantasy-land/contramap'](f) 399 | 400 | 1. `f` must be a function, 401 | 402 | 1. If `f` is not a function, the behaviour of `fantasy-land/contramap` is 403 | unspecified. 404 | 2. `f` can return any value. 405 | 3. No parts of `f`'s return value should be checked. 406 | 407 | 2. `fantasy-land/contramap` must return a value of the same Contravariant 408 | 409 | ### Apply 410 | 411 | A value that implements the Apply specification must also 412 | implement the [Functor](#functor) specification. 413 | 414 | 1. `v['fantasy-land/ap'](u['fantasy-land/ap'](a['fantasy-land/map'](f => g => x => f(g(x)))))` is equivalent to `v['fantasy-land/ap'](u)['fantasy-land/ap'](a)` (composition) 415 | 416 | 417 | 418 | #### `fantasy-land/ap` method 419 | 420 | ```hs 421 | fantasy-land/ap :: Apply f => f a ~> f (a -> b) -> f b 422 | ``` 423 | 424 | A value which has an Apply must provide a `fantasy-land/ap` method. The `fantasy-land/ap` 425 | method takes one argument: 426 | 427 | a['fantasy-land/ap'](b) 428 | 429 | 1. `b` must be an Apply of a function 430 | 431 | 1. If `b` does not represent a function, the behaviour of `fantasy-land/ap` is 432 | unspecified. 433 | 2. `b` must be same Apply as `a`. 434 | 435 | 2. `a` must be an Apply of any value 436 | 437 | 3. `fantasy-land/ap` must apply the function in Apply `b` to the value in 438 | Apply `a` 439 | 440 | 1. No parts of return value of that function should be checked. 441 | 442 | 4. The `Apply` returned by `fantasy-land/ap` must be the same as `a` and `b` 443 | 444 | ### Applicative 445 | 446 | A value that implements the Applicative specification must also 447 | implement the [Apply](#apply) specification. 448 | 449 | 1. `v['fantasy-land/ap'](A['fantasy-land/of'](x => x))` is equivalent to `v` (identity) 450 | 2. `A['fantasy-land/of'](x)['fantasy-land/ap'](A['fantasy-land/of'](f))` is equivalent to `A['fantasy-land/of'](f(x))` (homomorphism) 451 | 3. `A['fantasy-land/of'](y)['fantasy-land/ap'](u)` is equivalent to `u['fantasy-land/ap'](A['fantasy-land/of'](f => f(y)))` (interchange) 452 | 453 | 454 | 455 | #### `fantasy-land/of` method 456 | 457 | ```hs 458 | fantasy-land/of :: Applicative f => a -> f a 459 | ``` 460 | 461 | A value which has an Applicative must provide a `fantasy-land/of` function on its 462 | [type representative](#type-representatives). The `fantasy-land/of` function takes 463 | one argument: 464 | 465 | F['fantasy-land/of'](a) 466 | 467 | Given a value `f`, one can access its type representative via the 468 | `constructor` property: 469 | 470 | f.constructor['fantasy-land/of'](a) 471 | 472 | 1. `fantasy-land/of` must provide a value of the same Applicative 473 | 474 | 1. No parts of `a` should be checked 475 | 476 | ### Alt 477 | 478 | A value that implements the Alt specification must also implement 479 | the [Functor](#functor) specification. 480 | 481 | 1. `a['fantasy-land/alt'](b)['fantasy-land/alt'](c)` is equivalent to `a['fantasy-land/alt'](b['fantasy-land/alt'](c))` (associativity) 482 | 2. `a['fantasy-land/alt'](b)['fantasy-land/map'](f)` is equivalent to `a['fantasy-land/map'](f)['fantasy-land/alt'](b['fantasy-land/map'](f))` (distributivity) 483 | 484 | 485 | 486 | #### `fantasy-land/alt` method 487 | 488 | ```hs 489 | fantasy-land/alt :: Alt f => f a ~> f a -> f a 490 | ``` 491 | 492 | A value which has a Alt must provide a `fantasy-land/alt` method. The 493 | `fantasy-land/alt` method takes one argument: 494 | 495 | a['fantasy-land/alt'](b) 496 | 497 | 1. `b` must be a value of the same Alt 498 | 499 | 1. If `b` is not the same Alt, behaviour of `fantasy-land/alt` is 500 | unspecified. 501 | 2. `a` and `b` can contain any value of same type. 502 | 3. No parts of `a`'s and `b`'s containing value should be checked. 503 | 504 | 2. `fantasy-land/alt` must return a value of the same Alt. 505 | 506 | ### Plus 507 | 508 | A value that implements the Plus specification must also implement 509 | the [Alt](#alt) specification. 510 | 511 | 1. `x['fantasy-land/alt'](A['fantasy-land/zero']())` is equivalent to `x` (right identity) 512 | 2. `A['fantasy-land/zero']()['fantasy-land/alt'](x)` is equivalent to `x` (left identity) 513 | 3. `A['fantasy-land/zero']()['fantasy-land/map'](f)` is equivalent to `A['fantasy-land/zero']()` (annihilation) 514 | 515 | 516 | 517 | #### `fantasy-land/zero` method 518 | 519 | ```hs 520 | fantasy-land/zero :: Plus f => () -> f a 521 | ``` 522 | 523 | A value which has a Plus must provide a `fantasy-land/zero` function on its 524 | [type representative](#type-representatives): 525 | 526 | A['fantasy-land/zero']() 527 | 528 | Given a value `x`, one can access its type representative via the 529 | `constructor` property: 530 | 531 | x.constructor['fantasy-land/zero']() 532 | 533 | 1. `fantasy-land/zero` must return a value of the same Plus 534 | 535 | ### Alternative 536 | 537 | A value that implements the Alternative specification must also implement 538 | the [Applicative](#applicative) and [Plus](#plus) specifications. 539 | 540 | 1. `x['fantasy-land/ap'](f['fantasy-land/alt'](g))` is equivalent to `x['fantasy-land/ap'](f)['fantasy-land/alt'](x['fantasy-land/ap'](g))` (distributivity) 541 | 2. `x['fantasy-land/ap'](A['fantasy-land/zero']())` is equivalent to `A['fantasy-land/zero']()` (annihilation) 542 | 543 | ### Foldable 544 | 545 | 1. `u['fantasy-land/reduce']` is equivalent to `u['fantasy-land/reduce']((acc, x) => acc.concat([x]), []).reduce` 546 | 547 | 548 | 549 | #### `fantasy-land/reduce` method 550 | 551 | ```hs 552 | fantasy-land/reduce :: Foldable f => f a ~> ((b, a) -> b, b) -> b 553 | ``` 554 | 555 | A value which has a Foldable must provide a `fantasy-land/reduce` method. The `fantasy-land/reduce` 556 | method takes two arguments: 557 | 558 | u['fantasy-land/reduce'](f, x) 559 | 560 | 1. `f` must be a binary function 561 | 562 | 1. if `f` is not a function, the behaviour of `fantasy-land/reduce` is unspecified. 563 | 2. The first argument to `f` must be the same type as `x`. 564 | 3. `f` must return a value of the same type as `x`. 565 | 4. No parts of `f`'s return value should be checked. 566 | 567 | 1. `x` is the initial accumulator value for the reduction 568 | 569 | 1. No parts of `x` should be checked. 570 | 571 | ### Traversable 572 | 573 | A value that implements the Traversable specification must also 574 | implement the [Functor](#functor) and [Foldable](#foldable) specifications. 575 | 576 | 1. `t(u['fantasy-land/traverse'](F, x => x))` is equivalent to `u['fantasy-land/traverse'](G, t)` for any 577 | `t` such that `t(a)['fantasy-land/map'](f)` is equivalent to `t(a['fantasy-land/map'](f))` (naturality) 578 | 579 | 2. `u['fantasy-land/traverse'](F, F['fantasy-land/of'])` is equivalent to `F['fantasy-land/of'](u)` for any Applicative `F` 580 | (identity) 581 | 582 | 3. `u['fantasy-land/traverse'](Compose, x => new Compose(x))` is equivalent to 583 | `new Compose(u['fantasy-land/traverse'](F, x => x)['fantasy-land/map'](x => x['fantasy-land/traverse'](G, x => x)))` for 584 | `Compose` defined below and any Applicatives `F` and `G` (composition) 585 | 586 | ```js 587 | function Compose(c) { 588 | this.c = c; 589 | } 590 | 591 | Compose['fantasy-land/of'] = function(x) { 592 | return new Compose(F['fantasy-land/of'](G['fantasy-land/of'](x))); 593 | }; 594 | 595 | Compose.prototype['fantasy-land/ap'] = function(f) { 596 | return new Compose(this.c['fantasy-land/ap'](f.c['fantasy-land/map'](u => y => y['fantasy-land/ap'](u)))); 597 | }; 598 | 599 | Compose.prototype['fantasy-land/map'] = function(f) { 600 | return new Compose(this.c['fantasy-land/map'](y => y['fantasy-land/map'](f))); 601 | }; 602 | ``` 603 | 604 | 605 | 606 | #### `fantasy-land/traverse` method 607 | 608 | ```hs 609 | fantasy-land/traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b) 610 | ``` 611 | 612 | A value which has a Traversable must provide a `fantasy-land/traverse` method. The `fantasy-land/traverse` 613 | method takes two arguments: 614 | 615 | u['fantasy-land/traverse'](A, f) 616 | 617 | 1. `A` must be the [type representative](#type-representatives) of an 618 | Applicative. 619 | 620 | 2. `f` must be a function which returns a value 621 | 622 | 1. If `f` is not a function, the behaviour of `fantasy-land/traverse` is 623 | unspecified. 624 | 2. `f` must return a value of the type represented by `A`. 625 | 626 | 3. `fantasy-land/traverse` must return a value of the type represented by `A`. 627 | 628 | ### Chain 629 | 630 | A value that implements the Chain specification must also 631 | implement the [Apply](#apply) specification. 632 | 633 | 1. `m['fantasy-land/chain'](f)['fantasy-land/chain'](g)` is equivalent to `m['fantasy-land/chain'](x => f(x)['fantasy-land/chain'](g))` (associativity) 634 | 635 | 636 | 637 | #### `fantasy-land/chain` method 638 | 639 | ```hs 640 | fantasy-land/chain :: Chain m => m a ~> (a -> m b) -> m b 641 | ``` 642 | 643 | A value which has a Chain must provide a `fantasy-land/chain` method. The `fantasy-land/chain` 644 | method takes one argument: 645 | 646 | m['fantasy-land/chain'](f) 647 | 648 | 1. `f` must be a function which returns a value 649 | 650 | 1. If `f` is not a function, the behaviour of `fantasy-land/chain` is 651 | unspecified. 652 | 2. `f` must return a value of the same Chain 653 | 654 | 2. `fantasy-land/chain` must return a value of the same Chain 655 | 656 | ### ChainRec 657 | 658 | A value that implements the ChainRec specification must also implement the [Chain](#chain) specification. 659 | 660 | 1. `M['fantasy-land/chainRec']((next, done, v) => p(v) ? d(v)['fantasy-land/map'](done) : n(v)['fantasy-land/map'](next), i)` 661 | is equivalent to 662 | `(function step(v) { return p(v) ? d(v) : n(v)['fantasy-land/chain'](step); }(i))` (equivalence) 663 | 2. Stack usage of `M['fantasy-land/chainRec'](f, i)` must be at most a constant multiple of the stack usage of `f` itself. 664 | 665 | 666 | 667 | #### `fantasy-land/chainRec` method 668 | 669 | ```hs 670 | fantasy-land/chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b 671 | ``` 672 | 673 | A Type which has a ChainRec must provide a `fantasy-land/chainRec` function on its 674 | [type representative](#type-representatives). The `fantasy-land/chainRec` function 675 | takes two arguments: 676 | 677 | M['fantasy-land/chainRec'](f, i) 678 | 679 | Given a value `m`, one can access its type representative via the 680 | `constructor` property: 681 | 682 | m.constructor['fantasy-land/chainRec'](f, i) 683 | 684 | 1. `f` must be a function which returns a value 685 | 1. If `f` is not a function, the behaviour of `fantasy-land/chainRec` is unspecified. 686 | 2. `f` takes three arguments `next`, `done`, `value` 687 | 1. `next` is a function which takes one argument of same type as `i` and can return any value 688 | 2. `done` is a function which takes one argument and returns the same type as the return value of `next` 689 | 3. `value` is some value of the same type as `i` 690 | 3. `f` must return a value of the same ChainRec which contains a value returned from either `done` or `next` 691 | 2. `fantasy-land/chainRec` must return a value of the same ChainRec which contains a value of same type as argument of `done` 692 | 693 | ### Monad 694 | 695 | A value that implements the Monad specification must also implement 696 | the [Applicative](#applicative) and [Chain](#chain) specifications. 697 | 698 | 1. `M['fantasy-land/of'](a)['fantasy-land/chain'](f)` is equivalent to `f(a)` (left identity) 699 | 2. `m['fantasy-land/chain'](M['fantasy-land/of'])` is equivalent to `m` (right identity) 700 | 701 | ### Extend 702 | 703 | A value that implements the Extend specification must also implement the [Functor](#functor) specification. 704 | 705 | 1. `w['fantasy-land/extend'](g)['fantasy-land/extend'](f)` is equivalent to `w['fantasy-land/extend'](_w => f(_w['fantasy-land/extend'](g)))` 706 | 707 | 708 | 709 | #### `fantasy-land/extend` method 710 | 711 | ```hs 712 | fantasy-land/extend :: Extend w => w a ~> (w a -> b) -> w b 713 | ``` 714 | 715 | An Extend must provide a `fantasy-land/extend` method. The `fantasy-land/extend` 716 | method takes one argument: 717 | 718 | w['fantasy-land/extend'](f) 719 | 720 | 1. `f` must be a function which returns a value 721 | 722 | 1. If `f` is not a function, the behaviour of `fantasy-land/extend` is 723 | unspecified. 724 | 2. `f` must return a value of type `v`, for some variable `v` contained in `w`. 725 | 3. No parts of `f`'s return value should be checked. 726 | 727 | 2. `fantasy-land/extend` must return a value of the same Extend. 728 | 729 | ### Comonad 730 | 731 | A value that implements the Comonad specification must also implement the [Extend](#extend) specification. 732 | 733 | 1. `w['fantasy-land/extend'](_w => _w['fantasy-land/extract']())` is equivalent to `w` (left identity) 734 | 2. `w['fantasy-land/extend'](f)['fantasy-land/extract']()` is equivalent to `f(w)` (right identity) 735 | 736 | 737 | 738 | #### `fantasy-land/extract` method 739 | 740 | ```hs 741 | fantasy-land/extract :: Comonad w => w a ~> () -> a 742 | ``` 743 | 744 | A value which has a Comonad must provide a `fantasy-land/extract` method on itself. 745 | The `fantasy-land/extract` method takes no arguments: 746 | 747 | w['fantasy-land/extract']() 748 | 749 | 1. `fantasy-land/extract` must return a value of type `v`, for some variable `v` contained in `w`. 750 | 1. `v` must have the same type that `f` returns in `fantasy-land/extend`. 751 | 752 | ### Bifunctor 753 | 754 | A value that implements the Bifunctor specification must also implement 755 | the [Functor](#functor) specification. 756 | 757 | 1. `p['fantasy-land/bimap'](a => a, b => b)` is equivalent to `p` (identity) 758 | 2. `p['fantasy-land/bimap'](a => f(g(a)), b => h(i(b)))` is equivalent to `p['fantasy-land/bimap'](g, i)['fantasy-land/bimap'](f, h)` (composition) 759 | 760 | 761 | 762 | #### `fantasy-land/bimap` method 763 | 764 | ```hs 765 | fantasy-land/bimap :: Bifunctor f => f a c ~> (a -> b, c -> d) -> f b d 766 | ``` 767 | 768 | A value which has a Bifunctor must provide a `fantasy-land/bimap` method. The `fantasy-land/bimap` 769 | method takes two arguments: 770 | 771 | c['fantasy-land/bimap'](f, g) 772 | 773 | 1. `f` must be a function which returns a value 774 | 775 | 1. If `f` is not a function, the behaviour of `fantasy-land/bimap` is unspecified. 776 | 2. `f` can return any value. 777 | 3. No parts of `f`'s return value should be checked. 778 | 779 | 2. `g` must be a function which returns a value 780 | 781 | 1. If `g` is not a function, the behaviour of `fantasy-land/bimap` is unspecified. 782 | 2. `g` can return any value. 783 | 3. No parts of `g`'s return value should be checked. 784 | 785 | 3. `fantasy-land/bimap` must return a value of the same Bifunctor. 786 | 787 | ### Profunctor 788 | 789 | A value that implements the Profunctor specification must also implement 790 | the [Functor](#functor) specification. 791 | 792 | 1. `p['fantasy-land/promap'](a => a, b => b)` is equivalent to `p` (identity) 793 | 2. `p['fantasy-land/promap'](a => f(g(a)), b => h(i(b)))` is equivalent to `p['fantasy-land/promap'](f, i)['fantasy-land/promap'](g, h)` (composition) 794 | 795 | 796 | 797 | #### `fantasy-land/promap` method 798 | 799 | ```hs 800 | fantasy-land/promap :: Profunctor p => p b c ~> (a -> b, c -> d) -> p a d 801 | ``` 802 | 803 | A value which has a Profunctor must provide a `fantasy-land/promap` method. 804 | 805 | The `fantasy-land/promap` method takes two arguments: 806 | 807 | c['fantasy-land/promap'](f, g) 808 | 809 | 1. `f` must be a function which returns a value 810 | 811 | 1. If `f` is not a function, the behaviour of `fantasy-land/promap` is unspecified. 812 | 2. `f` can return any value. 813 | 3. No parts of `f`'s return value should be checked. 814 | 815 | 2. `g` must be a function which returns a value 816 | 817 | 1. If `g` is not a function, the behaviour of `fantasy-land/promap` is unspecified. 818 | 2. `g` can return any value. 819 | 3. No parts of `g`'s return value should be checked. 820 | 821 | 3. `fantasy-land/promap` must return a value of the same Profunctor 822 | 823 | ## Derivations 824 | 825 | When creating data types which satisfy multiple algebras, authors may choose 826 | to implement certain methods then derive the remaining methods. Derivations: 827 | 828 | - [`fantasy-land/equals`][] may be derived from [`fantasy-land/lte`][]: 829 | 830 | ```js 831 | function equals(other) { return this['fantasy-land/lte'](other) && other['fantasy-land/lte'](this); } 832 | ``` 833 | 834 | - [`fantasy-land/map`][] may be derived from [`fantasy-land/ap`][] and [`fantasy-land/of`][]: 835 | 836 | ```js 837 | function map(f) { return this['fantasy-land/ap'](this.constructor['fantasy-land/of'](f)); } 838 | ``` 839 | 840 | - [`fantasy-land/map`][] may be derived from [`fantasy-land/chain`][] and [`fantasy-land/of`][]: 841 | 842 | ```js 843 | function map(f) { return this['fantasy-land/chain'](a => this.constructor['fantasy-land/of'](f(a))); } 844 | ``` 845 | 846 | - [`fantasy-land/map`][] may be derived from [`fantasy-land/bimap`][]: 847 | 848 | ```js 849 | function map(f) { return this['fantasy-land/bimap'](a => a, f); } 850 | ``` 851 | 852 | - [`fantasy-land/map`][] may be derived from [`fantasy-land/promap`][]: 853 | 854 | ```js 855 | function map(f) { return this['fantasy-land/promap'](a => a, f); } 856 | ``` 857 | 858 | - [`fantasy-land/ap`][] may be derived from [`fantasy-land/chain`][]: 859 | 860 | ```js 861 | function ap(m) { return m['fantasy-land/chain'](f => this['fantasy-land/map'](f)); } 862 | ``` 863 | 864 | - [`fantasy-land/reduce`][] may be derived as follows: 865 | 866 | ```js 867 | function reduce(f, acc) { 868 | function Const(value) { 869 | this.value = value; 870 | } 871 | Const['fantasy-land/of'] = function(_) { 872 | return new Const(acc); 873 | }; 874 | Const.prototype['fantasy-land/map'] = function(_) { 875 | return this; 876 | }; 877 | Const.prototype['fantasy-land/ap'] = function(b) { 878 | return new Const(f(b.value, this.value)); 879 | }; 880 | return this['fantasy-land/traverse'](x => new Const(x), Const['fantasy-land/of']).value; 881 | } 882 | ``` 883 | 884 | - [`fantasy-land/map`][] may be derived as follows: 885 | 886 | ```js 887 | function map(f) { 888 | function Id(value) { 889 | this.value = value; 890 | } 891 | Id['fantasy-land/of'] = function(x) { 892 | return new Id(x); 893 | }; 894 | Id.prototype['fantasy-land/map'] = function(f) { 895 | return new Id(f(this.value)); 896 | }; 897 | Id.prototype['fantasy-land/ap'] = function(b) { 898 | return new Id(this.value(b.value)); 899 | }; 900 | return this['fantasy-land/traverse'](x => Id['fantasy-land/of'](f(x)), Id['fantasy-land/of']).value; 901 | } 902 | ``` 903 | 904 | - [`fantasy-land/filter`][] may be derived from [`fantasy-land/of`][], [`fantasy-land/chain`][], and [`fantasy-land/zero`][]: 905 | 906 | ```js 907 | function filter(pred) { 908 | var F = this.constructor; 909 | return this['fantasy-land/chain'](x => pred(x) ? F['fantasy-land/of'](x) : F['fantasy-land/zero']()); 910 | } 911 | ``` 912 | 913 | - [`fantasy-land/filter`][] may be derived from [`fantasy-land/concat`][], [`fantasy-land/of`][], [`fantasy-land/zero`][], and 914 | [`fantasy-land/reduce`][]: 915 | 916 | ```js 917 | function filter(pred) { 918 | var F = this.constructor; 919 | return this['fantasy-land/reduce']((f, x) => pred(x) ? f['fantasy-land/concat'](F['fantasy-land/of'](x)) : f, F['fantasy-land/zero']()); 920 | } 921 | ``` 922 | 923 | If a data type provides a method which *could* be derived, its behaviour must 924 | be equivalent to that of the derivation (or derivations). 925 | 926 | ## Notes 927 | 928 | 1. If there's more than a single way to implement the methods and 929 | laws, the implementation should choose one and provide wrappers for 930 | other uses. 931 | 2. It's discouraged to overload the specified methods. It can easily 932 | result in broken and buggy behaviour. 933 | 3. It is recommended to throw an exception on unspecified behaviour. 934 | 4. An `Identity` container which implements many of the methods is provided by 935 | [sanctuary-identity](https://github.com/sanctuary-js/sanctuary-identity). 936 | 937 | 938 | [`fantasy-land/ap`]: #ap-method 939 | [`fantasy-land/bimap`]: #bimap-method 940 | [`fantasy-land/chain`]: #chain-method 941 | [`fantasy-land/concat`]: #concat-method 942 | [`fantasy-land/equals`]: #equals-method 943 | [`fantasy-land/filter`]: #filter-method 944 | [`fantasy-land/lte`]: #lte-method 945 | [`fantasy-land/map`]: #map-method 946 | [`fantasy-land/of`]: #of-method 947 | [`fantasy-land/promap`]: #promap-method 948 | [`fantasy-land/reduce`]: #reduce-method 949 | [`fantasy-land/zero`]: #zero-method 950 | 951 | ## Alternatives 952 | 953 | There also exists [Static Land Specification](https://github.com/rpominov/static-land) 954 | with exactly the same ideas as Fantasy Land but based on static methods instead of instance methods. 955 | -------------------------------------------------------------------------------- /figures/dependencies.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | node [shape=plaintext] 3 | 4 | # Algebras 5 | Alt; 6 | Alternative; 7 | Applicative; 8 | Apply; 9 | Bifunctor; 10 | Category; 11 | Chain; 12 | ChainRec; 13 | Comonad; 14 | Contravariant; 15 | Extend; 16 | Filterable; 17 | Foldable; 18 | Functor; 19 | Group; 20 | Monad; 21 | Monoid; 22 | Ord; 23 | Plus; 24 | Profunctor; 25 | Semigroup; 26 | Semigroupoid; 27 | Setoid; 28 | Traversable; 29 | 30 | # Dependencies 31 | Alt -> Plus; 32 | Applicative -> Alternative; 33 | Applicative -> Monad; 34 | Apply -> Applicative; 35 | Apply -> Chain; 36 | Chain -> ChainRec; 37 | Chain -> Monad; 38 | Extend -> Comonad; 39 | Foldable -> Traversable; 40 | Functor -> Alt; 41 | Functor -> Apply; 42 | Functor -> Bifunctor; 43 | Functor -> Extend; 44 | Functor -> Profunctor; 45 | Functor -> Traversable; 46 | Monoid -> Group; 47 | Plus -> Alternative; 48 | Semigroup -> Monoid; 49 | Semigroupoid -> Category; 50 | Setoid -> Ord; 51 | } 52 | -------------------------------------------------------------------------------- /figures/dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dot -Tpng -odependencies.png dependencies.dot 4 | -------------------------------------------------------------------------------- /implementations.md: -------------------------------------------------------------------------------- 1 | # Conformant Implementations 2 | 3 | Here are a list of implementations that live in Fantasy Land: 4 | 5 | * [Most.js](https://github.com/cujojs/most) implements Monoid, Functor, Applicative, and Monad for streams 6 | * [creed](https://github.com/briancavalier/creed) implements Monoid, Functor, Applicative, and Monad for promises, and interops with Promises/A+ and ES2015 Promise 7 | * [bacon.js](https://github.com/raimohanska/bacon.js) implements 8 | Monad and Functor for EventStream and Property on the "fantasy-land" branch 9 | * [aljebra](https://github.com/markandrus/aljebra) implements common 10 | Monoid structures from Haskell 11 | * [supervis.es](https://github.com/raganwald/supervis.es) contains many 12 | structures that implement Monad 13 | * [sweet-fantasies](https://github.com/pufuwozu/sweet-fantasies) provides 14 | macros for syntactic sugar of Semigroup and Monad 15 | * [Fantasy Promises](https://github.com/pufuwozu/fantasy-promises) 16 | implements Monad and Functor for Promise 17 | * [Fantasy Sorcery](https://github.com/pufuwozu/fantasy-sorcery) 18 | provides common functions that work for Fantasy Land structures 19 | * ECMAScript 5 provides a Semigroup, Functor, and Foldable for Array 20 | * [lz](https://github.com/goatslacker/lz) implements Semigroup, Monoid, Functor, and Monad for lazy Array and String 21 | * [Pacta](https://github.com/mudge/pacta) is an algebraic implementation of 22 | Promises that implements Semigroup, Monoid, Functor, Applicative, Chain and 23 | Monad 24 | * [Pirandello](https://github.com/quarterto/Pirandello) better streams, with a MonadPlus 25 | * [Parsimmon](https://github.com/jayferd/parsimmon) implements parsers that are semigroups, applicative functors, and monads. 26 | * [Bennu](https://github.com/mattbierner/bennu/) parsec style parser combinators implement monad, monoid, functor, and applicative functor. 27 | * [Akh](https://github.com/mattbierner/akh/) is a collection of monad transformers and common structures that implement Fantasy Land interfaces. 28 | * [TsMonad](https://github.com/cbowdon/tsmonad) implements some common monads for TypeScript. 29 | * [List in JS](https://github.com/PandaNoir/List-in-JS/) implements Setoid, Semigroup, Monoid, Functor, Applicative, Foldable, Traversable, Chain and Monad. 30 | * [Sanctuary](https://github.com/plaid/sanctuary) is a refuge from unsafe JavaScript. It provides FL-compatible Either and Maybe types. 31 | * [Fluture](https://github.com/Avaq/Fluture) is a high-performance monadic alternative to Promises. 32 | * [Ramda Adjunct](https://github.com/char0n/ramda-adjunct) is a community-maintained extension of Ramda 33 | * [Folktale](https://folktale.origamitower.com/) implements Maybe, Result, Validation, Task and Future as FL-compatible types. 34 | * [Monastic](https://github.com/wearereasonablepeople/monastic) implements a Fantasy Land 3 compliant State Monad. 35 | * [SodiumFRP](https://github.com/sodiumFRP/sodium-typescript) implements Functor and Monoid for Streams, as well as Monad and Comonad for Cells. 36 | * [Zion](https://github.com/osstotalsoft/jsbb/tree/master/packages/zion) provides FL-compatible types like Maybe, List, Map, Reader, Step and polymorphic fns. 37 | * [purify](https://github.com/gigobyte/purify/) contains structures that implement Functor, Monad, and a lot of other Fantasy Land interfaces. 38 | 39 | Conforming implementations are encouraged to promote the Fantasy Land logo: 40 | 41 | ![](logo.png) 42 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export const equals: 'fantasy-land/equals'; 2 | export const lte: 'fantasy-land/lte'; 3 | export const compose: 'fantasy-land/compose'; 4 | export const id: 'fantasy-land/id'; 5 | export const concat: 'fantasy-land/concat'; 6 | export const empty: 'fantasy-land/empty'; 7 | export const invert: 'fantasy-land/invert'; 8 | export const filter: 'fantasy-land/filter'; 9 | export const map: 'fantasy-land/map'; 10 | export const contramap: 'fantasy-land/contramap'; 11 | export const ap: 'fantasy-land/ap'; 12 | export const of: 'fantasy-land/of'; 13 | export const alt: 'fantasy-land/alt'; 14 | export const zero: 'fantasy-land/zero'; 15 | export const reduce: 'fantasy-land/reduce'; 16 | export const traverse: 'fantasy-land/traverse'; 17 | export const chain: 'fantasy-land/chain'; 18 | export const chainRec: 'fantasy-land/chainRec'; 19 | export const extend: 'fantasy-land/extend'; 20 | export const extract: 'fantasy-land/extract'; 21 | export const bimap: 'fantasy-land/bimap'; 22 | export const promap: 'fantasy-land/promap'; 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 'use strict'; 4 | 5 | var mapping = { 6 | equals: 'fantasy-land/equals', 7 | lte: 'fantasy-land/lte', 8 | compose: 'fantasy-land/compose', 9 | id: 'fantasy-land/id', 10 | concat: 'fantasy-land/concat', 11 | empty: 'fantasy-land/empty', 12 | invert: 'fantasy-land/invert', 13 | filter: 'fantasy-land/filter', 14 | map: 'fantasy-land/map', 15 | contramap: 'fantasy-land/contramap', 16 | ap: 'fantasy-land/ap', 17 | of: 'fantasy-land/of', 18 | alt: 'fantasy-land/alt', 19 | zero: 'fantasy-land/zero', 20 | reduce: 'fantasy-land/reduce', 21 | traverse: 'fantasy-land/traverse', 22 | chain: 'fantasy-land/chain', 23 | chainRec: 'fantasy-land/chainRec', 24 | extend: 'fantasy-land/extend', 25 | extract: 'fantasy-land/extract', 26 | bimap: 'fantasy-land/bimap', 27 | promap: 'fantasy-land/promap' 28 | }; 29 | 30 | /* istanbul ignore else */ 31 | if (typeof module === 'object' && typeof module.exports === 'object') { 32 | module.exports = mapping; 33 | } else { 34 | self.FantasyLand = mapping; 35 | } 36 | 37 | } ()); 38 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | export const equals = 'fantasy-land/equals'; 2 | export const lte = 'fantasy-land/lte'; 3 | export const compose = 'fantasy-land/compose'; 4 | export const id = 'fantasy-land/id'; 5 | export const concat = 'fantasy-land/concat'; 6 | export const empty = 'fantasy-land/empty'; 7 | export const invert = 'fantasy-land/invert'; 8 | export const filter = 'fantasy-land/filter'; 9 | export const map = 'fantasy-land/map'; 10 | export const contramap = 'fantasy-land/contramap'; 11 | export const ap = 'fantasy-land/ap'; 12 | export const of = 'fantasy-land/of'; 13 | export const alt = 'fantasy-land/alt'; 14 | export const zero = 'fantasy-land/zero'; 15 | export const reduce = 'fantasy-land/reduce'; 16 | export const traverse = 'fantasy-land/traverse'; 17 | export const chain = 'fantasy-land/chain'; 18 | export const chainRec = 'fantasy-land/chainRec'; 19 | export const extend = 'fantasy-land/extend'; 20 | export const extract = 'fantasy-land/extract'; 21 | export const bimap = 'fantasy-land/bimap'; 22 | export const promap = 'fantasy-land/promap'; 23 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fantasyland/fantasy-land/0fc5ba5b3699046b024d21fe023c8bfef8d482d0/logo.png -------------------------------------------------------------------------------- /names: -------------------------------------------------------------------------------- 1 | equals 2 | lte 3 | compose 4 | id 5 | concat 6 | empty 7 | invert 8 | filter 9 | map 10 | contramap 11 | ap 12 | of 13 | alt 14 | zero 15 | reduce 16 | traverse 17 | chain 18 | chainRec 19 | extend 20 | extract 21 | bimap 22 | promap 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fantasy-land", 3 | "author": "Brian McKenna", 4 | "version": "5.0.1", 5 | "description": "Specification for interoperability of common algebraic structures in JavaScript", 6 | "license": "MIT", 7 | "homepage": "https://github.com/fantasyland/fantasy-land", 8 | "keywords": [ 9 | "algebraic", 10 | "monad", 11 | "applicative", 12 | "functor", 13 | "monoid", 14 | "semigroup", 15 | "chain", 16 | "apply" 17 | ], 18 | "issues": { 19 | "url": "https://github.com/fantasyland/fantasy-land/issues" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "sanctuary-scripts": "2.x.x" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/fantasyland/fantasy-land.git" 28 | }, 29 | "files": [ 30 | "/LICENSE", 31 | "/README.md", 32 | "/index.d.ts", 33 | "/index.js", 34 | "/index.mjs", 35 | "/package.json" 36 | ], 37 | "main": "index.js", 38 | "exports": { 39 | "types": "./index.d.ts", 40 | "require": "./index.js", 41 | "import": "./index.mjs" 42 | }, 43 | "scripts": { 44 | "doctest": "sanctuary-doctest", 45 | "generate-es": "scripts/generate-es", 46 | "generate-js": "scripts/generate-js", 47 | "generate-ts": "scripts/generate-ts", 48 | "lint": "sanctuary-lint", 49 | "release": "sanctuary-release", 50 | "test": "npm run lint && sanctuary-test && npm run doctest" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /scripts/generate-es: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euf -o pipefail 3 | 4 | awk '{ print "export const " $0 " = \047fantasy-land/" $0 "\047;" }' names >index.mjs 5 | -------------------------------------------------------------------------------- /scripts/generate-js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euf -o pipefail 3 | 4 | cat >index.js < 1 ? ",\n " : "") $0 ": \047fantasy-land/" $0 "\047" }' names) 11 | }; 12 | 13 | /* istanbul ignore else */ 14 | if (typeof module === 'object' && typeof module.exports === 'object') { 15 | module.exports = mapping; 16 | } else { 17 | self.FantasyLand = mapping; 18 | } 19 | 20 | } ()); 21 | EOF 22 | -------------------------------------------------------------------------------- /scripts/generate-readme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fantasyland/fantasy-land/0fc5ba5b3699046b024d21fe023c8bfef8d482d0/scripts/generate-readme -------------------------------------------------------------------------------- /scripts/generate-ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euf -o pipefail 3 | 4 | awk '{ print "export const " $0 ": \047fantasy-land/" $0 "\047;" }' names >index.d.ts 5 | -------------------------------------------------------------------------------- /scripts/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euf -o pipefail 3 | 4 | node_modules/.bin/sanctuary-lint "$@" 5 | 6 | scripts/generate-js && git diff --exit-code index.js 7 | scripts/generate-ts && git diff --exit-code index.d.ts 8 | scripts/generate-es && git diff --exit-code index.mjs 9 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require ('assert'); 4 | 5 | const FL = require ('..'); 6 | 7 | 8 | test ('exports', () => { 9 | assert.strictEqual (FL.map, 'fantasy-land/map'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --ui tdd 2 | --------------------------------------------------------------------------------