]> Lady’s Gitweb - Pisces/blob - function.js
d1d337a18d1c19d1602b9415e20f8fc2fb4c83d7
[Pisces] / function.js
1 // ♓🌟 Piscēs ∷ function.js
2 // ====================================================================
3 //
4 // Copyright © 2022‐2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import { ITERATOR } from "./value.js";
11
12 export const {
13 /**
14 * Creates a bound function from the provided function using the
15 * provided this value and arguments list.
16 *
17 * ☡ As with `call` and `construct`, the arguments must be passed as
18 * an array.
19 */
20 bind,
21
22 /**
23 * Returns a new function which calls the provided function with its
24 * first argument as the `this` value and the remaining arguments
25 * passed through.
26 *
27 * ※ This is effectively an alias for `Function::call.bind`.
28 */
29 makeCallable,
30 } = (() => {
31 // ☡ Because these functions are used to initialize module constants,
32 // they can’t depend on imports from elsewhere.
33 const {
34 bind: functionBind,
35 call: functionCall,
36 } = Function.prototype;
37 const callBind = Reflect.apply(functionBind, functionCall, [
38 functionBind,
39 ]);
40 const {
41 create: objectCreate,
42 defineProperties: defineOwnProperties,
43 getPrototypeOf: getPrototype,
44 } = Object;
45 const { [ITERATOR]: arrayIterator } = Array.prototype;
46 const {
47 next: arrayIteratorNext,
48 } = getPrototype([][ITERATOR]());
49 const argumentIterablePrototype = {
50 [ITERATOR]() {
51 return {
52 next: callBind(
53 arrayIteratorNext,
54 call(arrayIterator, this.args, []),
55 ),
56 };
57 },
58 };
59 return {
60 bind: ($, boundThis, boundArgs) =>
61 callBind(
62 $,
63 boundThis,
64 ...objectCreate(
65 argumentIterablePrototype,
66 { args: { value: boundArgs } },
67 ),
68 ),
69 makeCallable: ($, name = undefined) =>
70 defineOwnProperties(callBind(functionCall, $), {
71 length: { value: $.length + 1 },
72 name: { value: name ?? $.name ?? "" },
73 }),
74 };
75 })();
76
77 export const {
78 /**
79 * Calls the provided function with the provided this value and
80 * arguments list.
81 *
82 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
83 * must be passed as an arraylike.
84 */
85 call,
86
87 /**
88 * Constructs the provided function with the provided arguments list
89 * and new target.
90 *
91 * ☡ This is effectively an alias for `Reflect.construct`—the
92 * arguments must be passed as an arraylike.
93 */
94 construct,
95
96 /** Returns whether the provided value is a constructor. */
97 isConstructor,
98 } = (() => {
99 const { apply, construct } = Reflect;
100 return {
101 call: (target, thisArgument, argumentsList) =>
102 apply(target, thisArgument, argumentsList),
103 construct: (target, argumentsList, ...args) =>
104 args.length > 0
105 ? construct(target, argumentsList, args[0])
106 : construct(target, argumentsList),
107 isConstructor: ($) =>
108 completesNormally(() =>
109 // Try constructing a new object with the provided value as its
110 // `new.target`. This will throw if the provided value is not a
111 // constructor.
112 construct(function () {}, [], $)
113 ),
114 };
115 })();
116
117 /**
118 * Returns whether calling the provided function with no `this` value
119 * or arguments completes normally; that is, does not throw an error.
120 *
121 * ☡ This function will throw an error if the provided argument is not
122 * callable.
123 */
124 export const completesNormally = ($) => {
125 if (!isCallable($)) {
126 // The provided value is not callable; this is an error.
127 throw new TypeError(
128 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
129 );
130 } else {
131 // The provided value is callable.
132 try {
133 // Attempt to call the function and return true if this succeeds.
134 $();
135 return true;
136 } catch {
137 // Calling the function did not succeed; return false.
138 return false;
139 }
140 }
141 };
142
143 /**
144 * Returns the provided value.
145 *
146 * ※ This function can be called as a constructor. When used in an
147 * `extends` clause and called via `super`, it will set the value of
148 * `this` to the provided value, enabling it to be extended with
149 * private class features.
150 */
151 export const identity = function ($) {
152 return $;
153 };
154
155 /** Returns whether the provided value is callable. */
156 export const isCallable = ($) => typeof $ === "function";
157
158 /**
159 * Returns whether the provided object inherits from the prototype of
160 * the provided function.
161 */
162 export const ordinaryHasInstance = makeCallable(
163 Function.prototype[Symbol.hasInstance],
164 "ordinaryHasInstance",
165 );
This page took 0.053525 seconds and 3 git commands to generate.