1 | # Extension
|
2 |
|
3 | The library can easily be extended with functions and variables using the
|
4 | [`import`](../reference/functions/import.md) function. The `import` function is available on a mathjs instance, which can be created using the `create` function.
|
5 |
|
6 | ```js
|
7 | import { create, all } from 'mathjs'
|
8 |
|
9 | const math = create(all)
|
10 |
|
11 | math.import(/* ... */)
|
12 | ```
|
13 |
|
14 | The function `import` accepts an object with functions and variables, or an array with factory functions. It has the following syntax:
|
15 |
|
16 | ```js
|
17 | math.import(functions: Object [, options: Object])
|
18 | ```
|
19 |
|
20 | Where:
|
21 |
|
22 | - `functions` is an object or array containing the functions and/or values to be
|
23 | imported. `import` support regular values and functions, typed functions
|
24 | (see section [Typed functions](#typed-functions)), and factory functions
|
25 | (see section [Factory functions](#factory-functions)).
|
26 | An array is only applicable when it contains factory functions.
|
27 |
|
28 | - `options` is an optional second argument with options.
|
29 | The following options are available:
|
30 |
|
31 | - `{boolean} override`
|
32 | If `true`, existing functions will be overwritten. The default value is `false`.
|
33 | - `{boolean} silent`
|
34 | If `true`, the function will not throw errors on duplicates or invalid
|
35 | types. Default value is `false`.
|
36 | - `{boolean} wrap`
|
37 | If `true`, the functions will be wrapped in a wrapper function which
|
38 | converts data types like Matrix to primitive data types like Array.
|
39 | The wrapper is needed when extending math.js with libraries which do not
|
40 | support the math.js data types. The default value is `false`.
|
41 |
|
42 | The following code example shows how to import a function and a value into math.js:
|
43 |
|
44 | ```js
|
45 | // define new functions and variables
|
46 | math.import({
|
47 | myvalue: 42,
|
48 | hello: function (name) {
|
49 | return 'hello, ' + name + '!'
|
50 | }
|
51 | })
|
52 |
|
53 | // defined functions can be used in both JavaScript as well as the parser
|
54 | math.myvalue * 2 // 84
|
55 | math.hello('user') // 'hello, user!'
|
56 |
|
57 | const parser = math.parser()
|
58 | parser.evaluate('myvalue + 10') // 52
|
59 | parser.evaluate('hello("user")') // 'hello, user!'
|
60 | ```
|
61 |
|
62 | ## Import external libraries
|
63 |
|
64 | External libraries like
|
65 | [numbers.js](https://github.com/sjkaliski/numbers.js) and
|
66 | [numeric.js](https://github.com/sloisel/numeric) can be imported as follows.
|
67 | The libraries must be installed using npm:
|
68 |
|
69 | $ npm install numbers
|
70 | $ npm install numeric
|
71 |
|
72 | The libraries can be easily imported into math.js using `import`.
|
73 | In order to convert math.js specific data types like `Matrix` to primitive types
|
74 | like `Array`, the imported functions can be wrapped by enabling `{wrap: true}`.
|
75 |
|
76 | ```js
|
77 | import { create, all } from 'mathjs'
|
78 | import * as numbers from 'numbers'
|
79 | import * as numeric from 'numeric'
|
80 |
|
81 | // create a mathjs instance and import the numbers.js and numeric.js libraries
|
82 | const math = create(all)
|
83 | math.import(numbers, {wrap: true, silent: true})
|
84 | math.import(numeric, {wrap: true, silent: true})
|
85 |
|
86 | // use functions from numbers.js
|
87 | math.fibonacci(7) // 13
|
88 | math.evaluate('fibonacci(7)') // 13
|
89 |
|
90 | // use functions from numeric.js
|
91 | math.evaluate('eig([1, 2; 4, 3])').lambda.x // [5, -1]
|
92 | ```
|
93 |
|
94 |
|
95 | ## Typed functions
|
96 |
|
97 | Typed functions can be created using `math.typed`. A typed function is a function
|
98 | which does type checking on the input arguments. It can have multiple signatures.
|
99 | And can automatically convert input types where needed.
|
100 |
|
101 | A typed function can be created like:
|
102 |
|
103 | ```js
|
104 | const max = typed('max', {
|
105 | 'number, number': function (a, b) {
|
106 | return Math.max(a, b)
|
107 | },
|
108 |
|
109 | 'BigNumber, BigNumber': function (a, b) {
|
110 | return a.greaterThan(b) ? a : b
|
111 | }
|
112 | })
|
113 | ```
|
114 |
|
115 | Typed functions can be merged as long as there are no conflicts in the signatures.
|
116 | This allows for extending existing functions in math.js with support for new
|
117 | data types.
|
118 |
|
119 | ```js
|
120 | // create a new data type
|
121 | function MyType (value) {
|
122 | this.value = value
|
123 | }
|
124 | MyType.prototype.isMyType = true
|
125 | MyType.prototype.toString = function () {
|
126 | return 'MyType:' + this.value
|
127 | }
|
128 |
|
129 | // define a new datatype
|
130 | math.typed.addType({
|
131 | name: 'MyType',
|
132 | test: function (x) {
|
133 | // test whether x is of type MyType
|
134 | return x && x.isMyType
|
135 | }
|
136 | })
|
137 |
|
138 | // use the type in a new typed function
|
139 | const add = typed('add', {
|
140 | 'MyType, MyType': function (a, b) {
|
141 | return new MyType(a.value + b.value)
|
142 | }
|
143 | })
|
144 |
|
145 | // import in math.js, extend the existing function `add` with support for MyType
|
146 | math.import({add: add})
|
147 |
|
148 | // use the new type
|
149 | const ans = math.add(new MyType(2), new MyType(3)) // returns MyType(5)
|
150 | console.log(ans) // outputs 'MyType:5'
|
151 | ```
|
152 |
|
153 | Detailed information on typed functions is available here:
|
154 | [https://github.com/josdejong/typed-function](https://github.com/josdejong/typed-function)
|
155 |
|
156 |
|
157 |
|
158 |
|
159 | ## Factory functions
|
160 |
|
161 | Regular JavaScript functions can be imported in math.js using `math.import`:
|
162 |
|
163 | ```js
|
164 | math.import({
|
165 | myFunction: function (a, b) {
|
166 | // ...
|
167 | }
|
168 | })
|
169 | ```
|
170 |
|
171 | The function can be stored in a separate file:
|
172 |
|
173 | ```js
|
174 | export function myFunction (a, b) {
|
175 | // ...
|
176 | }
|
177 | ```
|
178 |
|
179 | Which can be imported like:
|
180 |
|
181 | ```js
|
182 | import { myFunction } from './myFunction.js'
|
183 |
|
184 | math.import({
|
185 | myFunction
|
186 | })
|
187 | ```
|
188 |
|
189 | An issue arises when `myFunction` needs functionality from math.js:
|
190 | it doesn't have access to the current instance of math.js when in a separate file.
|
191 | Factory functions can be used to solve this issue. A factory function allows to inject dependencies into a function when creating it.
|
192 |
|
193 | A syntax of factory function is:
|
194 |
|
195 | ```js
|
196 | factory(name: string, dependencies: string[], create: function, meta?: Object): function
|
197 | ```
|
198 |
|
199 | where:
|
200 |
|
201 | - `name` is the name of the created function.
|
202 | - `dependencies` is an array with names of the dependent functions.
|
203 | - `create` is a function which creates the function.
|
204 | An object with the dependencies is passed as first argument.
|
205 | - `meta` An optional object which can contain any meta data you want.
|
206 | This will be attached as a property `meta` on the created function.
|
207 | Known meta data properties used by the mathjs instance are:
|
208 | - `isClass: boolean` If true, the created function is supposed to be a
|
209 | class, and for example will not be exposed in the expression parser
|
210 | for security reasons.
|
211 | - `lazy: boolean`. By default, everything is imported lazily by `import`.
|
212 | only as soon as the imported function or constant is actually used, it
|
213 | will be constructed. A function can be forced to be created immediately
|
214 | by setting `lazy: false` in the meta data.
|
215 | - `isTransformFunction: boolean`. If true, the created function is imported
|
216 | as a transform function. It will not be imported in `math` itself, only
|
217 | in the internal `mathWithTransform` namespace that is used by the
|
218 | expression parser.
|
219 | - `recreateOnConfigChange: boolean`. If true, the imported factory will be
|
220 | created again when there is a change in the configuration. This is for
|
221 | example used for the constants like `pi`, which is different depending
|
222 | on the configsetting `number` which can be numbers or BigNumbers.
|
223 |
|
224 | Here an example of a factory function which depends on `multiply`:
|
225 |
|
226 | ```js
|
227 | import { factory, create, all } from 'mathjs'
|
228 |
|
229 | // create a factory function
|
230 | const name = 'negativeSquare'
|
231 | const dependencies = ['multiply', 'unaryMinus']
|
232 | const createNegativeSquare = factory(name, dependencies, function ({ multiply, unaryMinus }) {
|
233 | return function negativeSquare (x) {
|
234 | return unaryMinus(multiply(x, x))
|
235 | }
|
236 | })
|
237 |
|
238 | // create an instance of the function yourself:
|
239 | const multiply = (a, b) => a * b
|
240 | const unaryMinus = (a) => -a
|
241 | const negativeSquare = createNegativeSquare({ multiply, unaryMinus })
|
242 | console.log(negativeSquare(3)) // -9
|
243 |
|
244 | // or import the factory in a mathjs instance and use it there
|
245 | const math = create(all)
|
246 | math.import(createNegativeSquare)
|
247 | console.log(math.negativeSquare(4)) // -16
|
248 | console.log(math.evaluate('negativeSquare(5)')) // -25
|
249 | ```
|
250 |
|
251 | You may wonder why you would inject functions `multiply` and `unaryMinus`
|
252 | instead of just doing these calculations inside the function itself. The
|
253 | reason is that this makes the factory function `negativeSquare` work for
|
254 | different implementations: numbers, BigNumbers, units, etc.
|
255 |
|
256 | ```js
|
257 | import { Decimal } from 'decimal.js'
|
258 |
|
259 | // create an instance of our negativeSquare supporting BigNumbers instead of numbers
|
260 | const multiply = (a, b) => a.mul(b)
|
261 | const unaryMinus = (a) => new Decimal(0).minus(a)
|
262 | const negativeSquare = createNegativeSquare({ multiply, unaryMinus })
|
263 | ``` |
\ | No newline at end of file |