UNPKG

8.49 kBMarkdownView Raw
1# Extension
2
3The 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
7import { create, all } from 'mathjs'
8
9const math = create(all)
10
11math.import(/* ... */)
12```
13
14The function `import` accepts an object with functions and variables, or an array with factory functions. It has the following syntax:
15
16```js
17math.import(functions: Object [, options: Object])
18```
19
20Where:
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
42The following code example shows how to import a function and a value into math.js:
43
44```js
45// define new functions and variables
46math.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
54math.myvalue * 2 // 84
55math.hello('user') // 'hello, user!'
56
57const parser = math.parser()
58parser.evaluate('myvalue + 10') // 52
59parser.evaluate('hello("user")') // 'hello, user!'
60```
61
62## Import external libraries
63
64External 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.
67The libraries must be installed using npm:
68
69 $ npm install numbers
70 $ npm install numeric
71
72The libraries can be easily imported into math.js using `import`.
73In order to convert math.js specific data types like `Matrix` to primitive types
74like `Array`, the imported functions can be wrapped by enabling `{wrap: true}`.
75
76```js
77import { create, all } from 'mathjs'
78import * as numbers from 'numbers'
79import * as numeric from 'numeric'
80
81// create a mathjs instance and import the numbers.js and numeric.js libraries
82const math = create(all)
83math.import(numbers, {wrap: true, silent: true})
84math.import(numeric, {wrap: true, silent: true})
85
86// use functions from numbers.js
87math.fibonacci(7) // 13
88math.evaluate('fibonacci(7)') // 13
89
90// use functions from numeric.js
91math.evaluate('eig([1, 2; 4, 3])').lambda.x // [5, -1]
92```
93
94
95## Typed functions
96
97Typed functions can be created using `math.typed`. A typed function is a function
98which does type checking on the input arguments. It can have multiple signatures.
99And can automatically convert input types where needed.
100
101A typed function can be created like:
102
103```js
104const 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
115Typed functions can be merged as long as there are no conflicts in the signatures.
116This allows for extending existing functions in math.js with support for new
117data types.
118
119```js
120// create a new data type
121function MyType (value) {
122 this.value = value
123}
124MyType.prototype.isMyType = true
125MyType.prototype.toString = function () {
126 return 'MyType:' + this.value
127}
128
129// define a new datatype
130math.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
139const 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
146math.import({add: add})
147
148// use the new type
149const ans = math.add(new MyType(2), new MyType(3)) // returns MyType(5)
150console.log(ans) // outputs 'MyType:5'
151```
152
153Detailed 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
161Regular JavaScript functions can be imported in math.js using `math.import`:
162
163```js
164math.import({
165 myFunction: function (a, b) {
166 // ...
167 }
168})
169```
170
171The function can be stored in a separate file:
172
173```js
174export function myFunction (a, b) {
175 // ...
176}
177```
178
179Which can be imported like:
180
181```js
182import { myFunction } from './myFunction.js'
183
184math.import({
185 myFunction
186})
187```
188
189An issue arises when `myFunction` needs functionality from math.js:
190it doesn't have access to the current instance of math.js when in a separate file.
191Factory functions can be used to solve this issue. A factory function allows to inject dependencies into a function when creating it.
192
193A syntax of factory function is:
194
195```js
196factory(name: string, dependencies: string[], create: function, meta?: Object): function
197```
198
199where:
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
224Here an example of a factory function which depends on `multiply`:
225
226```js
227import { factory, create, all } from 'mathjs'
228
229// create a factory function
230const name = 'negativeSquare'
231const dependencies = ['multiply', 'unaryMinus']
232const 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:
239const multiply = (a, b) => a * b
240const unaryMinus = (a) => -a
241const negativeSquare = createNegativeSquare({ multiply, unaryMinus })
242console.log(negativeSquare(3)) // -9
243
244// or import the factory in a mathjs instance and use it there
245const math = create(all)
246math.import(createNegativeSquare)
247console.log(math.negativeSquare(4)) // -16
248console.log(math.evaluate('negativeSquare(5)')) // -25
249```
250
251You may wonder why you would inject functions `multiply` and `unaryMinus`
252instead of just doing these calculations inside the function itself. The
253reason is that this makes the factory function `negativeSquare` work for
254different implementations: numbers, BigNumbers, units, etc.
255
256```js
257import { Decimal } from 'decimal.js'
258
259// create an instance of our negativeSquare supporting BigNumbers instead of numbers
260const multiply = (a, b) => a.mul(b)
261const unaryMinus = (a) => new Decimal(0).minus(a)
262const negativeSquare = createNegativeSquare({ multiply, unaryMinus })
263```
\No newline at end of file