/*!
* Copyright (c) 2017-2018 by The Funfix Project Developers.
* Some rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Constructor, Setoid, Monad } from "funland"
/**
* Given a function, converts it into a method where `this` gets
* passed around as the last argument.
*/
export function convertToMethod(f: Function): Function {
return function (this: any) {
const args = Array.prototype.slice.call(arguments)
args.push(this)
return f.apply(undefined, args)
}
}
/**
* Given a constructor, searches for all Fantasy-Land compatible
* methods registered on its `prototype` and also registers them
* using Fantasy-Land compatible symbols.
*
* For example:
*
* ```typescript
* class Box {
* constructor(public readonly value: A) {}
*
* // Setoid
* equals(that: Box) {
* return that && this.value === that.value
* }
* // Functor
* map(f: (a: A) => B): Box {
* return new Box(f(this.value))
* }
* }
*
* // Registering Fantasy-Land compatible symbols
* fantasyLandRegister(Box)
* ```
*
* The above registration call would make `fantasy-land/equals` and
* `fantasy-land/functor` available on `Box.prototype`.
*
* @private
* @Hidden
*/
export function fantasyLandRegister(
cls: Constructor,
monad?: Monad,
setoid?: Setoid): void {
const c = cls as any
const p = c.prototype
const fl = "fantasy-land/"
const equals = "equals"
const flEquals = fl + equals
const map = "map"
const flMap = fl + map
const ap = "ap"
const flAp = fl + ap
const flOf = fl + "of"
const chain = "chain"
const flChain = fl + chain
const chainRec = "chainRec"
const flChainRec = fl + chainRec
// Setoid
if (p[equals]) {
p[flEquals] = p[equals]
} else {
/* istanbul ignore else */
if (setoid) p[flEquals] = convertToMethod(setoid.equals)
}
// Functor
if (p[map]) {
p[flMap] = p[map]
} else {
/* istanbul ignore else */
if (monad) p[flMap] = convertToMethod(monad.map)
}
// Apply
if (p[ap]) {
p[flAp] = p[ap]
} else {
/* istanbul ignore else */
if (monad) p[flAp] = convertToMethod(monad.ap)
}
// Applicative
if (c["pure"]) {
c[flOf] = c["pure"]
} else {
/* istanbul ignore else */
if (monad) c[flOf] = monad.of
}
// Chain
if (p[chain]) {
p[flChain] = p[chain]
} else {
/* istanbul ignore else */
if (monad) p[flChain] = convertToMethod(monad.chain)
}
// ChainRec
if (c[chainRec]) {
c[flChainRec] = c[chainRec]
} else {
/* istanbul ignore else */
if (monad) c[flChainRec] = monad.chainRec
}
}