functionalscript 0.0.258 → 0.0.267
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +0 -1
- package/README.md +80 -21
- package/commonjs/README.md +4 -0
- package/commonjs/build/index.js +62 -0
- package/commonjs/module/function/index.js +21 -0
- package/commonjs/module/index.js +24 -11
- package/commonjs/package/index.js +4 -6
- package/io/commonjs/index.js +6 -3
- package/io/commonjs/test.js +1 -1
- package/io/result/index.js +2 -1
- package/package.json +4 -2
- package/commonjs/run/index.js +0 -17
package/README.md
CHANGED
|
@@ -3,34 +3,28 @@
|
|
|
3
3
|
FunctionalScript is a pure functional programming language and a strict subset of
|
|
4
4
|
[ECMAScript](https://en.wikipedia.org/wiki/ECMAScript)/[JavaScript](https://en.wikipedia.org/wiki/JavaScript). It's inspired by
|
|
5
5
|
|
|
6
|
-
- [JSON](https://en.wikipedia.org/wiki/JSON)
|
|
6
|
+
- [JSON](https://en.wikipedia.org/wiki/JSON) as a subset of JavaScript. JSON is also a subset of FunctionalScript.
|
|
7
7
|
- [asm.JS](https://en.wikipedia.org/wiki/Asm.js)/[WebAssembly](https://en.wikipedia.org/wiki/WebAssembly), as a subset of JavaScript.
|
|
8
8
|
- [TypeScript](https://en.wikipedia.org/wiki/TypeScript), as a superset of JavaScript.
|
|
9
9
|
|
|
10
|
-
Try FunctionalScript [here](https://functionalscript.com/).
|
|
11
|
-
|
|
12
10
|
Create a new FunctionalScript repository on GitHub [here](https://github.com/functionalscript/template/generate).
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
npm install -S github:functionalscript/functionalscript
|
|
18
|
-
```
|
|
12
|
+
Try FunctionalScript [here](https://functionalscript.com/).
|
|
19
13
|
|
|
20
14
|
## 1. Design Principles
|
|
21
15
|
|
|
22
16
|
In FunctionalScript:
|
|
23
17
|
|
|
24
18
|
- Any module is a valid JavaScript module
|
|
19
|
+
- Code should not have [side-effects](https://en.wikipedia.org/wiki/Side_effect_(computer_science)). Any JavaScript statement, expression, or function which has a side effect is not allowed in FunctionalScript. There are no exceptions to this rule, such as `unsafe` code which can be found in Rust, C#, and other languages.
|
|
25
20
|
- A module can't depend on non FunctionalScript module.
|
|
26
|
-
-
|
|
27
|
-
- It also has no standard library, only a safe subset of standard JavaScript API is allowed.
|
|
21
|
+
- It also has no standard library, only a safe subset of standard JavaScript API can be used without referencing other modules.
|
|
28
22
|
|
|
29
23
|
## 2. Outlines
|
|
30
24
|
|
|
31
25
|
### 2.1. Module Ecosystem
|
|
32
26
|
|
|
33
|
-
FunctionalScript uses
|
|
27
|
+
FunctionalScript uses [CommonJS](https://en.wikipedia.org/wiki/CommonJS) conventions as a module ecosystem. For example,
|
|
34
28
|
|
|
35
29
|
```js
|
|
36
30
|
const thirdPartyModule = require('third-party-package/module')
|
|
@@ -40,20 +34,26 @@ const result = thirdPartyModule.someFunction('hello')
|
|
|
40
34
|
|
|
41
35
|
### 2.2. Packages
|
|
42
36
|
|
|
43
|
-
FunctionalScript uses a `package.json` file to define a package. This file is compatible with Node.js `package.json
|
|
44
|
-
The prefered way to refence dependencies is to use a GitHub URL.
|
|
37
|
+
FunctionalScript uses a `package.json` file to define a package. This file is compatible with [Node.js `package.json`](https://nodejs.org/en/knowledge/getting-started/npm/what-is-the-file-package-json/).
|
|
38
|
+
The prefered way to refence dependencies is to use a GitHub URL. These dependencies in a `package.json` file could look like this,
|
|
45
39
|
|
|
46
40
|
```json
|
|
47
41
|
{
|
|
48
|
-
...
|
|
42
|
+
// ...
|
|
49
43
|
"dependencies": {
|
|
50
44
|
"third-party-package": "github:exampleorg/thirdpartypackage"
|
|
51
45
|
}
|
|
52
|
-
...
|
|
46
|
+
// ...
|
|
53
47
|
}
|
|
54
48
|
```
|
|
55
49
|
|
|
56
|
-
|
|
50
|
+
**Note:** this repository is also a FunctionalScript package, and it can be used as a library. To install this package, use
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
npm install -S github:functionalscript/functionalscript
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2.3. Module Structure
|
|
57
57
|
|
|
58
58
|
A module is a file with the `.js` extention. It contains three parts: references to other modules, definitions, and exports. For example
|
|
59
59
|
|
|
@@ -64,6 +64,7 @@ const math = require('math')
|
|
|
64
64
|
|
|
65
65
|
// 2. definitions
|
|
66
66
|
const myConst = 42
|
|
67
|
+
// addition(a)(b) = a + b
|
|
67
68
|
const addition = a => b => a + b
|
|
68
69
|
const add42 = addition(42)
|
|
69
70
|
const _10digitsOfPi = math.calculatePi(10)
|
|
@@ -81,12 +82,18 @@ module.exports = {
|
|
|
81
82
|
// 1. references
|
|
82
83
|
const first = require('./first.js')
|
|
83
84
|
|
|
85
|
+
// 2. definitions
|
|
84
86
|
const _42plus7 = first.add42(7)
|
|
87
|
+
|
|
88
|
+
// 3. exports
|
|
89
|
+
module.exports = {
|
|
90
|
+
_42plus7,
|
|
91
|
+
}
|
|
85
92
|
```
|
|
86
93
|
|
|
87
|
-
### 2.
|
|
94
|
+
### 2.4. References To Other Modules
|
|
88
95
|
|
|
89
|
-
The format of references is `const ANYNAME = require('PATH_TO_A_MODULE')`. For example
|
|
96
|
+
The format of references is `const ANYNAME = require('PATH_TO_A_MODULE')`. For example,
|
|
90
97
|
|
|
91
98
|
```js
|
|
92
99
|
const math = require('math')
|
|
@@ -94,7 +101,7 @@ const algebra = require('math/algebra')
|
|
|
94
101
|
const localFile = require('../some-directory/some-file.js')
|
|
95
102
|
```
|
|
96
103
|
|
|
97
|
-
### 2.
|
|
104
|
+
### 2.5. Definitions
|
|
98
105
|
|
|
99
106
|
The format of defintions is `const NAME = EXPRESSION`, where the `EXPRESSION` is a subset of [JavaScript expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators).
|
|
100
107
|
|
|
@@ -114,10 +121,12 @@ const nestedStructure = {
|
|
|
114
121
|
}
|
|
115
122
|
```
|
|
116
123
|
|
|
117
|
-
|
|
124
|
+
See [3. Expressions](#3-Expressions).
|
|
125
|
+
|
|
126
|
+
### 2.6. Exports
|
|
118
127
|
|
|
119
128
|
The format of exports is `module.exports = { A_LIST_OF_EXPORTED_DEFINITIONS }`. There should be only one `module.exports` at
|
|
120
|
-
the end of a FunctionalScript file. For example
|
|
129
|
+
the end of a FunctionalScript file. For example,
|
|
121
130
|
|
|
122
131
|
```js
|
|
123
132
|
module.exports = {
|
|
@@ -126,3 +135,53 @@ module.exports = {
|
|
|
126
135
|
structure,
|
|
127
136
|
}
|
|
128
137
|
```
|
|
138
|
+
|
|
139
|
+
## 3. Expressions
|
|
140
|
+
|
|
141
|
+
Expressions could fall under these categories:
|
|
142
|
+
|
|
143
|
+
- Literals:
|
|
144
|
+
- Number Literals, e.g. `0`, `3.14`, `4e8`
|
|
145
|
+
- Boolean Literals: `true` or `false`
|
|
146
|
+
- A `null` Literal
|
|
147
|
+
- An `undefined` Literal
|
|
148
|
+
- String Literals, e.g. `"Hello world!"`
|
|
149
|
+
- Complex Structures
|
|
150
|
+
- Arrays, e.g. `[2, 5]`
|
|
151
|
+
- Objects, e.g. `{ a: "Hello", b: "world!" }`
|
|
152
|
+
- Arrow functions, e.g. `x => x * 2`
|
|
153
|
+
- Operators
|
|
154
|
+
- Comparison Operators: `===`, `!==`, `>`, `>=`, `<`, `<=`
|
|
155
|
+
- Arithmetic Operators: `+`, `-`, `*`, `/`, `%`, `**`
|
|
156
|
+
- Bitwise Operators: `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>`
|
|
157
|
+
- Logical Operators: `&&`, `||`, `!`, `??`
|
|
158
|
+
- Conditional Operator, e.g. `condition ? val1 : val2`
|
|
159
|
+
- Template Literals, e.g. `string ${expression}`
|
|
160
|
+
- `typeof`
|
|
161
|
+
- Relations Operators: `in`, `instanceof`.
|
|
162
|
+
- Member Operators: `.`, `[]`.
|
|
163
|
+
|
|
164
|
+
Note: the `.` member operator has prohibitted property names, such as `constructor` and `push`. To access such properties, it's recommeded to use the `Object.getPropertyDescriptor` function.
|
|
165
|
+
|
|
166
|
+
## 4. Arrow Functions
|
|
167
|
+
|
|
168
|
+
An arrow function is also known as [a lambda function](https://en.wikipedia.org/wiki/Anonymous_function).
|
|
169
|
+
The format of an arrow function is `ARGUMENT_NAME => FUNCTION_BODY`. An arrow function must have either a single argument or no arguments at all. For example
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
x => x * 2
|
|
173
|
+
a => a + 4
|
|
174
|
+
s => `template literal ${s}`
|
|
175
|
+
() => 'hello' // an arrow function with no arguments
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A function body is either an expression or a block statement. A block statement format is `{ A_LIST_OF_STATEMENTS }`. For example
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
// a function with one argument and a block statement
|
|
182
|
+
const f = x => {
|
|
183
|
+
const a = 2 + x
|
|
184
|
+
const r = a + 4
|
|
185
|
+
return r
|
|
186
|
+
}
|
|
187
|
+
```
|
package/commonjs/README.md
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const package_ = require('../package')
|
|
2
|
+
const module_ = require('../module')
|
|
3
|
+
const function_ = require('../module/function')
|
|
4
|
+
const { todo } = require('../../dev')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @template M
|
|
8
|
+
* @typedef {{
|
|
9
|
+
* readonly pagkageGet: package_.Get
|
|
10
|
+
* readonly moduleMapInterface: module_.MapInterface<M>
|
|
11
|
+
* readonly moduleId: module_.Id
|
|
12
|
+
* readonly moduleMap: M
|
|
13
|
+
* }} Config
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @template M
|
|
18
|
+
* @typedef {readonly[module_.State, M]} Result
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @type {(packageGet: package_.Get) =>
|
|
23
|
+
* <M>(moduleMapInterface: module_.MapInterface<M>) =>
|
|
24
|
+
* (compile: function_.Compile) =>
|
|
25
|
+
* (moduleId: module_.Id) =>
|
|
26
|
+
* (moduleMap: M) =>
|
|
27
|
+
* Result<M>
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
const getOrBuild = packageGet => moduleMapInterface => compile => moduleId => moduleMap => {
|
|
31
|
+
const moduleIdStr = module_.idToString(moduleId)
|
|
32
|
+
|
|
33
|
+
/** @type {() => Result<typeof moduleMap>} */
|
|
34
|
+
const notFound = () => [['error', ['file not found']], moduleMap]
|
|
35
|
+
|
|
36
|
+
/** @type {(e: module_.Error) => Result<typeof moduleMap>} */
|
|
37
|
+
const error = e => {
|
|
38
|
+
/** @type {module_.State} */
|
|
39
|
+
const state = ['error', e]
|
|
40
|
+
moduleMapInterface.insert(moduleIdStr)(state)
|
|
41
|
+
return [state, moduleMap]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const m = moduleMapInterface.at(moduleIdStr)(moduleMap)
|
|
45
|
+
if (m !== undefined) { return [m, moduleMap] }
|
|
46
|
+
|
|
47
|
+
const p = packageGet(moduleId.packageId)
|
|
48
|
+
if (p === undefined) { return notFound() }
|
|
49
|
+
|
|
50
|
+
const source = p.file(moduleId.path.join('/'))
|
|
51
|
+
if (source === undefined) { return notFound() }
|
|
52
|
+
|
|
53
|
+
const compileResult = compile(source)
|
|
54
|
+
if (compileResult[0] === 'error') { return error(['compilation error', compileResult[1]]) }
|
|
55
|
+
|
|
56
|
+
return todo()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
/** @readonly */
|
|
61
|
+
getOrBuild,
|
|
62
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An IO interface for creating and running module functions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const result = require('../../../types/result')
|
|
6
|
+
|
|
7
|
+
/** @typedef {<M>(require: Require<M>) => (prior: M) => Result<M>} Function */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @template M
|
|
11
|
+
* @typedef {readonly[result.Result<unknown, unknown>, M]} Result
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @template M
|
|
16
|
+
* @typedef {(path: string) => (prior: M) => Result<M>} Require
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @typedef {(source: string) => result.Result<Function, unknown>} Compile */
|
|
20
|
+
|
|
21
|
+
module.exports = {}
|
package/commonjs/module/index.js
CHANGED
|
@@ -3,17 +3,17 @@ const object = require('../../types/object')
|
|
|
3
3
|
/**
|
|
4
4
|
* @template M
|
|
5
5
|
* @typedef {{
|
|
6
|
-
* readonly at: (moduleId: string) => (moduleMap: M) =>
|
|
7
|
-
* readonly insert: (moduleId: string) => (moduleState:
|
|
8
|
-
* }}
|
|
6
|
+
* readonly at: (moduleId: string) => (moduleMap: M) => State | undefined
|
|
7
|
+
* readonly insert: (moduleId: string) => (moduleState: State) => (moduleMap: M) => M
|
|
8
|
+
* }} MapInterface
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @typedef {|
|
|
13
13
|
* readonly['ok', Module] |
|
|
14
|
-
* readonly['error',
|
|
14
|
+
* readonly['error', Error] |
|
|
15
15
|
* readonly['building']
|
|
16
|
-
* }
|
|
16
|
+
* } State
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -25,11 +25,24 @@ const object = require('../../types/object')
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* @typedef {|
|
|
28
|
-
* 'file not found' |
|
|
29
|
-
* '
|
|
30
|
-
* 'runtime error' |
|
|
31
|
-
* 'circular reference'
|
|
32
|
-
* }
|
|
28
|
+
* ['file not found'] |
|
|
29
|
+
* ['compilation error', unknown] |
|
|
30
|
+
* ['runtime error'] |
|
|
31
|
+
* ['circular reference']
|
|
32
|
+
* } Error
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {{
|
|
37
|
+
* readonly packageId: string
|
|
38
|
+
* readonly path: readonly string[]
|
|
39
|
+
* }} Id
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/** @type {(id: Id) => string} */
|
|
43
|
+
const idToString = ({ packageId, path }) => `${packageId}/${path.join('/')}`
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
/** @readonly */
|
|
47
|
+
idToString,
|
|
48
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
const json = require('../../json')
|
|
2
|
-
const
|
|
3
|
-
const object = require('../../types/object')
|
|
4
|
-
const run = require('../run')
|
|
2
|
+
const dependencies = require('./dependencies')
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* @typedef {{
|
|
8
6
|
* readonly name: string
|
|
9
7
|
* readonly version: string
|
|
10
|
-
* readonly dependencies?:
|
|
8
|
+
* readonly dependencies?: dependencies.DependenciesJson
|
|
11
9
|
* }} PackageJson
|
|
12
10
|
*/
|
|
13
11
|
|
|
@@ -16,7 +14,7 @@ const isPackageJson = j => {
|
|
|
16
14
|
if (!json.isObject(j)) { return false }
|
|
17
15
|
if (typeof j.name !== 'string') { return false }
|
|
18
16
|
if (typeof j.version !== 'string') { return false }
|
|
19
|
-
if (!
|
|
17
|
+
if (!dependencies.isDependenciesJson(j.dependencies)) { return false }
|
|
20
18
|
return true
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -27,7 +25,7 @@ const isPackageJson = j => {
|
|
|
27
25
|
* }} Package
|
|
28
26
|
*/
|
|
29
27
|
|
|
30
|
-
/** @typedef {(packageId: string) => Package | undefined}
|
|
28
|
+
/** @typedef {(packageId: string) => Package | undefined} Get */
|
|
31
29
|
|
|
32
30
|
module.exports = {
|
|
33
31
|
/** @readonly */
|
package/io/commonjs/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
const { tryCatch } = require('../result')
|
|
2
2
|
const { unwrap } = require('../../types/result')
|
|
3
|
-
const
|
|
3
|
+
const moduleFunction = require('../../commonjs/module/function')
|
|
4
4
|
|
|
5
|
-
/** @type {(f: Function) =>
|
|
5
|
+
/** @type {(f: Function) => moduleFunction.Function} */
|
|
6
6
|
const build = f => immutableRequire => mutableData => {
|
|
7
7
|
/** @type {(path: string) => unknown} */
|
|
8
8
|
const mutableRequire = path => {
|
|
9
9
|
const [result, data] = immutableRequire(path)(mutableData)
|
|
10
|
+
// Side effect: setting a variable from a nested function (closure)
|
|
11
|
+
// is not allowed in FunctionalScript.
|
|
10
12
|
mutableData = data
|
|
11
13
|
return unwrap(result)
|
|
12
14
|
}
|
|
@@ -18,8 +20,9 @@ const build = f => immutableRequire => mutableData => {
|
|
|
18
20
|
return [result, mutableData]
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
/** @type {
|
|
23
|
+
/** @type {moduleFunction.Compile} */
|
|
22
24
|
const compile = source =>
|
|
25
|
+
// Side effect: a `Function` constructor is not allowed in FunctionalScript.
|
|
23
26
|
tryCatch(() => build(Function('module', 'require', `"use strict";${source}`)))
|
|
24
27
|
|
|
25
28
|
module.exports = {
|
package/io/commonjs/test.js
CHANGED
package/io/result/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const result = require('../../types/result')
|
|
|
2
2
|
|
|
3
3
|
/** @type {<T>(f: () => T) => result.Result<T, unknown>} */
|
|
4
4
|
const tryCatch = f => {
|
|
5
|
+
// Side effect: `try catch` is not allowed in FunctionalScript.
|
|
5
6
|
try {
|
|
6
7
|
return result.ok(f())
|
|
7
8
|
} catch (e) {
|
|
@@ -12,4 +13,4 @@ const tryCatch = f => {
|
|
|
12
13
|
module.exports = {
|
|
13
14
|
/** @readonly */
|
|
14
15
|
tryCatch,
|
|
15
|
-
}
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.267",
|
|
4
4
|
"description": "FunctionalScript is a functional subset of JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"functional-programming",
|
|
20
20
|
"closure",
|
|
21
21
|
"pure-functional",
|
|
22
|
-
"typescript"
|
|
22
|
+
"typescript",
|
|
23
|
+
"programming-language",
|
|
24
|
+
"lazy-evaluation"
|
|
23
25
|
],
|
|
24
26
|
"bugs": {
|
|
25
27
|
"url": "https://github.com/functionalscript/functionalscript/issues"
|
package/commonjs/run/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const result = require('../../types/result')
|
|
2
|
-
|
|
3
|
-
/** @typedef {<T>(req: Require<T>) => (prior: T) => ModuleResult<T>} Module*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @template T
|
|
7
|
-
* @typedef {readonly[result.Result<unknown, unknown>, T]} ModuleResult
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @template T
|
|
12
|
-
* @typedef {(path: string) => (prior: T) => ModuleResult<T>} Require
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/** @typedef {(source: string) => result.Result<Module, unknown>} Compile */
|
|
16
|
-
|
|
17
|
-
module.exports = {}
|