Update README

parent 3a9a8425
......@@ -85,7 +85,10 @@ Here's some precompiled binaries courtesy of Gitlab CI:
## Agenda
- [ ] An event loop. Like `libuv` but... not `libuv`. Something small and manageable.
- [ ] An event loop. I *think* this portion can be written in JavaScript, with just
a bit of C assist.
Like `libuv` but... not `libuv`. Something small and manageable.
I was very tempted to use `nanomsg` to create a queue (still might if I can ever get it
to compile). But I am currently thinking [OpenMP](https://en.wikipedia.org/wiki/OpenMP#Thread_creation) is the dead simplest
......@@ -94,66 +97,8 @@ you have it. Then we just need to write an ethernet driver in assembly. ;)
- [ ] Pure `musl`? Or whatever? We need to eventually isolate all OS kernel calls since we won't need no stinkin' operating system.
## Module strategy
Still working on it. Current thoughts:
1. No magic. Node made a mistake when it decided to automatically wrap files
in an IIFE to make it "easier". It's a deceptive abstraction.\*
2. No globals. Implicit globals are mistakes waiting to happen.
3. Given 1 and 2, I'm leaning towards Modules are Functions. End of Story.
(Since functional programming is superior anyway, why encourage anything else?)
4. Modules as functions might not work with async-ish stuff. We might need a postMessage
type of structure. We'll see. We could make each module a thread and modules
communicate using `nanomsg`. Or just some ZMQ compatible
\* We'll need to make a compatibility layer though, which will probably involve
automatically wrapping files in a magical IIFE.
### Packages vs Modules
Node makes a distinction between packages and modules. This is confusing and annoying to me.
However it IS important to distinguish between "files I wrote and trust" and "libraries written by a third party I don't trust".
I also don't want to encourage users to use giant monolithic files.
So here's what I'm thinking:
- All file modules have a single top-level function.
- All file modules are named the same name as their top-level function (minus the '.js').
- All file modules in the same package are loaded into a shared context:
- All file modules in a package share the same heap.
- All file modules are in that context's "global" scope.
People should love it because it
- is less typing. You never have to `require('./some/local/file')`
- enforces consistancy. You always know what filename goes with what function.
- is simple to browserify. `cat **.js` and wrap in an IFFE.
What about subfolders? Indeed that is sometimes very convenient for organizational purposes.
I'm gonna break with the stupid `index.js` convention (because having 20 tabs open
in your editor that all say `index.js` is not helpful. At all.) and say that
you can refactor `'Module.js'` by putting it in a folder of the same name:
`'Module/Module.js'`. Then that function will be registered as `global.Module`
and any sibling files in that folder are placed into the same closure. E.g.
folder directories = anonymous closure, where only `FolderName.js` gets exposed.
That allows some level of portability (copy a folder from an existing project to
a new project) and allows functions to be organized while still allowing foldered
functions access to all the module-global functions. Essentially what I'm hoping
is that we can tie the "information hiding" aspect of closures to the "visibility
hiding" of file directories. In the end, we need a representation that is NOT
tied down to a file-system per se. Because file systems need to die. But there
is definitely a use case where you want to separate some parts from other parts,
even though you trust both parts and need them to be able to interact directly
with each other sometimes. What should we call that? Weak encapsulation?
So next is the great question of how "packages" aka functions that don't trust
each other should interact. Ideas include:
- dependency injection
- packages are services
- shared heaps for argument passing (hmm that ties in with WebAssembly ideas doesn't it?)
- postMessage based interaction?
- proxy objects? have awesome revocation semantics
- [x] Networking! I've got a very basic networking library added that appears to work so far.
# API v0
......@@ -197,6 +142,40 @@ are strings.
This is just here temporarily until I add "proper" support for stdout. It's
like `console.log`.
## Event Loop
Everyone else is using `libuv`. But libuv actually does *more* than I think
is actually necessary. And using it would means that `dukboot` would have exactly the same
bugs / vulnerabilities as all the other stuff out there. So why not be crazy
and try something different?
A lot of JS programmers are mystified by the Event Loop. This is largely because
its existance is *implicit*, and you can go for years writing JavaScript and
never even be aware of it. It is used via functions like `setImmediate()` and
`process.nextTick()` and `requestAnimationFrame()` and `requestIdleCallback()`
which are all poorly named\* and obscure what's really going on. Once you understand
it, it's extremely simple! It's a cooperative scheduler. Callbacks are put in a queue and
get executed in order for the most part. Some things, like animation frames in
the browser or I/O in node, get executed out-of-order to satisfy constraints:
animationFrame callbacks need to happen between window paint events for instance
and I/O callbacks cannot be called until the data they are asking for arrives.
But at its heart, it's just a handful of queues that the scheduler picks from.
So I figure... why not make that explicit? Why not literally represent the
queues as a JS arrays, so you can write your own scheduling logic? You can see
my initial fooling around with this idea in the `examples/time/` directory.
Conceptually, you would have one Duktape.Thread be the "scheduler" and have a
while loop that pops callbacks from the queue(s) and run them until the queue is
empty. The callbacks themselves, other Duktape Threads, or the C code will insert
new callbacks into the queues so the scheduler thread has stuff to execute.
\* except requestAnimationFrame, which actually does what the name suggests
### Queues.timers (name subject to change)
Right now this is the only queue. I'm storing it in the heap-stash. The code in
`examples/time/` shows you can actually pull off the whole event loop in JS without
using any C code. This may prove a totally awesome idea or a completely
terrible one; I don't know yet.
## Module System
### compile ( filepath : String ) : Function
......
# Module strategy
Still working on it. Current thoughts:
1. No magic. Node made a mistake when it decided to automatically wrap files
in an IIFE to make it "easier". It's a deceptive abstraction.\*
2. No globals. Implicit globals are mistakes waiting to happen.
3. Given 1 and 2, I'm leaning towards Modules are Functions. End of Story.
(Since functional programming is superior anyway, why encourage anything else?)
4. Modules as functions might not work with async-ish stuff. We might need a postMessage
type of structure. We'll see. We could make each module a thread and modules
communicate using `nanomsg`. Or just some ZMQ compatible
\* We'll need to make a compatibility layer though, which will probably involve
automatically wrapping files in a magical IIFE.
## Packages vs Modules
Node makes a distinction between packages and modules. This is confusing and annoying to me.
However it IS important to distinguish between "files I wrote and trust" and "libraries written by a third party I don't trust".
I also don't want to encourage users to use giant monolithic files.
## A possible simple and secure module algorithm
Here's what I'm thinking:
- All file modules have a single top-level function.
- All file modules are named the same name as their top-level function (minus the '.js').
- All file modules in the same package are loaded into a shared context:
- All file modules in a package share the same heap.
- All file modules are in that context's "global" scope.
People should love it because it
- is less typing. You never have to `require('./some/local/file')`
- enforces consistancy. You always know what filename goes with what function.
- is simple to browserify. `cat **.js` and wrap in an IFFE.
What about subfolders? Indeed that is sometimes very convenient for organizational purposes.
I'm gonna break with the stupid `index.js` convention (because having 20 tabs open
in your editor that all say `index.js` is not helpful. At all.) and say that
you can refactor `'Module.js'` by putting it in a folder of the same name:
`'Module/Module.js'`. Then that function will be registered as `global.Module`
and any sibling files in that folder are placed into the same closure. E.g.
folder directories = anonymous closure, where only `FolderName.js` gets exposed.
That allows some level of portability (copy a folder from an existing project to
a new project) and allows functions to be organized while still allowing foldered
functions access to all the module-global functions. Essentially what I'm hoping
is that we can tie the "information hiding" aspect of closures to the "visibility
hiding" of file directories. In the end, we need a representation that is NOT
tied down to a file-system per se. Because file systems need to die. But there
is definitely a use case where you want to separate some parts from other parts,
even though you trust both parts and need them to be able to interact directly
with each other sometimes. What should we call that? Weak encapsulation?
So next is the great question of how "packages" aka functions that don't trust
each other should interact. Ideas include:
- dependency injection
- packages are services
- shared heaps for argument passing (hmm that ties in with WebAssembly ideas doesn't it?)
- postMessage based interaction?
- proxy objects? have awesome revocation semantics
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment