Current: Hypermedia Client and VRSJMP Command Bar Client
- [X] Runtime Pubsub
- [X] Hypermedia Client Design
- [X] VRSJMP Command Bar Client
Goal: Build Core Runtime Features
- [ ] Init File
- [ ] File IO for Simple Storage
- [ ] Command IO
- [ ] Process Links and Supervisor
- [ ] Rust Macros for Code Compression
- [ ] Lyric Macros
- [X] Parallel Development / Release Instances
- [ ] Daemon Installation Hook
Goal: Deploy VRS to the Cloud as a Rust Backend Service This is in service of desktop + web + mobile replacement for Things / Org Mode
See Also: Integration with paradigm service
Goal: Replicate Emacs-like Development Workflow to build VRS software E.g. Emacs Eval, SLIME-like workflows for molding Software
Goal: Non-connection-backed spawn
from kernel.rs
Purpose:
- Spawn from Process / REPL
- Spawn from Init File
Goal: Replace existing pid 0
hardcoding w/ service mgmt
Skip interface / imports map for this one - see also “Runtime Linking”
Convenience APIs to see running services and some level of introspection
- [X]
register
- [X]
ls-srv
- [X]
find-srv
Goal: Provide better wrappers over call
callee
serves unique named-interface w/ a set of callable symbol IDs:
- Runtime linker records: The PID of callee, and exported symbols
caller
calls bind
, which:
- Requests service information from runtime linker
- Runtime linker returns a set of
SymbolId
andVal
to add intoEnv
- Newly added bindings use
send pid
recv pid id
to send + wait for result
Want svc
for service and bind
for callers
See also - pattern matching / destructuring
Maybe also cover imports / exported interface and discoverability
# *** service-side
(srv :launcher
:export '(get_favs, add_favs))
# *** client side (gets imports!)
(bind_srv :launcher)
- Support pub/sub channels
- Proof-of-Concept: Pub/Sub listening to “counter” variable on new task execution model
- Reactivity of PubSub over PubSub
- Method to access pubsub topic via
vrsctl
Goal: Build CLI Hypermedia Client over VRS See also - “Revisit Client Connection”
APIs to manipulate lyric S-Exp more effectively See also s-explib https://github.com/janestreet/sexp
See Also: CLI Client Shell
- [ ] DOM API - Set Content of “Page”
- [ ] DOM API - Get Element with ID
Goal: Spawn Link to be notified of errors in spawn-ed processes
Purpose:
- Service Mgmt on Startup (?)
- Restarting on error
Read / Write S-Expressions to File
Goal: Replace hard-coded data in launcher.ll
Goal: Better interface over Command
to access:
- Process output
- Exit code
Goal: Revisit protocol b/t/ Runtime / Client w/ New Learnings
Idea - Few standardized interactions?
- REPL - text only (?)
- Command-Bar / Browser (List, Search, Select, Navigate)
Learnings:
- Instead of always sending
Form
, what client wants depends:- REPL always wants text - it’d rather have error message v.s. “cannot serialize” error
- How to reconcile with Hypermedia API?
How to handle nesting? e.g. calling a yielding call in peval while it is already yielding Nested Yielding + Input routing for connection
Allows:
- Currently running program as “url”
- Interactions on that program are routed
- “Interrupt” to exit to prompt, like Ctrl-c
Similar to read
and input()
working in shell + python respectively
- Start a process that infinite loops,
(loop 5)
- Show that other screen is still responsive
Goal: Unified Interface to persistent storage Goal: Expose entities in runtime programs via core primitive
Define actions on entities to act on shape of data I.e. the hypermedia is generated based rules on entities it matches
“What can I do with this data” - like Emacs Embark Thought - Running embark-like mechanics is itself a cmd - i.e. “show me embark meno on this item”
Give VRs capability to see (web pages, screen), Hear (user, text) and prompt for input (text, voice, selection)
Add cond
macro to avoid pyramid of if
.
Goal: Provide way to extract params from messages w/o indexing
Add pattern matching conditional branching
Test: Make sure that (a b a)
only matches (1 2 1)
but not (1 2 3)
A process can send a lambda (which captures its environment) to another process to break process isolation
Repro Steps
- Define variable in process A,
count
, and a functioninc
updating that variable - Send the
inc
lambda to another process B viasend
- Call
inc
from process B
Issue: Above can be used to update process A from process B without message passing
Not an issue in erlang / elixir due to pure functional types. No values mutating via references captured.
Thought: Allow but discourage passing lambdas
Currently, escaping backslash is hacked together - see lexer for strings
Add proper support for:
- \n
- '
- "
And appropriate printing of those values
(Streaming?) https://clojure.org/guides/threading_macros
- Thread first, thread last, etc
and EXPR...
- left-to-right, and stop if one expr evaluates to #f
. Otherwise #t
or
- left-to-right, stop if one expr evaluates to NOT #f
Support richer lambda list
Alternate Idea: Only support rest
- but build convenience for:
- Supplying value for optional arg from
rest
list - Extracting keyword argument from
rest
list - [ ]
optional
- [ ]
rest
- [ ]
keyword
arguments
Using the value of symbol instead of symbol
E.g. (match '(+ a b)
would not work, since +
would be bound to Val::Symbol
instead of Val::NativeFn
Allow creating and running fibers within lyric? E.g. Use Yielding Function as an Iterator
CallFrames have base pointer to stack Local variable access is relative to base pointer Removes need for Env - Env is implicit (?)
Wait… how do closures work?
Goal: Replace /scripts/serve.sh
hack w/ proper init rcfile load path
Goal: Introspect running runtime via logs
Better Error Reporting when Process Crashes Lots of noise for fiber execution.
// E.g. below fails in =call= BUT the real error is at ping_pong :interface '(echo) - echo is not defined for this program
let echo_prog = r#" (begin
(spawn (lambda () (begin
(defn ping (msg) (list "pong" msg))
(defn pong (msg) (list "ping" msg))
(srv :ping_pong :interface '(echo)))))
(list
(call (find-srv :ping_pong) '(:ping "hi"))
(call (find-srv :ping_pong) '(:pong "bye")))
)"#;
Goal: When runtime crashes, auto restart
Quickly capture notes, todos, thoughts, tasks Recall + Search them
Use query string! e.g.:
- Open URL typed, instead of selecting item
- Pass query string to command
- Run CLI command from launchbar
Exercises Embark Mechanics + Consuming Program Output
Generate hypermedia based on ps
output
A replacement for “self-texting”
A chat interface for:
- Storing information (notes, messages, URLs, etc)
- Recalling information
- Timeline Record
- Brainstorming
- Task Management
- “Assistant”
Macro expansions, powered by LLM to generate UI Use bret-blocks to interactively tweak
# Key idea: It's generated, and prompt is inline - but evaluation is NOT at runtime. Develop time expansion
(prompt "An user interface for X"
...)
Goal: Recovering from process or host failure
Goal: Moving snapshot of running program from machine to machine
E.g. built-in lambda bindings - e.g. call_fn
/// Binding for call
pub(crate) fn call_fn() -> Lambda {
Lambda {
params: vec![SymbolId::from("pid"), SymbolId::from("msg")],
code: compile(
&parse(
r#"
(begin
(def r (ref))
(send pid (list r (self) msg))
(get (recv (list r 'any)) 1))
"#,
)
.unwrap()
.into(),
)
.unwrap(),
parent: None,
}
}
E.g. srv
definition - srv_fn
Extend existing emacs mode with inferior mode comint shell, like run-python
Add ability to sign a script from editor - e.g. (sign "Ey.....")
and workflow
with Emacs
Seamless flow from REPL / Past Interaction, into durable program
E.g. See values
E.g. See value of watch
-ed topic
See Jane Street
See:
- Processes / Services
- Messages
Inspiration - Bret Victor Ladders of Abstraction
Quickly do interactive “prop testing”
- Go from a function
- Parameterize over range of values
- Immediately see output results
Do this… in source?
Extend to visual elements? UI:
- Visually drag and adjust parameters, which get reflected in source
(defn my-fn (a b)
(+ a b))
(defn my-ui (title subtitle)
(list :title title
:subtitle subtitle))
(bret-block
(my-fn (a :from 0 :to 10)
(b :from 0 :to 10))
; => See matrix of results
(render (my-ui (title :in '("ONE" "TWO" ...))
(subtitle in '("one" "two" ...))))
)
- [ ] CLI to subscribe to a topic that is the “interface”
- [ ] Hypermedia Interface shows the “Actions”
- [ ] “Search Query” can be provided via CLI
- [ ] Actions can be invoked via CLI - Opening Apps, URLs, “Pushing” secondary menus
(defn child (parent_pid)
(def results '())
(subscribe :my_topic)
(send parent :child_ready) # ** BUG ** - parent was not defined here!
(loop (match (recv '(topic_updated _ _))
((_ t :done) (send parent (list :child_results results)))
((_ t val) (set results (push results (list t val))))
(_ (error "Unexpected result"))
)))
(def parent (self))
(def child (spawn (lambda () (child parent))))
(recv :child_ready)
(publish :my_topic :one)
(publish :my_topic :two)
(publish :my_topic :three)
(publish :my_topic :done)
(recv '(:child_results _))
Test case to repro
//! Tests for [Client] Pubsub API
use lyric::parse;
use vrs::{Client, Connection, Form, KeywordId, Program, Runtime};
/// Test Client::subscribe between client and runtime service process
#[tokio::test]
async fn client_pubsub_process_and_service() {
let rt = Runtime::new();
// counter service
let prog = Program::from_expr(
r#"(begin
(def count 0)
(defn increment (n)
(set count (+ count n))
(publish :count count))
(srv :counter :interface '(increment)))
"#,
)
.unwrap();
rt.run(prog).await.unwrap();
let (local, remote) = Connection::pair().unwrap();
let client = Client::new(local);
rt.handle_conn(remote).await.unwrap();
// subscribe then increment
let mut sub = client.subscribe(KeywordId::from("count")).await.unwrap();
client
.request(parse("(increment 1)").unwrap())
.await
.unwrap();
client
.request(parse("(increment 10)").unwrap())
.await
.unwrap();
client
.request(parse("(increment 31)").unwrap())
.await
.unwrap();
assert_eq!(
sub.recv().await.unwrap(),
Form::Int(1),
"should publish 1 after (increment 1)"
);
}
// TODO: Pubsub from another client
I.e. killing connection does NOT terminate proc.
Errors in REPL are sub-par quality
Record of “I want to be able to” thoughts
Open a visual structured editor for building interfaces from source, with changes reflected in source
E.g. (completing-read)
and (interactive "f")
in Emacs