simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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 ```