1 | /*!
|
2 | * Copyright (c) 2017-2018 by The Funfix Project Developers.
|
3 | * Some rights reserved.
|
4 | *
|
5 | * Licensed under the Apache License, Version 2.0 (the "License");
|
6 | * you may not use this file except in compliance with the License.
|
7 | * You may obtain a copy of the License at
|
8 | *
|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
10 | *
|
11 | * Unless required by applicable law or agreed to in writing, software
|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14 | * See the License for the specific language governing permissions and
|
15 | * limitations under the License.
|
16 | */
|
17 |
|
18 | import { Constructor, Setoid, Monad } from "funland"
|
19 |
|
20 | /**
|
21 | * Given a function, converts it into a method where `this` gets
|
22 | * passed around as the last argument.
|
23 | */
|
24 | export function convertToMethod(f: Function): Function {
|
25 | return function (this: any) {
|
26 | const args = Array.prototype.slice.call(arguments)
|
27 | args.push(this)
|
28 | return f.apply(undefined, args)
|
29 | }
|
30 | }
|
31 |
|
32 | /**
|
33 | * Given a constructor, searches for all Fantasy-Land compatible
|
34 | * methods registered on its `prototype` and also registers them
|
35 | * using Fantasy-Land compatible symbols.
|
36 | *
|
37 | * For example:
|
38 | *
|
39 | * ```typescript
|
40 | * class Box<A> {
|
41 | * constructor(public readonly value: A) {}
|
42 | *
|
43 | * // Setoid
|
44 | * equals(that: Box<A>) {
|
45 | * return that && this.value === that.value
|
46 | * }
|
47 | * // Functor
|
48 | * map<B>(f: (a: A) => B): Box<B> {
|
49 | * return new Box(f(this.value))
|
50 | * }
|
51 | * }
|
52 | *
|
53 | * // Registering Fantasy-Land compatible symbols
|
54 | * fantasyLandRegister(Box)
|
55 | * ```
|
56 | *
|
57 | * The above registration call would make `fantasy-land/equals` and
|
58 | * `fantasy-land/functor` available on `Box.prototype`.
|
59 | *
|
60 | * @private
|
61 | * @Hidden
|
62 | */
|
63 | export function fantasyLandRegister<A>(
|
64 | cls: Constructor<A>,
|
65 | monad?: Monad<any>,
|
66 | setoid?: Setoid<any>): void {
|
67 |
|
68 | const c = cls as any
|
69 | const p = c.prototype
|
70 |
|
71 | const fl = "fantasy-land/"
|
72 | const equals = "equals"
|
73 | const flEquals = fl + equals
|
74 | const map = "map"
|
75 | const flMap = fl + map
|
76 | const ap = "ap"
|
77 | const flAp = fl + ap
|
78 | const flOf = fl + "of"
|
79 | const chain = "chain"
|
80 | const flChain = fl + chain
|
81 | const chainRec = "chainRec"
|
82 | const flChainRec = fl + chainRec
|
83 |
|
84 | // Setoid
|
85 | if (p[equals]) {
|
86 | p[flEquals] = p[equals]
|
87 | } else {
|
88 | /* istanbul ignore else */
|
89 | if (setoid) p[flEquals] = convertToMethod(setoid.equals)
|
90 | }
|
91 | // Functor
|
92 | if (p[map]) {
|
93 | p[flMap] = p[map]
|
94 | } else {
|
95 | /* istanbul ignore else */
|
96 | if (monad) p[flMap] = convertToMethod(monad.map)
|
97 | }
|
98 | // Apply
|
99 | if (p[ap]) {
|
100 | p[flAp] = p[ap]
|
101 | } else {
|
102 | /* istanbul ignore else */
|
103 | if (monad) p[flAp] = convertToMethod(monad.ap)
|
104 | }
|
105 | // Applicative
|
106 | if (c["pure"]) {
|
107 | c[flOf] = c["pure"]
|
108 | } else {
|
109 | /* istanbul ignore else */
|
110 | if (monad) c[flOf] = monad.of
|
111 | }
|
112 | // Chain
|
113 | if (p[chain]) {
|
114 | p[flChain] = p[chain]
|
115 | } else {
|
116 | /* istanbul ignore else */
|
117 | if (monad) p[flChain] = convertToMethod(monad.chain)
|
118 | }
|
119 | // ChainRec
|
120 | if (c[chainRec]) {
|
121 | c[flChainRec] = c[chainRec]
|
122 | } else {
|
123 | /* istanbul ignore else */
|
124 | if (monad) c[flChainRec] = monad.chainRec
|
125 | }
|
126 | }
|