├── .gitignore ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Peng Jie 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 | ![Fantasy Land Specification](https://raw.githubusercontent.com/fantasyland/fantasy-land/master/figures/dependencies.png) 4 | 5 | ## 概述 6 | 7 | 代數是一組值,一組操作,它是封閉的,必須遵守一些規則。 8 | 9 | 每個 Fantasy Land 代數是一個獨立的規範。一個代數可能依賴於其他必須實現的代數。 10 | 11 | ## 術語 12 | 13 | 1. 「value」是任何 JavaScript 的值,包含具有下面定義的的任何結構。 14 | 2. 「equivalent」對於給定的值有一個等價的定義。這個定義應該確保這兩個值可以在一個尊重抽象的程式中安全的被交換出來。例如: 15 | - 如果兩個 list 所有指數都是相同的,那它們是相同的。 16 | - 兩個被解釋為 dictionary 的 JavaScript 純 Object,當它們有相等的 key 時,它們是等價的。 17 | - 當兩個 Promise 產生相等的值時,它們是等價的。 18 | - 如果兩個函式輸入相同的值,且得到相同的輸出,它們是等價的。 19 | 20 | ## Type Signature 符號 21 | 22 | 以下介紹文件中使用的 Type Signature 符號: 23 | 24 | - _`::` 「is a member of」。_ 25 | - `e :: t` 可以讀作:「表達式 `e` 是類型 `t` 的一位成員」。 26 | - `true :: Boolean` - 「`true` 是類型 `Boolean` 的一位成員」。 27 | - `42 :: Integer, Number` - 「`42` 是類型 `Integer` 和 `Number` 的一位成員」。 28 | 29 | - _新 type 可以藉由 type constructor 被建立。_ 30 | - Type constructor 可以接受零個或多個 type 參數。 31 | - `Array` 是一個 type constructor,它接受一個 type 參數。 32 | - `Array String` 是一個字串陣列的 type。以下每個 type 都是 `Array String`:`[]`、`['foo', 'bar', 'baz']`。 33 | - `Array (Array String)` 是一個陣列的字串陣列 type。以下每個 type 都是 `Array (Array String)`:`[]`、`[ [], [] ]`、`[ [], ['foo'], ['bar', 'baz'] ]`。 34 | 35 | - _小寫字母代表 type 變數。_ 36 | - Type 變數可以接受任何 type,除非已經透過 type constraint 被限制(參考以下 fat arrow)。 37 | 38 | - `->`(arrow)_Function type constructor。_ 39 | - `->` 是一個 infix type constructor,它接受兩個 type 參數,左邊參數是輸入的 type,右邊的則是輸出 type。 40 | - `->` 的輸入 type 可以是一組 type 來建立接受零個或多個的函式類型。語法是:`() -> `,其中 `` 包含零個或多個逗號(`,`)來分隔 type 表示,對於一元函式可以省略括號。_(參考此 [Issue](https://github.com/fantasyland/fantasy-land/issues/279) 解釋)_ 41 | - `String -> Array String` 是一個透過函式滿足的 type,接受一個 `String` 並回傳一個 `Array String`。 42 | - `String -> Array String -> Array String` 是一個透過函式滿足的 type,它接受一個 `String` 並回傳一個函式接受一個 `Array String` 最後回傳一個 `Array String`。 43 | - `(String, Array String) -> Array String` 是一個透過函式滿足的 type,它接受一個 `String` 和一個 `Array String` 作為參數,並回傳一個 `Array String`。 44 | - `() -> Number` 是一個透過函式滿足的 type,它不接受任何的參數,並回傳一個 `Number`。 45 | 46 | - `~>`(squiggly arrow)_Method type constructor。_ 47 | - 當函式是一個 Object 的屬性時,它被稱為一個方法。所有的方法有一個隱式的參數 type - 它們是屬性的 type。 48 | - `a ~> a -> a` 是 type `a` 的 Object 上的方法所滿足的類型,它將 type `a` 作為參數並回傳 type `a` 的值。 49 | 50 | - `=>`(fat arrow)_表示對 type 變數的 constraint。_ 51 | - 在 `a ~> a -> a`(參考上方的 squiggly arrow),`a` 可以是任何 type。`Semigroup a => a ~> a -> a` 加入了一個 constraint,使得 type `a` 現在必須滿足 `Semigroup` typeclass。滿足 typeclass 意思是,合法地實現 typeclass 指定的所有函式和方法。 52 | 53 | 例如: 54 | 55 | ``` 56 | traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b) 57 | '------' '--------------------------' '-' '-------------------' '-----' 58 | ' ' ' ' ' 59 | ' ' - type constraints ' ' - argument types ' - return type 60 | ' ' 61 | '- method name ' - method target type 62 | ``` 63 | 64 | --- 65 | 66 | 更多資訊,請參考 Sanctuary 文件的 [Type](https://sanctuary.js.org/#types) 部分。[↩️](https://github.com/fantasyland/fantasy-land#sanctuary-types-return) 67 | 68 | ## 前綴方法名稱 69 | 70 | 為了讓 data type 可以兼容 Fantasy Land,其值必須具有某些屬性。這些所有屬性都由 `fantasy-land/` 作為前綴。例如: 71 | 72 | ```js 73 | // MyType#fantasy-land/map :: MyType a ~> (a -> b) -> MyType b 74 | MyType.prototype['fantasy-land/map'] = ... 75 | ``` 76 | 77 | 此外,在本份文件沒有使用前綴名稱。 78 | 79 | 為了方便你可以使用 `fantasy-land` 的 package: 80 | 81 | ```js 82 | var fl = require('fantasy-land') 83 | 84 | // ... 85 | 86 | MyType.prototype[fl.map] = ... 87 | 88 | // ... 89 | 90 | var foo = bar[fl.map](x => x + 1) 91 | ``` 92 | 93 | ## Type 代表 94 | 95 | 某些行為是從一個 type 成員的角度定義的。其他行為不需要一個成員。因此,某些代數要求一個 type 提供一個 value 層級的代表(具有一定的屬性)。例如,可以提供 `Id` 作為其 type 代表:`Id :: TypeRep Identity`。 96 | 97 | 如果一個 type 提供了一個 type 代表,該 type 的每個成員必須有一個 `constructor` 屬性,該屬性是對 type 代表的引用。 98 | 99 | ## 代數 100 | 101 | ### Setoid 102 | 103 | 1. `a.equals(a) === true`(反身性) 104 | 2. `a.equals(b) === b.equals(a)`(對稱性) 105 | 3. 如果 `a.equals(b)` 而且 `b.equals(c)`,因此 `a.equals(c)`(傳遞性) 106 | 107 | #### `equals` 方法 108 | 109 | ```haskell 110 | equals :: Setoid a => a ~> a -> Boolean 111 | ``` 112 | 113 | 具有 Setoid 的值必須提供一個 `equals` 方法。`equals` 方法接受一個參數: 114 | 115 | ``` 116 | a.equals(b) 117 | ``` 118 | 119 | 1. `b` 必須是相同於 Setoid 的值。 120 | - i. 如果 `b` 不相同於 Setoid,`equals` 的行為是沒有被指定的(推薦回傳 `false`)。 121 | 2. `equals` 必須回傳一個布林值(`true` 或 `false`)。 122 | 123 | ### Ord 124 | 125 | 實作 Ord 規範的該值也必須實作 [Setoid](https://github.com/fantasyland/fantasy-land#setoid) 規範。 126 | 127 | 1. `a.lte(b)` 或 `b.lte(a)` (完整性) 128 | 2. 如果 `a.lte(b)` 而且 `b.lte(a)`,因此 `a.equals(b)` (反對稱性) 129 | 3. 如果 `a.lte(b)` 而且 `b.lte(c)`,因此 `a.lte(c)`(傳遞性) 130 | 131 | #### `lte` 方法 132 | 133 | ```haskell 134 | lte :: Ord a => a ~> a -> Boolean 135 | ``` 136 | 137 | 具有 Ord 的值必須提供一個 `lte` 方法。`lte` 方法接受一個參數: 138 | 139 | ``` 140 | a.lte(b) 141 | ``` 142 | 143 | 1. `b` 必須是相同於 Ord 的值。 144 | i. 如果 `b` 不相同於 Ord,`lte` 的行為是沒有被指定的(推薦回傳 `false`)。 145 | 2. `lte` 必須回傳一個布林值(`true` 或 `false`)。 146 | 147 | ### Semigroupid 148 | 149 | `a.compose(b).compose(c) === a.compose(b.compose(c))`(關聯性) 150 | 151 | #### `compose` 方法 152 | 153 | ```haskell 154 | compose :: Semigroupoid c => c i j ~> c j k -> c i k 155 | ``` 156 | 157 | 具有 Semigroupoid 的值必須提供一個 `compose` 方法。`compose` 方法接受一個參數: 158 | 159 | ``` 160 | a.compose(b) 161 | ``` 162 | 163 | 1. `b` 必須是相同的 Semigroupoid 的值。 164 | i. 如果 `b` 不是相同的 Semigroupoid,`compose` 的行為是沒有被指定的。 165 | 2. `compose` 必須回傳一個相同的 Semigroupoid 的值。 166 | 167 | ### Category 168 | 169 | 實作 Category 規範的該值也必須實作 [Semigroupoid](https://github.com/fantasyland/fantasy-land#semigroupoid) 規範。 170 | 171 | 1. `a.compose(C.id())` 相等於 `a`(Right Identity) 172 | 2. `C.id().compose(a)` 相等於 `a`(Left Identity) 173 | 174 | #### `id` 方法 175 | 176 | ```haskell 177 | id :: Category c => () -> c a a 178 | ``` 179 | 180 | 具有 Category 的值必須在它的 [type 代表](https://github.com/fantasyland/fantasy-land#type-representatives)上提供一個 `id` 方法: 181 | 182 | ``` 183 | C.id() 184 | ``` 185 | 186 | 給定一個 `c` 值,可以通過 `constructor` 屬性拜訪其 type 代表: 187 | 188 | ``` 189 | c.constructor.id() 190 | ``` 191 | 192 | 1. `id` 必須回傳一個相同的 Category 的值。 193 | 194 | ### Semigroup 195 | 196 | 1. `a.concat(b).concat(c)` 相等於 `a.concat(b.concat(c))`(關聯性) 197 | 198 | #### `contact` 方法 199 | 200 | ```haskell 201 | concat :: Semigroup a => a ~> a -> a 202 | ``` 203 | 204 | 具有 Semigroup 的值必須提供一個 `contact` 方法。`contact` 方法接受一個參數: 205 | 206 | ``` 207 | s.concat(b) 208 | ``` 209 | 210 | 1. `b` 必須是相同的 Semigroup 的值。 211 | i. 如果 `b` 不是相同的 Semigroup,`contact` 的行為是沒有被指定的。 212 | 2. `contact` 必須回傳一個相同的 Semigroup 的值。 213 | 214 | ### Monoid 215 | 216 | 實作 Monoid 規範的該值也必須實作 [Semigroup](https://github.com/fantasyland/fantasy-land#semigroup) 規範。 217 | 218 | #### `empty` 方法 219 | 220 | ```haskell 221 | empty :: Monoid m => () -> m 222 | ``` 223 | 224 | 具有 Monoid 的值必須在它的 [type 代表](https://github.com/fantasyland/fantasy-land#type-representatives)上提供一個 `empty` 方法: 225 | 226 | ``` 227 | M.empty() 228 | ``` 229 | 230 | 給定一個 `m` 值,可以通過 `constructor` 屬性拜訪其 type 代表: 231 | 232 | ``` 233 | m.constructor.empty() 234 | ``` 235 | 236 | 1. `empty` 必須回傳一個相同的 Monoid 的值。 237 | 238 | ### Group 239 | 240 | 實作 Group 規範的該值也必須實作 [Monoid](https://github.com/fantasyland/fantasy-land#monoid) 規範。 241 | 242 | 1. `g.concat(g.invert())` 相等於 `g.constructor.empty()`(Right Inverse) 243 | 2. `g.invert().concat(g)` 相等於 `g.constructor.empty()`(Left Inverse) 244 | 245 | #### `invert` 方法 246 | 247 | ```haskell 248 | invert :: Group g => g ~> () -> g 249 | ``` 250 | 251 | 具有 Group 的值必須提供一個 `invert` 方法。`invert` 方法不接受參數: 252 | 253 | ``` 254 | g.invert() 255 | ``` 256 | 257 | 1. `invert` 必須回傳相同的 Group 的值。 258 | 259 | ### Functor 260 | 261 | 1. `u.map(a => a)` 相等於 `u`(identity) 262 | 2. `u.map(x => f(g(x)))` 相等於 `u.map(g).map(f)`(組合性) 263 | 264 | #### `map` 方法 265 | 266 | ```haskell 267 | map :: Functor f => f a ~> (a -> b) -> f b 268 | ``` 269 | 具有 Functor 的值必須提供一個 `map` 方法。`map` 方法接受一個參數: 270 | 271 | ``` 272 | u.map(f) 273 | ``` 274 | 275 | 1. `f` 必須是一個函式, 276 | - i. 如果 `f` 不是一個函式,`map` 的行為是沒有被指定的。 277 | - ii. `f` 可以回傳任何值。 278 | - iii. No parts of f's return value should be checked. 279 | 2. `map` 必須回傳一個相同的 Functor 的值。 280 | 281 | ### Contravariant 282 | 283 | 1. `u.contramap(a => a)` 相等於 `u`(identity) 284 | 2. `u.contramap(x => f(g(x)))` 相等於 `u.contramap(f).contramap(g)`(composition) 285 | 286 | #### `contramap` 方法 287 | 288 | ```haskell 289 | contramap :: Contravariant f => f a ~> (b -> a) -> f b 290 | ``` 291 | 292 | 具有 Contravariant 的值必須提供一個 `contramap` 方法。`contrampa` 方法接受一個參數: 293 | 294 | ``` 295 | u.contramap(f) 296 | ``` 297 | 298 | 1. `f` 必須是一個函式, 299 | - i. 如果 `f` 不是一個函式,`contramap` 的行為是沒有被指定的。 300 | - ii. `f` 可以回傳任何值。 301 | - iii. No parts of f's return value should be checked. 302 | 2. `contramap` 必須回傳一個相同的 Contravariant 的值。 303 | 304 | ### Apply 305 | 306 | 實作 Apply 規範的該值也必須實作 [Functor](https://github.com/fantasyland/fantasy-land#functor) 規範。 307 | 308 | 1. `v.ap(u.ap(a.map(f => g => x => f(g(x)))))` 相等於 `v.ap(u).ap(a)`(composition) 309 | 310 | #### `ap` 方法 311 | 312 | ```haskell 313 | ap :: Apply f => f a ~> f (a -> b) -> f b 314 | ``` 315 | 316 | 具有 Apply 的值必須提供一個 `ap` 方法。`ap` 方法接受一個參數: 317 | 318 | ``` 319 | a.ap(b) 320 | ``` 321 | 322 | 1. `b` 必須是一個 Apply 函式, 323 | - i. 如果 `b` 不是代表一個函式,`ap` 的行為是沒有被指定的。 324 | - ii. `b` 必須相同於 a 是 Apply。 325 | 2. `a` 必須是任何值的一個 Apply。 326 | 3. `ap` 必須將 Apply `b` 中的函式應用於 Apply `a` 中的值。 327 | - i. 該函式沒有回傳值的部分需要被檢查 328 | 4. 透過 `ap` 被回傳的 Apply 必須與 `a` 和 `b` 相同。 329 | 330 | ### Applicative 331 | 332 | 實作 Applicative 規範的該值也必須實作 [Apply](https://github.com/fantasyland/fantasy-land#apply) 規範。 333 | 334 | 1. `v.ap(A.of(x => x))` 相等於 `v`(identity) 335 | 2. `A.of(x).ap(A.of(f))` 相等於 `A.of(f(x))`(homomorphism) 336 | 3. `A.of(y).ap(u)` 相等於 `u.ap(A.of(f => f(y)))`(interchange) 337 | 338 | #### `of` 方法 339 | 340 | ```haskell 341 | of :: Applicative f => a -> f a 342 | ``` 343 | 344 | 具有 Applicative 的值必須在它的 [type representative](https://github.com/fantasyland/fantasy-land#type-representatives) 上提供一個 `of` 方法。`of` 方法接受一個參數: 345 | 346 | ``` 347 | F.of(a) 348 | ``` 349 | 350 | 給定一個 `f` 值,透過 `constructor` 屬性訪問它的 type representative: 351 | 352 | ``` 353 | f.constructor.of(a) 354 | ``` 355 | 356 | 1. `of` 必須提供相同的 Applicative 值 357 | - i. 不需要檢查任何部分 358 | 359 | ### Alt 360 | 361 | 實作 Alt 規範的該值也必須實作 [Functor](https://github.com/fantasyland/fantasy-land#functor) 規範。 362 | 363 | 1. `a.alt(b).alt(c)` 相等於 `a.alt(b.alt(c))`(associativity) 364 | 2. `a.alt(b).map(f)` 相等於 `a.map(f).alt(b.map(f))`(distributivity) 365 | 366 | #### `alt` 方法 367 | 368 | ```haskell 369 | alt :: Alt f => f a ~> f a -> f a 370 | ``` 371 | 372 | 具有 Alt 的值必須提供一個 `alt` 方法。`alt` 方法接受一個參數: 373 | 374 | ``` 375 | a.alt(b) 376 | ``` 377 | 378 | 1. `b` 必須是相同 Alt 的值。 379 | - i. 如果 `b` 不相同於 Alt,`alt` 的行為是沒有被指定的。 380 | - ii. `a` 和 `b` 可以包涵任何相同 type 的值。 381 | - iii. No parts of `a`'s and `b`'s containing value should be checked. 382 | 383 | 2. `alt` 必須回傳一個相同 Alt 的值。 384 | 385 | ### Plus 386 | 387 | 實作 Plus 規範的該值也必須實作 [Alt](https://github.com/fantasyland/fantasy-land#alt) 規範。 388 | 389 | 1. `x.alt(A.zero())` 相等於 `x`(right identity) 390 | 2. `A.zero().alt(x)` 相等於 `x`(left identity) 391 | 3. `A.zero().map(f)` 相等於 `A.zero()`(annihilation) 392 | 393 | #### `zero` 方法 394 | 395 | ```haskell 396 | zero :: Plus f => () -> f a 397 | ``` 398 | 399 | 具有 Plus 的值必須在它的 [type representative](https://github.com/fantasyland/fantasy-land#type-representatives) 上提供一個 `zero` 函式: 400 | 401 | ``` 402 | A.zero() 403 | ``` 404 | 405 | 給定一個 `x` 值,可以通過 `constructor` 屬性拜訪其 type 代表: 406 | 407 | ``` 408 | x.constructor.zero() 409 | ``` 410 | 411 | 1. `zero` 必須回傳一個相同 Plus 的值。 412 | --------------------------------------------------------------------------------