msgpack is a lovely little serialization format. As a
JSON replacement, it saves bandwidth while preserving native language features
(e.g. tuples, records, objects, dates).
By leveraging msgpack, every scrapscript
program/expression can be crammed into “flat” scraps.
Because flat scraps encode a full programming language, you can send unambiguous
programs/queries across a wire:
"Joy" |> (name -> ok { name })
-- EXPR 0xc7201f
-- "Joy" 0xa34a6f79
-- name=name 0x81a46e616d65c704786e616d65
-- REC 0xc17b7d
-- OK 0xc10006
-- name 0xc704786e616d65
-- -> 0xc12d3e
-- |> 0xc17c3e
…and represent its evaluated result:
ok { name = "Joy" }
-- EXPR 0xc7101f
-- name="Joy" 0x81a46e616d65a34a6f79
-- REC 0xc17b7d
-- OK 0xc10006
This first-class serialization format confers non-obvious benefits:
- a compact format affords a canonical format for hashing
- when computation is cheaper than bandwidth, you can send programs instead of
sending data - both code & data can share the same tooling for compression, analysis,
optimization, etc. - cheaper to store programs on disk (or KV store) and load into memory
- it’s a simple/convenient cross-platform compilation target
- with types, encoders/decoders can be magically inferred/generated/typechecked
at compile-time - msgpack is supported by 50+ languages/environments; all
msgpack messages are already scraps, and scrap-specific extensions can be
incrementally implemented in each host language
Literals
scrapscript | msgpack |
---|---|
$$bool::false () |
c2 |
$$bool::true () |
c3 |
() |
c0 |
11 |
d0 0b |
1234 |
cd 04d2 |
1.2 |
ca 3f99999a |
123456789.1234 |
cb 419d6f34547e5c92 |
-& |
cb fff0 |
+& |
cb 7ff0 |
;ff |
ff |
"hi" |
a2 6869 |
"🐀🐀" |
ac 7ac97bca7ac97bca |
;;aGVsbG8gd29ybGQ= |
c4 0cab aGVsbG8gd29ybGQ= |
Expressions
Flat scrap expressions are written in
reverse polish notation:
scrapscript | flatscrap | msgpack | |
---|---|---|---|
1 + 2 |
1 2 + |
c7 05 1f 01 02 c1002b |
ext8 length extID 1 2 + |
(1 + 2) * 3 |
1 2 + 3 * |
c7 08 1f 01 02 c1002b 03 c1002a |
ext8 length extID 1 2 + 3 * |
1 + 2 * 3 |
1 2 3 * + |
c7 08 1f 01 02 03 c1002a c1002b |
ext8 length extID 1 2 3 * + |
Within the expression, prepend each 2-byte operator with 0xc1
.
Here are many of the normal computer operations you know and love:
num | 002b |
+ |
add |
num | 002d |
- |
subtract |
num | 002a |
* |
multiply |
num | 002f |
/ |
divide |
num | 005e |
^ |
pow |
num | 0025 |
% |
modulo |
num | 2525 |
%% |
remainder |
comp | 003c |
< |
lt |
comp | 003e |
> |
gt |
comp | 3c3d |
<= |
lte |
comp | 3e3d |
>= |
gte |
many | 2b2b |
++ |
concat |
many | 3e2b |
>+ |
prepend |
many | 2b3c |
+< |
append |
bitty | 2626 |
&& |
and |
bitty | 7c7c |
⏐⏐ |
or |
bitty | 5e5e |
^^ |
xor |
nofun | 3d3d |
== |
equal |
nofun | 2f3d |
/= |
not equal |
fun | 3c7c |
<⏐ |
pipe left |
fun | 7c3e |
⏐> |
pipe right |
fun | 3c3c |
<< |
compose left |
fun | 3e3e |
>> |
compose right |
Fancier operators are also available:
scrapscript | flatscrap | msgpack |
---|---|---|
t::a |
t "a" :. |
c7081f d47674 a161 c13a2e |
t::a () |
t "a" () :: |
c7081f d47674 a161 c0 c13a3a |
rec ~ a |
rec "a" ~ |
c70b1f c70476726563 a161 c1007e |
a ? a == 1 |
a "a" 1 == ? |
c70c1f d47261 a161 01 c13d3d c1003f |
f 1 2 |
f 1 2 apply2 |
c7081f d47666 01 02 c12032 |
⏐ 0 -> 0 ⏐ n -> n - 1 |
0 0 -> n n 1 - -> ⏐ |
c7121f 00 00 c12d3e d4766e d4766e 01 c1002d c12d3e c12d7c |
arity | bytes | char | desc |
---|---|---|---|
2 | 007e |
~ |
access |
2 | 3a2e |
:. |
tagger |
3 | 3a3a |
:: |
tag |
2 | 003f |
? |
assert |
2 | 2d3e |
-> |
function |
2 | 007c |
⏐ |
case |
2 | 003a |
: |
annotate |
2 | 003a |
: |
annotate |
2 | 2031 |
apply 1 | |
3 | 2032 |
apply 2 | |
4 | 2033 |
apply 3 |
To keep things tiny, we also cram common expressions into the operator space:
arity | bytes | scrap |
---|---|---|
1 | 0015 |
result::no |
1 | 0006 |
result::ok |
0 | 0030 |
maybe::none |
1 | 0031 |
maybe::some |
2 | 0032 |
pair |
3 | 0033 |
trip |
4 | 0034 |
quad |
2 | 6c6d |
$$list-map |
1 | 6e39 |
$$max |
1 | 6e30 |
$$min |
… | … |
References
Flat scraps distinguish between
scope variables (76
),
native functions (72
), and
expression hashes (24
):
scrapscript | msgpack | |
---|---|---|
x |
d4 76 78 |
fixext1 extID “x” |
$$x |
d4 72 78 |
fixext1 extID “x” |
$$min |
c7 03 72 6d696e |
ext8 length extID “max” |
$md5;;aG93ZHk= |
c7 11 24 00 aG93ZHk= |
ext8 length extID algoID data |
ID | algo |
---|---|
00 |
MD5 |
01 |
SHA1 |
02 |
SHA2 |
03 |
SHA3 |
62 |
BLAKE |
63 |
CityHash |
66 |
FNV |
6d |
Murmur |
72 |
RIPEMD |
73 |
SipHash |
78 |
xxHash |
Structures
A scrapscript list corresponds to a plain msgpack array:
scrapscript | msgpack | |
---|---|---|
[1,2,3] |
93 010203 |
fixarray 1 2 3 |
The other structures are represented as
maps:
scrapscript | msgpack | |
---|---|---|
[ ;;oWE=';;AQ==, ;;oWI=';;Ag== ] |
82 a161 01 a162 02 |
map8 “a” 1 “b” 2 |
dict/from-list [ "a"'1, "b"'2 ] |
c7161f c10064 82a16101a16202 |
expr d {a=1,b=2} |
#a c #b d |
c71a1f c10023 82a161d47263a162d47265 |
expr # {a=c,b=d} |
{ a = 1, b = 2 } |
c7161f c17b7d 82a16101a16202 |
expr {} {a=1,b=2} |
{ a } |
c7151f c17b7d 81a161d47261 |
expr {} {a=a} |
{ a = 1, ..z } |
c7171f c17b7d 82a16101a0d4727a |
expr {} {a=1,""=z} |
{ a = 1, ... } |
c7151f c17b7d 82a16101a0a0 |
expr {} {a=1,""=""} |
Because maps are ambiguously typed, use special operators to clarify the
intended structure:
arity | bytes | char | desc |
---|---|---|---|
1 | 0064 |
d |
dict |
1 | 7b7d |
{} |
rec |
1 | 0023 |
# |
type |
2 | 002e |
. |
where |
2 | 0021 |
! |
await |
Evaluation
Astute observers may have noticed some strange operators like
where and
await:
scrapscript | flatscrap | msgpack |
---|---|---|
x . x = 1 . y = 2 |
{"x"=1,"y"=2} x . |
c7141f 82a17801a17902 d47678 c1002e |
x <- 1 ! y <- 2 ! x |
{"x"=1,"y"=2} x ! |
c7141f 82a17801a17902 d47678 c10021 |
To build a basic interpreter, push values on a stack and perform operations
in-place. Bind function arguments and environment variables into a scoped map
that can be copied from.
And someday soon, we’ll all be able to do irresponsible stuff like this:
// javascript
const fib = scrap.decode(await fetch("yard.scrap.land/tom/fib"));
console.log(fib(40)); // 102334155
// javascript
import fib from "yard.scrap.land/tom/fib";
console.log(fib(40)); // 102334155
// golang
import (
"fmt"
"yard.scrap.land/tom"
)
func main() {
fmt.Println(tom.Fib(40)) // 102334155
}
# python
async with aiohttp.ClientSession() as sess:
async with sess.get("yard.scrap.land/tom/fib") as res:
fib = scrap.decode(await res.read())
print(fib(40)) # 102334155
// javascript
const fib = n => {
let phi = (1 + Math.sqrt(5)) / 2;
let asymp = Math.pow(phi, n) / Math.sqrt(5);
return Math.round(asymp);
};
await fetch("api.example.com", {
headers: { "Content-Type": "application/scrap" },
method: "POST",
body: scrap.encode(fib),
});
Premium IPTV Experience with line4k
Experience the ultimate entertainment with our premium IPTV service. Watch your favorite channels, movies, and sports events in stunning 4K quality. Enjoy seamless streaming with zero buffering and access to over 10,000+ channels worldwide.
