extension.md (8491B)
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 ```