on d3af603 help sign in
.gitignore−1
since splitsnapshots
unifiedsplit
@@ -1,3 +1,2 @@
1elm-stuff
1elm-stuff
2elm.js
2elm.js
3−zod
LICENSEdeleted−7
@@ -1,7 +0,0 @@
1−Copyright 2023 Ilia Kostiuchenko
2−
3−Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4−
5−The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6−
7−THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md−11
@@ -4,19 +4,8 @@
4
4
5## Usage
5## Usage
6
6
7−### Demo
8−
9−[![DEMO](https://img.youtube.com/vi/tynCzifvIzE/0.jpg)](https://www.youtube.com/watch?v=tynCzifvIzE)
10−
11−
12−## Getting started
13−
14−The easiest way of using this library is by using the [airlock template](https://github.com/ilyakooo0/airlock-template).
15−
16### FFI
7### FFI
17
8
18−This is for setting up this library from scratch.
19−
20Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
9Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
21Unfortunately, Elm does not provide a native implementation.
10Unfortunately, Elm does not provide a native implementation.
22This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
11This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
dev/sink-elm-live.sh → dev/elm-live.shrenamed+1−1
@@ -4,4 +4,4 @@ set -e
4
4
5cd "$(dirname "$0")/../example"
5cd "$(dirname "$0")/../example"
6
6
7−nix run nixpkgs#elmPackages.elm-live -- src/Sink.elm --start-page=index.html -- --output=elm.js --debug "$@"
7+nix run nixpkgs#elmPackages.elm-live -- src/Main.elm --start-page=index.html -- --output=elm.js --debug "$@"
dev/vanilla-elm-live.shdeleted−7
@@ -1,7 +0,0 @@
1−#!/bin/sh
2−
3−set -e
4−
5−cd "$(dirname "$0")/../example"
6−
7−nix run nixpkgs#elmPackages.elm-live -- src/Vanilla.elm --start-page=index.html -- --output=elm.js --debug "$@"
docs/sink.mddeleted−101
@@ -1,101 +0,0 @@
1−# %sink
2−
3−%sink is a state replication system between your %gall agent and your Elm airlock.
4−
5−%sink allows you to avoid having to manually write subscriptions which send semantic diffs between your %gall agent and the frontend.
6−
7−## How it works
8−
9−%sink consists of two parts:
10−1. The %sink library you import into your %gall agent and call appropriate functions at the right points in your agent lifecycle. More on this in a bit.
11−2. The `Ur.Sub.sink` urbit subscription which reconciles the state and hands you the latest version of %gall agent state that you can then `Deconstruct` into Elm data structures.
12−
13−The whole system works by saving the previous version your %gall agent state and diffing the raw nouns that compose your old state and your new state.
14−On first initialization of the %sink system the whole state you supplied is sent to the frontend. Any subsequent changes are sent as a diff between your old state and your new state.
15−
16−## How to use it
17−
18−### The %gall agent part
19−
20−#### Copying library files
21−
22−In order to use %sink you have to copy the following two files into your agent desk into the `lib` directory:
23−1. [example/urbit/lib/noun-diff.hoon](../example/urbit/lib/noun-diff.hoon) -- contains the algorithm for diffing raw nouns.
24−2. [example/urbit/lib/sink.hoon](../example/urbit/lib/sink.hoon) -- contains the logic of syncing the state between your agent and your frontend.
25−
26−#### Adding sync points into your agent
27−
28−First you need to import the %sink library into your agent:
29−
30−```hoon
31−/+ *sink
32−```
33−
34−Next you need to initialize the sink. Put the following declaration somewhere before your agent door:
35−
36−```hoon
37−::
38−:: A deferred expression of the state you want to sync to the frontend.
39−=* entries (tap:j-orm journal.stat)
40−::
41−:: Replace /sync with whatever path you want to use for syncing your state.
42−=/ snik (sink ~[/sync])
43−::
44−:: Next you initialize your sink with your initial agent state.
45−=/ sink (snik entries)
46−```
47−
48−You can have multiple sinks in the same application to sync different parts of your state on different paths.
49−
50−Don't forget to reinitialize your `sink` when your restore your agent state in the `++on-load` arm:
51−
52−```hoon
53−++ on-load
54− |= old-vase=vase
55− ^- (quip card _this)
56− =/ state !<(versioned-state old-vase)
57− ::
58− :: the `sink (snik entries)` is the important bit.
59− `this(state state, sink (snik entries))
60−```
61−
62−Lastly, you need to send sink updates whenever you change your state. Most likely this will be in your `++on-poke` arm:
63−
64−```hoon
65−::
66−:: This line generates a `card` that you need to pass to arvo and updates
67−:: the `sink` to reference the latest state of your agent.
68−=^ card sink (sync:sink entries)
69−```
70−
71−You can look at the [journal.hoon](../example/urbit/app/journal.hoon) for a full example.
72−
73−#### Adding sink the the frontend
74−
75−For your frontend to recieve %sink updates you need to pass the result of calling `Ur.Sub.sink` to the `urbitSubscriptions` field of `Ur.Run.application` or similar function from `Ur.Run`:
76−
77−```elm
78−main : Ur.Run.Program Model Msg
79−main =
80− Ur.Run.application
81− {
82− -- ...
83− urbitSubscriptions =
84− -- ...
85− Ur.Sub.sink
86− { ship = ship
87− , app = "journal"
88− , path = [ "sync" ]
89− , deconstructor =
90− D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b )))
91− |> D.map GotListings
92− }
93− -- ...
94− }
95−```
96−
97−In the `deconstructor` field you specify a `Deconstructor` the deconstructs _the whole_ state that is being synced form the %gall agent.
98−
99−You do not have to deal with diffs. It is handled automatically
100−
101−You can look at [example/src/Sink.elm](../example/src/Sink.elm) for a full example.
elm.json+2−11
@@ -3,18 +3,9 @@
3 "name": "ilyakooo0/airlock",
3 "name": "ilyakooo0/airlock",
4 "summary": "Urbit airlock",
4 "summary": "Urbit airlock",
5 "license": "MIT",
5 "license": "MIT",
6− "version": "1.1.1",
6+ "version": "1.0.0",
7 "exposed-modules": [
7 "exposed-modules": [
8− "Ur",
8+ "Ur", "Ur.Cmd", "Ur.Sub", "Ur.Constructor", "Ur.Deconstructor", "Ur.Phonemic", "Ur.Da", "Ur.Uw", "Ur.Run", "Ur.Types"
9− "Ur.Cmd",
10− "Ur.Sub",
11− "Ur.Constructor",
12− "Ur.Deconstructor",
13− "Ur.Phonemic",
14− "Ur.Da",
15− "Ur.Uw",
16− "Ur.Run",
17− "Ur.Types"
18 ],
9 ],
19 "elm-version": "0.19.0 <= v < 0.20.0",
10 "elm-version": "0.19.0 <= v < 0.20.0",
20 "dependencies": {
11 "dependencies": {
example/urbit/app/journal.hoon → example/journal/app/journal.hoonrenamed+19−26
@@ -1,5 +1,5 @@
1/- *journal
1/- *journal
2−/+ default-agent, dbug, agentio, *sink
2+/+ default-agent, dbug, agentio
3|%
3|%
4+$ versioned-state
4+$ versioned-state
5 $% state-0
5 $% state-0
@@ -17,15 +17,11 @@
17 ?. (has:log-orm log unix-ms)
17 ?. (has:log-orm log unix-ms)
18 unix-ms
18 unix-ms
19 $(time (add unix-ms 1))
19 $(time (add unix-ms 1))
20−--
20+--
21−
22%- agent:dbug
21%- agent:dbug
23−=/ state *state-0
22+=| state-0
24−=* entries (tap:j-orm journal.state)
23+=* state -
25−=/ snik (sink ~[/sync])
26−=/ sink (snik entries)
27^- agent:gall
24^- agent:gall
28−::
29|_ =bowl:gall
25|_ =bowl:gall
30+* this .
26+* this .
31 def ~(. (default-agent this %|) bowl)
27 def ~(. (default-agent this %|) bowl)
@@ -38,8 +34,7 @@
38++ on-load
34++ on-load
39 |= old-vase=vase
35 |= old-vase=vase
40 ^- (quip card _this)
36 ^- (quip card _this)
41− =/ state !<(versioned-state old-vase)
37+ `this(state !<(versioned-state old-vase))
42− `this(state state, sink (snik entries))
43::
38::
44++ on-poke
39++ on-poke
45 |= [=mark =vase]
40 |= [=mark =vase]
@@ -47,28 +42,27 @@
47 |^
42 |^
48 ?> (team:title our.bowl src.bowl)
43 ?> (team:title our.bowl src.bowl)
49 ?. ?=(%journal-action mark) (on-poke:def mark vase)
44 ?. ?=(%journal-action mark) (on-poke:def mark vase)
50− =/ now=@ (unique-time now.bowl log.state)
45+ =/ now=@ (unique-time now.bowl log)
51 =/ act !<(action vase)
46 =/ act !<(action vase)
52 =. state (poke-action act)
47 =. state (poke-action act)
53− =^ card sink (sync:sink entries)
54− :_ this(log.state (put:log-orm log.state now act))
48+ :_ this(log (put:log-orm log now act))
55− ~[(fact:io journal-update+!>(`update`[now act]) ~[/updates]) card]
49+ ~[(fact:io journal-update+!>(`update`[now act]) ~[/updates])]
56 ::
50 ::
57 ++ poke-action
51 ++ poke-action
58 |= act=action
52 |= act=action
59 ^- _state
53 ^- _state
60 ?- -.act
54 ?- -.act
61 %add
55 %add
62− ?< (has:j-orm journal.state id.act)
56+ ?< (has:j-orm journal id.act)
63− state(journal (put:j-orm journal.state id.act txt.act))
57+ state(journal (put:j-orm journal id.act txt.act))
64 ::
58 ::
65 %edit
59 %edit
66− ?> (has:j-orm journal.state id.act)
60+ ?> (has:j-orm journal id.act)
67− state(journal (put:j-orm journal.state id.act txt.act))
61+ state(journal (put:j-orm journal id.act txt.act))
68 ::
62 ::
69 %del
63 %del
70− ?> (has:j-orm journal.state id.act)
64+ ?> (has:j-orm journal id.act)
71− state(journal +:(del:j-orm journal.state id.act))
65+ state(journal +:(del:j-orm journal id.act))
72 ==
66 ==
73 --
67 --
74::
68::
@@ -78,7 +72,6 @@
78 ?> (team:title our.bowl src.bowl)
72 ?> (team:title our.bowl src.bowl)
79 ?+ path (on-watch:def path)
73 ?+ path (on-watch:def path)
80 [%updates ~] `this
74 [%updates ~] `this
81− [%sync ~] [~[flush:sink] this]
82 ==
75 ==
83::
76::
84++ on-peek
77++ on-peek
@@ -92,14 +85,14 @@
92 [%all ~]
85 [%all ~]
93 :^ ~ ~ %journal-update
86 :^ ~ ~ %journal-update
94 !> ^- update
87 !> ^- update
95− [now %jrnl entries]
88+ [now %jrnl (tap:j-orm journal)]
96 ::
89 ::
97 [%before @ @ ~]
90 [%before @ @ ~]
98 =/ before=@ (rash i.t.t.t.path dem)
91 =/ before=@ (rash i.t.t.t.path dem)
99 =/ max=@ (rash i.t.t.t.t.path dem)
92 =/ max=@ (rash i.t.t.t.t.path dem)
100 :^ ~ ~ %journal-update
93 :^ ~ ~ %journal-update
101 !> ^- update
94 !> ^- update
102− [now %jrnl (tab:j-orm journal.state `before max)]
95+ [now %jrnl (tab:j-orm journal `before max)]
103 ::
96 ::
104 [%between @ @ ~]
97 [%between @ @ ~]
105 =/ start=@
98 =/ start=@
@@ -108,7 +101,7 @@
108 =/ end=@ (add 1 (rash i.t.t.t.t.path dem))
101 =/ end=@ (add 1 (rash i.t.t.t.t.path dem))
109 :^ ~ ~ %journal-update
102 :^ ~ ~ %journal-update
110 !> ^- update
103 !> ^- update
111− [now %jrnl (tap:j-orm (lot:j-orm journal.state `end `start))]
104+ [now %jrnl (tap:j-orm (lot:j-orm journal `end `start))]
112 ==
105 ==
113 ::
106 ::
114 [%x %updates *]
107 [%x %updates *]
@@ -116,13 +109,13 @@
116 [%all ~]
109 [%all ~]
117 :^ ~ ~ %journal-update
110 :^ ~ ~ %journal-update
118 !> ^- update
111 !> ^- update
119− [now %logs (tap:log-orm log.state)]
112+ [now %logs (tap:log-orm log)]
120 ::
113 ::
121 [%since @ ~]
114 [%since @ ~]
122 =/ since=@ (rash i.t.t.t.path dem)
115 =/ since=@ (rash i.t.t.t.path dem)
123 :^ ~ ~ %journal-update
116 :^ ~ ~ %journal-update
124 !> ^- update
117 !> ^- update
125− [now %logs (tap:log-orm (lot:log-orm log.state `since ~))]
118+ [now %logs (tap:log-orm (lot:log-orm log `since ~))]
126 ==
119 ==
127 ==
120 ==
128::
121::
example/urbit/desk.bill → example/journal/desk.billrenamed

The content is the same.

example/urbit/desk.docket-0 → example/journal/desk.docket-0renamed

The content is the same.

example/urbit/gen/journal/add.hoon → example/journal/gen/journal/add.hoonrenamed

The content is the same.

example/urbit/lib/agentio.hoon → example/journal/lib/agentio.hoonrenamed

The content is the same.

example/urbit/lib/dbug.hoon → example/journal/lib/dbug.hoonrenamed

The content is the same.

example/urbit/lib/default-agent.hoon → example/journal/lib/default-agent.hoonrenamed

The content is the same.

example/urbit/lib/docket.hoon → example/journal/lib/docket.hoonrenamed

The content is the same.

example/urbit/lib/journal.hoon → example/journal/lib/journal.hoonrenamed

The content is the same.

example/urbit/lib/skeleton.hoon → example/journal/lib/skeleton.hoonrenamed

The content is the same.

example/urbit/mar/bill.hoon → example/journal/mar/bill.hoonrenamed

The content is the same.

example/urbit/mar/docket-0.hoon → example/journal/mar/docket-0.hoonrenamed

The content is the same.

example/urbit/mar/hoon.hoon → example/journal/mar/hoon.hoonrenamed

The content is the same.

example/urbit/mar/jam.hoon → example/journal/mar/jam.hoonrenamed

The content is the same.

example/urbit/mar/journal/action.hoon → example/journal/mar/journal/action.hoonrenamed

The content is the same.

example/urbit/mar/journal/update.hoon → example/journal/mar/journal/update.hoonrenamed

The content is the same.

example/urbit/mar/json.hoon → example/journal/mar/json.hoonrenamed

The content is the same.

example/urbit/mar/kelvin.hoon → example/journal/mar/kelvin.hoonrenamed

The content is the same.

example/urbit/mar/mime.hoon → example/journal/mar/mime.hoonrenamed

The content is the same.

example/urbit/mar/noun.hoon → example/journal/mar/noun.hoonrenamed

The content is the same.

example/urbit/mar/txt-diff.hoon → example/journal/mar/txt-diff.hoonrenamed

The content is the same.

example/urbit/mar/txt.hoon → example/journal/mar/txt.hoonrenamed

The content is the same.

example/urbit/sur/docket.hoon → example/journal/sur/docket.hoonrenamed

The content is the same.

example/urbit/sur/journal.hoon → example/journal/sur/journal.hoonrenamed

The content is the same.

example/urbit/sys.kelvin → example/journal/sys.kelvinrenamed

The content is the same.

example/script.js+1−16
@@ -2,22 +2,7 @@ import { fetchEventSource } from "/fetch-event-source.js"
2
2
3const uid = `${Math.floor(Date.now() / 1000)}-${Math.random()}`;
3const uid = `${Math.floor(Date.now() / 1000)}-${Math.random()}`;
4
4
5−function searchForInit(obj) {
6− if (obj.init) {
7− return obj.init;
8− } else {
9− for (let key in obj) {
10− if (obj.hasOwnProperty(key)) {
11− const result = searchForInit(obj[key]);
12− if (result) {
13− return result;
14− }
15− }
16− }
17− }
18−}
19−
20−let app = searchForInit(Elm)({ node: document.getElementById("elm"), flags: { uid } });
5+let app = Elm.Main.init({ node: document.getElementById("elm"), flags: { uid } });
21
6
22app.ports.createEventSource.subscribe((url) => {
7app.ports.createEventSource.subscribe((url) => {
23 fetchEventSource(url, {
8 fetchEventSource(url, {
example/src/Vanilla.elm → example/src/Main.elmrenamed+16−21
@@ -1,4 +1,4 @@
1−port module Vanilla exposing (main)
1+port module Main exposing (main)
2
2
3import BigInt exposing (BigInt)
3import BigInt exposing (BigInt)
4import Browser exposing (Document)
4import Browser exposing (Document)
@@ -12,6 +12,7 @@ import Ur
12import Ur.Cmd
12import Ur.Cmd
13import Ur.Constructor as C
13import Ur.Constructor as C
14import Ur.Deconstructor as D
14import Ur.Deconstructor as D
15+import Ur.Requests
15import Ur.Run
16import Ur.Run
16import Ur.Sub
17import Ur.Sub
17import Widget
18import Widget
@@ -19,11 +20,6 @@ import Widget.Icon as Icon
19import Widget.Material as Material
20import Widget.Material as Material
20
21
21
22
22−url : String
23−url =
24− "http://localhost:8080"
25−
26−
27main : Ur.Run.Program Model Msg
23main : Ur.Run.Program Model Msg
28main =
24main =
29 Ur.Run.application
25 Ur.Run.application
@@ -35,20 +31,20 @@ main =
35 , shipName = Nothing
31 , shipName = Nothing
36 }
32 }
37 , Cmd.batch
33 , Cmd.batch
38− [ Ur.logIn url "lidlut-tabwed-pillex-ridrup"
34+ [ Ur.logIn "http://localhost:8080" "lidlut-tabwed-pillex-ridrup"
39 |> Cmd.map (result (Debug.toString >> Error) (always Noop))
35 |> Cmd.map (result (Debug.toString >> Error) (always Noop))
40− , Ur.getShipName url |> Cmd.map (result (always Noop) GotShipName)
36+ , Ur.getShipName "http://localhost:8080" |> Cmd.map (result (always Noop) GotShipName)
41− , Ur.scry
37+ , Ur.Requests.scry
42− { url = url
38+ { url = "http://localhost:8080"
43 , agent = "journal"
39 , agent = "journal"
44 , path = [ "entries", "all" ]
40 , path = [ "entries", "all" ]
45 , error = Noop
41 , error = Noop
46 , success =
42 , success =
47 D.cell D.ignore
43 D.cell D.ignore
48 (D.cell (D.const D.cord "jrnl")
44 (D.cell (D.const D.cord "jrnl")
49− (D.list (D.cell D.bigint D.cord))
45+ (D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b ))))
46+ |> D.map GotListings
50 )
47 )
51− |> D.map (\( (), ( (), listings ) ) -> GotListings listings)
52 }
48 }
53 ]
49 ]
54 |> Ur.Cmd.cmd
50 |> Ur.Cmd.cmd
@@ -73,7 +69,7 @@ main =
73 , onEventSourceMsg = onEventSourceMessage
69 , onEventSourceMsg = onEventSourceMessage
74 , onUrlChange = \_ -> Noop
70 , onUrlChange = \_ -> Noop
75 , onUrlRequest = \_ -> Noop
71 , onUrlRequest = \_ -> Noop
76− , urbitUrl = \_ -> url
72+ , urbitUrl = \_ -> "http://localhost:8080"
77 }
73 }
78
74
79
75
@@ -177,15 +173,14 @@ type JournalUpdate
177 | Delete BigInt
173 | Delete BigInt
178
174
179
175
180−decodeJournalUpdate : D.Deconstructor JournalUpdate
176+decodeJournalUpdate : D.Deconstructor (JournalUpdate -> a) a
181decodeJournalUpdate =
177decodeJournalUpdate =
178+ D.cell D.ignore <|
182− D.oneOf
179+ D.oneOf
183− [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Add id txt)
180+ [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map Add
184− , D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
181+ , (D.cell (D.const D.cord "edit") <| D.cell D.bigint D.cord) |> D.map Edit
185− , D.cell (D.const D.cord "del") D.bigint |> D.map (\( (), id ) -> Delete id)
182+ , D.cell (D.const D.cord "del") D.bigint |> D.map Delete
186− ]
183+ ]
187− |> D.cell D.ignore
188− |> D.map (\( (), upd ) -> upd)
189
184
190
185
191view : Model -> Document Msg
186view : Model -> Document Msg
example/src/Sink.elmdeleted−199
@@ -1,199 +0,0 @@
1−port module Sink exposing (main)
2−
3−import BigInt exposing (BigInt)
4−import Browser exposing (Document)
5−import Element exposing (..)
6−import Element.Border as Border
7−import Heroicons.Outline
8−import Json.Decode as JD
9−import Task
10−import Time
11−import Ur
12−import Ur.Cmd
13−import Ur.Constructor as C
14−import Ur.Deconstructor as D
15−import Ur.Run
16−import Ur.Sub
17−import Widget
18−import Widget.Icon as Icon
19−import Widget.Material as Material
20−
21−
22−url : String
23−url =
24− "http://localhost:8080"
25−
26−
27−main : Ur.Run.Program Model Msg
28−main =
29− Ur.Run.application
30− { init =
31− \_ _ ->
32− ( { error = ""
33− , entries = Nothing
34− , newEntry = ""
35− , shipName = Nothing
36− }
37− , Cmd.batch
38− [ Ur.logIn url "lidlut-tabwed-pillex-ridrup"
39− |> Cmd.map (result (Debug.toString >> Error) (always Noop))
40− , Ur.getShipName url |> Cmd.map (result (always Noop) GotShipName)
41− ]
42− |> Ur.Cmd.cmd
43− )
44− , update = update
45− , view = view
46− , subscriptions = always Sub.none
47− , createEventSource = createEventSource
48− , urbitSubscriptions =
49− \{ shipName } ->
50− case shipName of
51− Just ship ->
52− Ur.Sub.batch
53− [ Ur.Sub.sink
54− { ship = ship
55− , app = "journal"
56− , path = [ "sync" ]
57− , deconstructor =
58− D.list (D.cell D.bigint D.cord)
59− |> D.map GotListings
60− }
61− ]
62−
63− _ ->
64− Ur.Sub.none
65− , onEventSourceMsg = onEventSourceMessage
66− , onUrlChange = \_ -> Noop
67− , onUrlRequest = \_ -> Noop
68− , urbitUrl = \_ -> url
69− }
70−
71−
72−type alias Model =
73− { error : String
74− , entries : Maybe (List ( BigInt, String ))
75− , newEntry : String
76− , shipName : Maybe String
77− }
78−
79−
80−type Msg
81− = Noop
82− | Error String
83− | GotListings (List ( BigInt, String ))
84− | UpdateNewEntry String
85− | DeleteEntry BigInt
86− | AddEntry String
87− | RunCmd (Ur.Cmd.Cmd Msg)
88− | GotShipName String
89−
90−
91−update : Msg -> Model -> ( Model, Ur.Cmd.Cmd Msg )
92−update msg model =
93− case msg of
94− Noop ->
95− ( model, Ur.Cmd.none )
96−
97− Error err ->
98− ( { model | error = err }, Ur.Cmd.none )
99−
100− GotListings entries ->
101− ( { model | entries = Just entries }, Ur.Cmd.none )
102−
103− UpdateNewEntry txt ->
104− ( { model | newEntry = txt }, Ur.Cmd.none )
105−
106− DeleteEntry id ->
107− ( model
108− , Ur.Cmd.poke
109− { ship = "~zod"
110− , agent = "journal"
111− , mark = "journal-action"
112− , noun = C.cell (C.cord "del") (C.bigint id)
113− }
114− )
115−
116− AddEntry txt ->
117− ( { model | newEntry = "" }
118− , Time.now
119− |> Task.perform
120− (\time ->
121− Ur.Cmd.poke
122− { ship = "~zod"
123− , agent = "journal"
124− , mark = "journal-action"
125− , noun = C.cell (C.cord "add") (C.cell (time |> Time.posixToMillis |> BigInt.fromInt |> C.bigint) (C.cord txt))
126− }
127− |> RunCmd
128− )
129− |> Ur.Cmd.cmd
130− )
131−
132− RunCmd cmd ->
133− ( model, cmd )
134−
135− GotShipName name ->
136− ( { model | shipName = Just name }, Ur.Cmd.none )
137−
138−
139−view : Model -> Document Msg
140−view model =
141− { body =
142− [ layout []
143− ([ el [ alignTop ] (text model.error)
144− , row [ spacing 8 ]
145− [ Widget.textInput (Material.textInput Material.defaultPalette)
146− { chips = []
147− , text = model.newEntry
148− , placeholder = Nothing
149− , label = "New entry"
150− , onChange = UpdateNewEntry
151− }
152− , Widget.iconButton (Material.containedButton Material.defaultPalette)
153− { text = "submit"
154− , icon = Icon.elmHeroicons Heroicons.Outline.check
155− , onPress = AddEntry model.newEntry |> Just
156− }
157− ]
158− , model.entries
159− |> Maybe.withDefault []
160− |> List.map
161− (\( id, txt ) ->
162− row
163− [ Border.rounded 10
164− , Border.shadow { offset = ( 0, 5 ), size = 1, blur = 10, color = rgba 0 0 0 0.3 }
165− , centerX
166− , padding 10
167− , spacing 12
168− ]
169− [ paragraph [] [ text txt ]
170− , Widget.iconButton (Material.containedButton Material.defaultPalette)
171− { text = "delete"
172− , icon = Icon.elmHeroicons Heroicons.Outline.trash
173− , onPress = DeleteEntry id |> Just
174− }
175− ]
176− )
177− |> column [ spacing 10, centerX ]
178− ]
179− |> column [ spacing 18, centerX ]
180− )
181− ]
182− , title = "Airlock"
183− }
184−
185−
186−result : (a -> c) -> (b -> c) -> Result a b -> c
187−result f g res =
188− case res of
189− Ok b ->
190− g b
191−
192− Err a ->
193− f a
194−
195−
196−port createEventSource : String -> Cmd msg
197−
198−
199−port onEventSourceMessage : (JD.Value -> msg) -> Sub msg
example/urbit/lib/noun-diff.hoondeleted−227
@@ -1,227 +0,0 @@
1−=<
2−|%
3− ++ diff
4− |= [old=* new=*]
5− ^- patch
6− =/ del (extract-del (oracle old new) old)
7− =/ ins (extract-ins (oracle old new) new)
8− =/ allowed-holes (~(int in (find-del-holes del)) (find-ins-holes ins))
9− =. del (filter-del-holes allowed-holes del)
10− =/ ins (filter-ins-holes allowed-holes ins)
11− =/ closed-patch (closure (gcp [del ins]))
12− ?> =(& +.closed-patch)
13− -.closed-patch
14− ++ apply
15− |= [patch=_id noun=*]
16− ?- -.patch
17− %diff
18− =/ var-map (del del.patch noun)
19− (ins ins.patch var-map)
20− %cell
21− ?> ?=(^ noun)
22− [$(patch lhs.patch, noun -.noun) $(patch rhs.patch, noun +.noun)]
23− ==
24− ++ id
25− ^- patch
26− [%diff [%hole ~] [%hole ~]]
27− +$ patch
28− $%
29− [%cell lhs=patch rhs=patch]
30− [%diff ^diff]
31− ==
32−--
33−::
34−|%
35− +$ del-diff
36− $%
37− [%hole @]
38− [%cell lhs=del-diff rhs=del-diff]
39− [%ignore ~]
40− ==
41− +$ ins-diff
42− $%
43− [%hole @ original=*]
44− [%cell lhs=ins-diff rhs=ins-diff]
45− [%atom @]
46− ==
47− +$ final-ins-diff
48− $%
49− [%hole @]
50− [%cell lhs=final-ins-diff rhs=final-ins-diff]
51− [%atom @]
52− ==
53− +$ diff [del=del-diff ins=final-ins-diff]
54− +$ patch
55− $%
56− [%cell lhs=patch rhs=patch]
57− [%diff diff]
58− ==
59− ++ insify-noun
60− |= noun=*
61− ^- final-ins-diff
62− ?- noun
63− ^ [%cell $(noun -.noun) $(noun +.noun)]
64− @ [%atom noun]
65− ==
66− ++ empty-set (silt `(list @)`~)
67− ++ find-del-holes
68− |= diff=del-diff
69− ~+
70− ^- (set @)
71− ?- -.diff
72− %hole (silt ~[+.diff])
73− %cell
74− (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
75− %ignore empty-set
76− ==
77− ++ find-final-ins-holes
78− |= diff=final-ins-diff
79− ~+
80− ^- (set @)
81− ?- -.diff
82− %hole (silt ~[+.diff])
83− %cell
84− (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
85− %atom empty-set
86− ==
87− ++ find-ins-holes
88− |= diff=ins-diff
89− ~+
90− ^- (set @)
91− ?- -.diff
92− %hole (silt ~[+<.diff])
93− %cell
94− (~(uni in $(diff lhs.diff)) $(diff rhs.diff))
95− %atom empty-set
96− ==
97− ++ filter-del-holes
98− |= [allowed-holes=(set @) diff=del-diff]
99− ^- del-diff
100− ?: ?=(%ignore -.diff) diff
101− ?- -.diff
102− %hole
103− ?: (~(has in allowed-holes) +.diff) diff
104− [%ignore ~]
105− %cell [%cell $(diff +<.diff) $(diff +>.diff)]
106− ==
107− ++ filter-ins-holes
108− |= [allowed-holes=(set @) diff=ins-diff]
109− ^- final-ins-diff
110− ?- -.diff
111− %hole
112− ?: (~(has in allowed-holes) +<.diff) [%hole +<.diff]
113− (insify-noun original:diff)
114− %cell [%cell $(diff +<.diff) $(diff +>.diff)]
115− %atom diff
116− ==
117− ++ gcp
118− |= diff=diff
119− ^- patch
120− ?- -.ins.diff
121− %atom
122− [%diff diff]
123− %cell
124− ?: ?=(%cell -.del.diff)
125− [%cell $(diff [+<.del.diff +<.ins.diff]) $(diff [+>.del.diff +>.ins.diff])]
126− [%diff diff]
127− %hole
128− [%diff diff]
129− ==
130− ++ closure
131− |= =patch
132− ^- [^patch ?]
133− ?- -.patch
134− %diff
135− =/ del-holes (find-del-holes del:patch)
136− =/ ins-holes (find-final-ins-holes ins:patch)
137− =/ difference (~(dif in ins-holes) del-holes)
138− [patch =(difference empty-set)]
139− %cell
140− =/ lhs $(patch lhs:patch)
141− =/ rhs $(patch rhs:patch)
142− ?: ?&(+.lhs +.rhs) [[%cell -.lhs -.rhs] &]
143− $(patch (pull-diff [%cell -.lhs -.rhs]))
144− ==
145− ++ pull-diff
146− |= =patch
147− ^- [%diff del=del-diff ins=final-ins-diff]
148− ?- -.patch
149− %diff patch
150− %cell
151− =/ pulled-lhs $(patch lhs:patch)
152− =/ pulled-rhs $(patch rhs:patch)
153− :+
154− %diff
155− [%cell del:pulled-lhs del:pulled-rhs]
156− [%cell ins:pulled-lhs ins:pulled-rhs]
157− ==
158− ++ is-subtree
159− |= [tree=* subtree=*]
160− ~+
161− ^- ?
162− ?: =(tree subtree) &
163− ?@ tree |
164− ?|
165− (is-subtree -.tree subtree)
166− (is-subtree +.tree subtree)
167− ==
168− ++ oracle
169− |= [a=* b=*]
170− |= subtree=*
171− ^- (unit @)
172− ?:
173− ?&
174− (is-subtree a subtree)
175− (is-subtree b subtree)
176− ==
177− `(mug subtree)
178− ~
179− ++ extract-del
180− |= [oracle=$-(* (unit @)) subtree=*]
181− ~+
182− ^- del-diff
183− =/ hash (oracle subtree)
184− ?^ hash [%hole +.hash]
185− ?@ subtree [%ignore ~]
186− [%cell (extract-del oracle -.subtree) (extract-del oracle +.subtree)]
187− ++ extract-ins
188− |= [oracle=$-(* (unit @)) subtree=*]
189− ~+
190− ^- ins-diff
191− =/ hash (oracle subtree)
192− ?^ hash [%hole +.hash subtree]
193− ?@ subtree [%atom subtree]
194− [%cell (extract-ins oracle -.subtree) (extract-ins oracle +.subtree)]
195− ++ ins
196− |= [diff=final-ins-diff var-map=(map @ *)]
197− ^- *
198− ?- -.diff
199− %atom
200− +.diff
201− %cell
202− [$(diff +<.diff) $(diff +>.diff)]
203− %hole
204− (~(got by var-map) +.diff)
205− ==
206− ++ del
207− |= [diff=del-diff noun=*]
208− ^- (map @ *)
209− |^ (go diff noun ((map @ *) ~))
210− ++ go
211− |= [diff=del-diff noun=* var-map=(map @ *)]
212− ^- (map @ *)
213− ?- -.diff
214− %ignore var-map
215− %hole
216− =/ subtree (~(get by var-map) +.diff)
217− ?~ subtree (~(put by var-map) +.diff noun)
218− ?> =(+.subtree noun)
219− var-map
220− %cell
221− ?> ?=(^ noun)
222− =/ lhs-var-map $(diff +<.diff, noun -.noun)
223− =/ rhs-var-map $(diff +>.diff, noun +.noun, var-map lhs-var-map)
224− rhs-var-map
225− ==
226− --
227−--
example/urbit/lib/sink.hoondeleted−26
@@ -1,26 +0,0 @@
1−/+ noun-diff
2−|%
3−++ clog
4− |$ [stut]
5− $%
6− [%flush stut]
7− [%drain patch:noun-diff]
8− ==
9−++ sink
10− |* pats=(list path)
11− |* stat=*
12− |@
13− ++ sync
14− |= [stat=_stat]
15− ^- [card:agent:gall _..sync]
16− =/ dif
17− %+ diff:noun-diff ^stat stat
18− :-
19− [%give %fact pats %noun !>(^-((clog) [%drain dif]))]
20− ..sync(stat stat)
21− ++ paths pats
22− ++ flush
23− ^- card:agent:gall
24− [%give %fact pats %noun !>(^-((clog) [%flush stat]))]
25− --
26−--
src/BitWriter.elm+5−5
@@ -14,17 +14,17 @@ import Bytes.Encode as BE
14
14
15empty : BitWriter
15empty : BitWriter
16empty =
16empty =
17− BitWriter { running = Nothing, collected = [], offset = 0 }
17+ BitWriter { running = Nothing, collected = BE.sequence [], offset = 0 }
18
18
19
19
20run : BitWriter -> Bytes
20run : BitWriter -> Bytes
21run (BitWriter { running, collected }) =
21run (BitWriter { running, collected }) =
22 case running of
22 case running of
23 Nothing ->
23 Nothing ->
24− BE.encode (collected |> List.reverse |> BE.sequence)
24+ BE.encode collected
25
25
26 Just { value } ->
26 Just { value } ->
27− BE.sequence (BE.unsignedInt8 value :: collected |> List.reverse) |> BE.encode
27+ BE.sequence [ collected, BE.unsignedInt8 value ] |> BE.encode
28
28
29
29
30type BitWriter
30type BitWriter
@@ -35,7 +35,7 @@ type BitWriter
35 , length : Int
35 , length : Int
36 }
36 }
37 , offset : Int
37 , offset : Int
38− , collected : List BE.Encoder
38+ , collected : BE.Encoder
39 }
39 }
40
40
41
41
@@ -68,7 +68,7 @@ bit b (BitWriter { running, collected, offset }) =
68 if length == 7 then
68 if length == 7 then
69 BitWriter
69 BitWriter
70 { running = Nothing
70 { running = Nothing
71− , collected = BE.unsignedInt8 newValue :: collected
71+ , collected = BE.sequence [ collected, BE.unsignedInt8 newValue ]
72 , offset = newOffset
72 , offset = newOffset
73 }
73 }
74
74
src/Ur.elm+4−20
@@ -56,42 +56,26 @@ getShipName root =
56 }
56 }
57
57
58
58
59−{-| Scry an agent at some path
60−
61− scry
62− { url = "http://localhost:8080"
63− , agent = "journal"
64− , path = [ "entries", "all" ]
65− , error = Noop
66− , success =
67− D.cell D.ignore
68− (D.cell (D.const D.cord "jrnl")
69− (D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b ))))
70− |> D.map GotListings
71− )
72− }
73−
74−-}
59+{-| -}
75scry :
60scry :
76 { url : String
61 { url : String
77 , agent : Agent
62 , agent : Agent
78 , path : Path
63 , path : Path
79 , error : msg
64 , error : msg
80− , success : D.Deconstructor msg
65+ , success : D.Deconstructor (msg -> msg) msg
81 }
66 }
82 -> Cmd msg
67 -> Cmd msg
83scry args =
68scry args =
84 scryTask args |> Task.perform identity
69 scryTask args |> Task.perform identity
85
70
86
71
87−{-| Same as `scry` but returns a `Task` instead of a `Cmd`.
88−-}
72+{-| -}
89scryTask :
73scryTask :
90 { url : String
74 { url : String
91 , agent : Agent
75 , agent : Agent
92 , path : Path
76 , path : Path
93 , error : msg
77 , error : msg
94− , success : D.Deconstructor msg
78+ , success : D.Deconstructor (msg -> msg) msg
95 }
79 }
96 -> Task a msg
80 -> Task a msg
97scryTask { url, agent, path, error, success } =
81scryTask { url, agent, path, error, success } =
src/Ur/Cmd.elm+5−18
@@ -10,43 +10,30 @@ import Ur.Cmd.Internal
10import Ur.Types exposing (Agent, Mark, Noun, Ship)
10import Ur.Types exposing (Agent, Mark, Noun, Ship)
11
11
12
12
13−{-| Like `Cmd` from `Platform.Cmd`, but for Urbit commands.
14−-}
13+{-| -}
15type alias Cmd msg =
14type alias Cmd msg =
16 List (Ur.Cmd.Internal.Cmd msg)
15 List (Ur.Cmd.Internal.Cmd msg)
17
16
18
17
19−{-| A command that does exactly nothing.
20−-}
18+{-| -}
21none : Cmd msg
19none : Cmd msg
22none =
20none =
23 []
21 []
24
22
25
23
26−{-| Sends a %poke to a Gall Agent.
27−
28− poke
29− { ship = "~zod"
30− , agent = "journal"
31− , mark = "journal-action"
32− , noun = C.cell (C.cord "del") (C.bigint id)
33− }
34−
35−-}
24+{-| -}
36poke : { ship : Ship, agent : Agent, mark : Mark, noun : Noun } -> Cmd msg
25poke : { ship : Ship, agent : Agent, mark : Mark, noun : Noun } -> Cmd msg
37poke p =
26poke p =
38 [ Ur.Cmd.Internal.Poke p ]
27 [ Ur.Cmd.Internal.Poke p ]
39
28
40
29
41−{-| Turns a `Platform.Cmd` command into a `Ur.Cmd` command.
42−-}
30+{-| -}
43cmd : Cmd.Cmd msg -> Cmd msg
31cmd : Cmd.Cmd msg -> Cmd msg
44cmd c =
32cmd c =
45 [ Ur.Cmd.Internal.Cmd c ]
33 [ Ur.Cmd.Internal.Cmd c ]
46
34
47
35
48−{-| Batches multiple commands into one. Similar to `Platform.Cmd.batch`.
49−-}
36+{-| -}
50batch : List (Cmd msg) -> Cmd msg
37batch : List (Cmd msg) -> Cmd msg
51batch =
38batch =
52 List.concat
39 List.concat
src/Ur/Constructor.elm+2−2
@@ -89,7 +89,7 @@ signedInt i =
89 )
89 )
90
90
91
91
92−{-| Constructs an `Atom` from `Bytes`.
92+{-| Constructs an `Atom` from `Bytes`
93-}
93-}
94bytes : Bytes -> Noun
94bytes : Bytes -> Noun
95bytes =
95bytes =
@@ -103,7 +103,7 @@ cord s =
103 Atom (BE.encode (BE.string s))
103 Atom (BE.encode (BE.string s))
104
104
105
105
106−{-| Constructs a [`tape`](https://developers.urbit.org/reference/glossary/tape) from a `String`.
106+{-| Constructs a [`tape`](https://developers.urbit.org/reference/glossary/tape) from a `String`
107-}
107-}
108tape : String -> Noun
108tape : String -> Noun
109tape s =
109tape s =
src/Ur/Da.elm+1−1
@@ -26,7 +26,7 @@ second =
26 BigInt.fromIntString "18446744073709551616" |> Maybe.withDefault (BigInt.fromInt 0)
26 BigInt.fromIntString "18446744073709551616" |> Maybe.withDefault (BigInt.fromInt 0)
27
27
28
28
29−{-| Converts `Posix` to a `@da`.
29+{-| Converts `Posix` to a `@da`
30-}
30-}
31posixToDa : Posix -> Da
31posixToDa : Posix -> Da
32posixToDa p =
32posixToDa p =
src/Ur/Deconstructor.elm+218−211
@@ -6,16 +6,24 @@ module Ur.Deconstructor exposing
6 , int, signedInt, bigint
6 , int, signedInt, bigint
7 , float32, float64
7 , float32, float64
8 , cord, tape
8 , cord, tape
9− , bytes, sig, ignore, tar, lazy, debug
9+ , bytes, sig, ignore, tar
10 )
10 )
11
11
12{-| This module provides an API to deconstruct `Noun`s into arbitrary Elm data structures.
12{-| This module provides an API to deconstruct `Noun`s into arbitrary Elm data structures.
13
13
14−You would parse a `[%edit @ cord]` like this:
14+The principal (and types) are very similar to `Url.Parser` from `elm/url`.
15
15
16− type alias Edit = {id: Int, text : String}
16+You would parse a `[%edit cord @]` like this:
17
17
18+ type alias Edit = {text : String, id: Int}
19+
18− (D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord)) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
20+ (D.cell (D.const D.cord "edit") (D.cell D.int D.cord)) |> D.map Edit
21+
22+When you `map` a `Deconstructor` the function you pass to `map` will accept exactly the number of arguments
23+that "captured" values in exacly the order they occured in the `Deconstructor`.
24+
25+In our case we `map` the `Edit` type, which accepts exaclty two arguments because there are two `Deconstructor`s
26+that "capture" a value: `D.int` and `D.cord`.
19
27
20@docs Deconstructor
28@docs Deconstructor
21
29
@@ -55,7 +63,7 @@ You would parse a `[%edit @ cord]` like this:
55
63
56# Miscellaneous
64# Miscellaneous
57
65
58−@docs bytes, sig, ignore, tar, lazy, debug
66+@docs bytes, sig, ignore, tar
59
67
60-}
68-}
61
69
@@ -66,74 +74,139 @@ import Bytes exposing (Bytes, Endianness(..))
66import Bytes.Decode as BD
74import Bytes.Decode as BD
67import Bytes.Encode as BE
75import Bytes.Encode as BE
68import Bytes.Extra
76import Bytes.Extra
69−import Ur.Jam exposing (cue, isSig)
77+import Ur.Jam exposing (cue)
70import Ur.Types exposing (..)
78import Ur.Types exposing (..)
71
79
72
80
73{-| A `Noun` deconstructor.
81{-| A `Noun` deconstructor.
74-}
82-}
75−type alias Deconstructor a =
83+type Deconstructor a b
76− Noun -> Maybe a
84+ = Deconstructor (Noun -> a -> Maybe b)
77
85
78
86
79{-| Executes a `Deconstructor` on a `Noun`.
87{-| Executes a `Deconstructor` on a `Noun`.
80-}
88-}
81−run : Deconstructor a -> Noun -> Maybe a
89+run : Deconstructor (a -> a) a -> Noun -> Maybe a
82−run f noun =
90+run (Deconstructor f) noun =
83− f noun
91+ f noun identity
84
92
85
93
86{-| Executes a deconstructor on a `jam`med `Noun`.
94{-| Executes a deconstructor on a `jam`med `Noun`.
87-}
95-}
88−runBytes : Deconstructor a -> Bytes -> Maybe a
96+runBytes : Deconstructor (a -> a) a -> Bytes -> Maybe a
89−runBytes f bs =
97+runBytes (Deconstructor f) bs =
98+ cue bs
90− cue bs |> Maybe.andThen f
99+ |> Maybe.andThen
100+ (\noun -> f noun identity)
91
101
92
102
93−{-| Asserts that the value should be exactly equal to the second argument.
103+{-| Asserts that the value at the current position should be exactly equal to the second argument.
94
104
95−The first argument is a `Deconstructor` for the given type.
105+The first argument is a `Deconstructor` for the gven tyoe.
96
106
97The second argument is the value to compare with.
107The second argument is the value to compare with.
98
108
99This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
109This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
100
110
101-}
111-}
102−const : Deconstructor a -> a -> Deconstructor ()
112+const : Deconstructor (a -> a) a -> a -> Deconstructor c c
103−const f value noun =
113+const (Deconstructor f) value =
114+ Deconstructor
115+ (\noun c ->
104− case f noun of
116+ case f noun identity of
105− Just a ->
117+ Just a ->
106− if a == value then
118+ if a == value then
107− Just ()
119+ Just c
108
120
109− else
121+ else
110− Nothing
122+ Nothing
111
123
112− Nothing ->
124+ Nothing ->
113− Nothing
125+ Nothing
126+ )
114
127
115
128
116−{-| Extracts a `BigInt`.
129+{-| Extracts a `cord` at the current location.
117-}
130-}
118−bigint : Deconstructor BigInt
131+cord : Deconstructor (String -> a) a
119−bigint x =
132+cord =
133+ Deconstructor
134+ (\x f ->
120− case x of
135+ case x of
121− Atom bs ->
136+ Atom bs ->
122− BigInt.Bytes.decode bs |> Just
137+ BD.decode (BD.string (Bytes.width bs)) bs
138+ |> Maybe.map f
139+
140+ Cell _ ->
141+ Nothing
142+ )
123
143
124− Cell _ ->
144+
125− Nothing
145+{-| Extracts a `tape` at the current location.
146+-}
147+tape : Deconstructor (String -> a) a
148+tape =
149+ list cord |> fmap String.concat
126
150
127
151
128−{-| Extracts a 32-bit signed `Int`.
152+{-| Extracts a 32-bit unsigned `Int` at the given location.
129
153
130−If the `Atom` is larger than 32 bits the the behaviour is undefined.
154+If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
131
155
132-}
156-}
157+int : Deconstructor (Int -> a) a
158+int =
159+ Deconstructor
160+ (\x f ->
161+ case x of
162+ Atom bs ->
163+ (case Bytes.width bs of
164+ 1 ->
165+ BD.decode BD.unsignedInt8 bs
166+
167+ 2 ->
168+ BD.decode (BD.unsignedInt16 LE) bs
169+
170+ 3 ->
171+ BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
172+
173+ 4 ->
174+ BD.decode (BD.unsignedInt32 LE) bs
175+
176+ _ ->
177+ Nothing
178+ )
179+ |> Maybe.map f
180+
181+ Cell _ ->
182+ Nothing
183+ )
184+
185+
186+{-| Extracts a `BigInt` at the current location.
187+-}
188+bigint : Deconstructor (BigInt -> a) a
189+bigint =
190+ Deconstructor
191+ (\x f ->
192+ case x of
193+ Atom bs ->
194+ BigInt.Bytes.decode bs |> f |> Just
195+
196+ Cell _ ->
197+ Nothing
198+ )
199+
200+
201+{-| Extracts a 32-bit signed `Int` at the given location.
202+
203+If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
204+
205+-}
133−signedInt : Deconstructor Int
206+signedInt : Deconstructor (Int -> a) a
134signedInt =
207signedInt =
135 int
208 int
136− |> map
209+ |> fmap
137 (\i ->
210 (\i ->
138 if Bitwise.and 1 i == 1 then
211 if Bitwise.and 1 i == 1 then
139 -(Bitwise.shiftRightBy 1 (1 + i))
212 -(Bitwise.shiftRightBy 1 (1 + i))
@@ -143,85 +216,105 @@ signedInt =
143 )
216 )
144
217
145
218
146−{-| Extracts a 32-bit `Float`.
219+{-| Extracts a 32-bit `Float` at the given location.
147-}
220-}
148−float32 : Deconstructor Float
221+float32 : Deconstructor (Float -> a) a
149−float32 x =
222+float32 =
223+ Deconstructor
224+ (\x f ->
150− case x of
225+ case x of
151− Atom bs ->
226+ Atom bs ->
152− BD.decode (BD.float32 LE) bs
227+ BD.decode (BD.float32 LE) bs
153−
228+ |> Maybe.map f
229+
154− Cell _ ->
230+ Cell _ ->
155− Nothing
231+ Nothing
232+ )
156
233
157
234
158−{-| Extracts a 64-bit `Float`.
235+{-| Extracts a 64-bit `Float` at the given location.
159-}
236-}
160−float64 : Deconstructor Float
237+float64 : Deconstructor (Float -> a) a
161−float64 x =
238+float64 =
239+ Deconstructor
240+ (\x f ->
162− case x of
241+ case x of
163− Atom bs ->
242+ Atom bs ->
164− BD.decode (BD.float64 LE) bs
243+ BD.decode (BD.float64 LE) bs
165−
244+ |> Maybe.map f
245+
166− Cell _ ->
246+ Cell _ ->
167− Nothing
247+ Nothing
248+ )
168
249
169
250
170−{-| Extracts the raw `Byte`s of an `Atom`.
251+{-| Extracts the raw `Byte`s of an `Atom` at the current location.
171-}
252-}
172−bytes : Deconstructor Bytes
253+bytes : Deconstructor (Bytes -> a) a
173−bytes n =
254+bytes =
255+ Deconstructor
256+ (\n f ->
174− case n of
257+ case n of
175− Atom bs ->
258+ Atom bs ->
176− Just bs
259+ f bs |> Just
177
260
178− Cell _ ->
261+ Cell _ ->
179− Nothing
262+ Nothing
263+ )
180
264
181
265
182−{-| Asserts the the `Atom` should be exactly `sig` (`~`).
266+{-| Asserts the the `Atom` at the current position should be exactly `sig` (`~`).
183-}
267-}
184−sig : Deconstructor ()
268+sig : Deconstructor a a
185−sig n =
269+sig =
270+ Deconstructor
271+ (\n a ->
186− case n of
272+ case n of
187− Atom b ->
273+ Atom b ->
188− if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
274+ if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
189− Just ()
275+ Just a
190
276
191− else
277+ else
192− Nothing
278+ Nothing
193
279
194− Cell _ ->
280+ Cell _ ->
195− Nothing
281+ Nothing
282+ )
196
283
197
284
198−{-| Extracts a sig-terminated list of arbitrary elements.
285+{-| Extracts a sig-terminated list of arbitrary elements at the current position.
199
286
200The first argument is a `Deconstructor` of the elements of the list.
287The first argument is a `Deconstructor` of the elements of the list.
201
288
202-}
289-}
203−list : Deconstructor a -> Deconstructor (List a)
290+list : Deconstructor (a -> a) a -> Deconstructor (List a -> b) b
204−list f n =
291+list (Deconstructor f) =
292+ Deconstructor
293+ (\n g ->
205− let
294+ let
206− go n_ =
295+ go n_ =
207− case n_ of
296+ case n_ of
208− Atom _ ->
297+ Atom _ ->
209− Just []
298+ Just []
210
299
211− Cell ( el, tail ) ->
300+ Cell ( el, tail ) ->
212− Maybe.map2 (\a b -> a :: b) (f el) (go tail)
301+ Maybe.map2 (\a b -> a :: b) (f el identity) (go tail)
213− in
302+ in
214− go n
303+ go n |> Maybe.map g
304+ )
215
305
216
306
217−alt : Deconstructor a -> Deconstructor a -> Deconstructor a
307+alt : Deconstructor a b -> Deconstructor a b -> Deconstructor a b
218−alt f g n =
308+alt (Deconstructor f) (Deconstructor g) =
219− case f n of
309+ Deconstructor
220− Just x ->
310+ (\n a ->
311+ case f n a of
312+ Just x ->
221− Just x
313+ Just x
222
314
223− Nothing ->
315+ Nothing ->
224− g n
316+ g n a
317+ )
225
318
226
319
227{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
320{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
@@ -229,150 +322,64 @@ alt f g n =
229This is especially useful for deconstructing head-tagged unions from Hoon.
322This is especially useful for deconstructing head-tagged unions from Hoon.
230
323
231-}
324-}
232−oneOf : List (Deconstructor a) -> Deconstructor a
325+oneOf : List (Deconstructor a b) -> Deconstructor a b
233oneOf l =
326oneOf l =
234 case l of
327 case l of
235 [] ->
328 [] ->
236− always Nothing
329+ Deconstructor (\_ _ -> Nothing)
237
330
238 x :: xs ->
331 x :: xs ->
239 alt x (oneOf xs)
332 alt x (oneOf xs)
240
333
241
334
242−{-| Ignore any value.
335+{-| Extract the raw `Noun` at the current position.
243−
244−This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
245−
246−-}
247−ignore : Deconstructor ()
248−ignore =
249− always (Just ())
250−
251−
252−{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
253−
254−Accepts two arbitrary `Deconstructor`s that form a Cell.
255−
256−-}
257−cell : Deconstructor a -> Deconstructor b -> Deconstructor ( a, b )
258−cell l r noun =
259− case noun of
260− Cell ( lhs, rhs ) ->
261− l lhs |> Maybe.andThen (\a -> r rhs |> Maybe.map (\b -> ( a, b )))
262−
263− Atom _ ->
264− Nothing
265−
266−
267−{-| Maps (applies) a function to all of the values deconstructed.
268−
269−This is useful when you want to create a data type with extracted values as fields.
270−
271−-}
272−lazy : (() -> Deconstructor a) -> Deconstructor a
273−lazy f noun =
274− f () noun
275
336
337+Always succeeds.
276
338
277−{-| Maps over the result of the deconstructor.
278-}
339-}
279−map : (a -> b) -> Deconstructor a -> Deconstructor b
340+tar : Deconstructor (Noun -> a) a
280−map g f noun =
341+tar =
281− f noun |> Maybe.map g
342+ Deconstructor (\noun f -> Just (f noun))
282−
283
343
284−{-| Extracts a `cord`.
285−-}
286−cord : Deconstructor String
287−cord x =
288− case x of
289− Atom bs ->
290− BD.decode (BD.string (Bytes.width bs)) bs
291
344
292− Cell _ ->
345+{-| Ignore any value at the current position.
293− Nothing
294
346
347+This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
295
348
296−{-| Extracts a `tape`.
297-}
349-}
298−tape : Deconstructor String
350+ignore : Deconstructor a a
299−tape =
351+ignore =
300− list cord |> map String.concat
352+ Deconstructor (\_ f -> Just f)
301
353
302
354
303−{-| Extracts a 32-bit unsigned `Int`.
355+{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
304
356
305−If the `Atom` is larger than 32 bits the the behaviour is undefined.
357+Accepts two arbitrary `Deconstructor`s that form a Cell.
306
358
307-}
359-}
308−int : Deconstructor Int
360+cell : Deconstructor a b -> Deconstructor b c -> Deconstructor a c
309−int x =
361+cell (Deconstructor l) (Deconstructor r) =
310− case x of
362+ Deconstructor
311− Atom bs ->
363+ (\noun a ->
312− case Bytes.width bs of
364+ case noun of
313− 1 ->
365+ Cell ( lhs, rhs ) ->
314− BD.decode BD.unsignedInt8 bs
366+ l lhs a |> Maybe.andThen (\b -> r rhs b)
315−
316− 2 ->
317− BD.decode (BD.unsignedInt16 LE) bs
318−
319− 3 ->
320− BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
321−
322− 4 ->
323− BD.decode (BD.unsignedInt32 LE) bs
324
367
325− _ ->
368+ Atom _ ->
326 Nothing
369 Nothing
370+ )
327
371
328− Cell _ ->
329− Nothing
330−
331−
332−{-| Extract the raw `Noun`.
333−
334−Always succeeds.
335
372
336−-}
373+{-| Maps (applies) a function to all of the values deconstructed.
337−tar : Deconstructor Noun
338−tar =
339− Just
340
374
375+This is useful when you want to create a data type with extracted values as fields.
341
376
342−{-| Debug a deconstructor.
343−If the deconstructor from the second argument fails then the failed nock will be printed to the console.
344-}
377-}
345−debug : (String -> String) -> Deconstructor a -> Deconstructor a
378+map : a -> Deconstructor a b -> Deconstructor (b -> c) c
346−debug log f noun =
379+map a (Deconstructor f) =
347− case f noun of
380+ Deconstructor (\noun g -> f noun a |> Maybe.map g)
348− Just x ->
349− Just x
350
381
351− Nothing ->
352− let
353− _ =
354− log (prettyNoun noun)
355− in
356− Nothing
357−
358−
359−prettyNoun : Noun -> String
360−prettyNoun noun =
361− let
362− go isRhs n =
363− case n of
364− Atom a ->
365− if isSig a then
366− "~"
367
382
368− else
383+fmap : (a -> b) -> Deconstructor (a -> c) c -> Deconstructor (b -> c) c
369− "@"
384+fmap f (Deconstructor g) =
370−
385+ Deconstructor (\noun h -> g noun (f >> h))
371− Cell ( lhs, rhs ) ->
372− if isRhs then
373− go False lhs ++ " " ++ go True rhs
374−
375− else
376− "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"
377− in
378− go False noun
src/Ur/NounDiff.elmdeleted−115
@@ -1,115 +0,0 @@
1−module Ur.NounDiff exposing (Patch, apply, deconstructPatch)
2−
3−import Bytes.Extra
4−import Dict exposing (Dict)
5−import Ur.Deconstructor as D
6−import Ur.Types exposing (..)
7−
8−
9−type Patch
10− = PatchCell Patch Patch
11− | Diff DelDiff InsDiff
12−
13−
14−deconstructPatch : () -> D.Deconstructor Patch
15−deconstructPatch () =
16− D.oneOf
17− [ D.cell (D.const D.cord "cell")
18− (D.cell
19− (D.lazy deconstructPatch)
20− (D.lazy deconstructPatch)
21− )
22− |> D.map (\( (), ( a, b ) ) -> PatchCell a b)
23− , D.cell (D.const D.cord "diff") (D.cell (D.lazy deconstructDel) (D.lazy deconstructIns))
24− |> D.map (\( (), ( a, b ) ) -> Diff a b)
25− ]
26−
27−
28−apply : Patch -> Noun -> Noun
29−apply patch noun =
30− case ( patch, noun ) of
31− ( PatchCell lhs rhs, Cell ( lhs_, rhs_ ) ) ->
32− Cell ( apply lhs lhs_, apply rhs rhs_ )
33−
34− ( Diff delDiff insDiff, _ ) ->
35− ins insDiff (del delDiff noun)
36−
37− _ ->
38− noun
39−
40−
41−del : DelDiff -> Noun -> Dict Int Noun
42−del diff noun =
43− let
44− go : DelDiff -> Noun -> Dict Int Noun -> Dict Int Noun
45− go diff_ noun_ dict =
46− case ( diff_, noun_ ) of
47− ( Ignore, _ ) ->
48− dict
49−
50− ( DelCell lhsDiff rhsDiff, Cell ( lhs, rhs ) ) ->
51− go lhsDiff lhs (go rhsDiff rhs dict)
52−
53− ( DelHole hole, _ ) ->
54− -- There should be a continuity check here.
55− -- Check that the noun_ is equal to whatever the hole maps to in dict.
56− -- I don't include it for speed and laziness reasons.
57− Dict.insert hole noun_ dict
58−
59− _ ->
60− dict
61− in
62− go diff noun Dict.empty
63−
64−
65−ins : InsDiff -> Dict Int Noun -> Noun
66−ins diff dict =
67− case diff of
68− InsAtom a ->
69− Atom a
70−
71− InsHole hole ->
72− Dict.get hole dict |> Maybe.withDefault (Atom Bytes.Extra.empty)
73−
74− InsCell lhs rhs ->
75− Cell ( ins lhs dict, ins rhs dict )
76−
77−
78−type DelDiff
79− = Ignore
80− | DelHole Int
81− | DelCell DelDiff DelDiff
82−
83−
84−deconstructDel : () -> D.Deconstructor DelDiff
85−deconstructDel () =
86− D.oneOf
87− [ D.cell (D.const D.cord "ignore") D.ignore |> D.map (\( (), () ) -> Ignore)
88− , D.cell (D.const D.cord "hole") D.int |> D.map (\( (), x ) -> DelHole x)
89− , D.cell (D.const D.cord "cell")
90− (D.cell
91− (D.lazy deconstructDel)
92− (D.lazy deconstructDel)
93− )
94− |> D.map (\( (), ( lhs, rhs ) ) -> DelCell lhs rhs)
95− ]
96−
97−
98−type InsDiff
99− = InsAtom Atom
100− | InsHole Int
101− | InsCell InsDiff InsDiff
102−
103−
104−deconstructIns : () -> D.Deconstructor InsDiff
105−deconstructIns () =
106− D.oneOf
107− [ D.cell (D.const D.cord "hole") D.int |> D.map (\( (), x ) -> InsHole x)
108− , D.cell (D.const D.cord "atom") D.bytes |> D.map (\( (), bs ) -> InsAtom bs)
109− , D.cell (D.const D.cord "cell")
110− (D.cell
111− (D.lazy deconstructIns)
112− (D.lazy deconstructIns)
113− )
114− |> D.map (\( (), ( a, b ) ) -> InsCell a b)
115− ]
src/Ur/Phonemic.elm+1−1
@@ -13,7 +13,7 @@ import Urbit.Encoding.Atom exposing (toBigInt)
13import Urbit.Encoding.Phonemic exposing (..)
13import Urbit.Encoding.Phonemic exposing (..)
14
14
15
15
16−{-| Converts a ship name like `~zod` into an `Atom`.
16+{-| Converts a ship name like `~zod` into an Atom.
17-}
17-}
18fromString : Ship -> Atom
18fromString : Ship -> Atom
19fromString s =
19fromString s =
src/Ur/Requests.elm+1−1
@@ -98,7 +98,7 @@ toNoun eventId req =
98 C.cell (C.cord app) (C.listOf C.cord path)
98 C.cell (C.cord app) (C.listOf C.cord path)
99
99
100 Unsubscribe subId ->
100 Unsubscribe subId ->
101− C.cell (C.cord "unsubscribe") <|
101+ C.cell (C.cord "usubscribe") <|
102 C.cell (C.int eventId) (C.int subId)
102 C.cell (C.int eventId) (C.int subId)
103
103
104 Poke { ship, agent, mark, noun } ->
104 Poke { ship, agent, mark, noun } ->
src/Ur/Run.elm+25−82
@@ -1,6 +1,6 @@
1module Ur.Run exposing (element, document, application, Program, Model, Msg)
1module Ur.Run exposing (element, document, application, Program, Model, Msg)
2
2
3−{-| This module contains functions to run your application with Urbit integration.
3+{-|
4
4
5@docs element, document, application, Program, Model, Msg
5@docs element, document, application, Program, Model, Msg
6
6
@@ -18,7 +18,6 @@ import Ur.Cmd
18import Ur.Cmd.Internal
18import Ur.Cmd.Internal
19import Ur.Constructor as C
19import Ur.Constructor as C
20import Ur.Deconstructor as D
20import Ur.Deconstructor as D
21−import Ur.NounDiff exposing (Patch, deconstructPatch)
22import Ur.Requests exposing (..)
21import Ur.Requests exposing (..)
23import Ur.Sub
22import Ur.Sub
24import Ur.Sub.Internal
23import Ur.Sub.Internal
@@ -31,9 +30,8 @@ type alias SubDict msg =
31 Dict
30 Dict
32 -- (ship, agent, path)
31 -- (ship, agent, path)
33 ( String, String, List String )
32 ( String, String, List String )
34− { deconstructor : D.Deconstructor msg
33+ { deconstructor : D.Deconstructor (msg -> msg) msg
35 , number : Int
34 , number : Int
36− , sink : Bool
37 }
35 }
38
36
39
37
@@ -51,7 +49,6 @@ type alias Model app msg =
51 , eventId : Int
49 , eventId : Int
52 , flags : Flags
50 , flags : Flags
53 , requestsToRetry : List UrbitRequest
51 , requestsToRetry : List UrbitRequest
54− , sinks : Dict Int Noun
55 }
52 }
56
53
57
54
@@ -67,7 +64,7 @@ type Msg msg
67 | RetryRequests
64 | RetryRequests
68
65
69
66
70−{-| A wrapper around your application `Program`.
67+{-| A wrapper around your application `Program`
71-}
68-}
72type alias Program model msg =
69type alias Program model msg =
73 Platform.Program Flags (Model model msg) (Msg msg)
70 Platform.Program Flags (Model model msg) (Msg msg)
@@ -174,11 +171,12 @@ update inp msg model =
174 ( appModel, appCmds ) =
171 ( appModel, appCmds ) =
175 inp.update msg_ model.app
172 inp.update msg_ model.app
176
173
174+ -- { subscriptions, eventId, subscriptionRequests, subscriptionIntMapping } =
177 subsResult =
175 subsResult =
178 processUrSubs
176 processUrSubs
179 model.eventId
177 model.eventId
180 model.subscriptions
178 model.subscriptions
181− (inp.urbitSubscriptions appModel |> (\(Ur.Sub.Internal.Sub x) -> x))
179+ (inp.urbitSubscriptions model.app |> (\(Ur.Sub.Internal.Sub x) -> x))
182
180
183 ( eventId_, cmds, urReqs ) =
181 ( eventId_, cmds, urReqs ) =
184 processCmd subsResult.eventId appCmds
182 processCmd subsResult.eventId appCmds
@@ -211,10 +209,12 @@ update inp msg model =
211 Ok string ->
209 Ok string ->
212 case
210 case
213 D.runBytes
211 D.runBytes
214− (D.cell D.int (D.cell D.cord D.tar))
212+ (D.cell D.int (D.cell D.cord D.tar)
213+ |> D.map (\a b c -> ( a, b, c ))
214+ )
215 (Ur.Uw.decode string)
215 (Ur.Uw.decode string)
216 of
216 of
217− Just ( messageId, ( messageType, rest ) ) ->
217+ Just ( messageId, messageType, rest ) ->
218 let
218 let
219 ( eventId, ackReqs ) =
219 ( eventId, ackReqs ) =
220 tag model.eventId [ Ack messageId ]
220 tag model.eventId [ Ack messageId ]
@@ -244,51 +244,14 @@ update inp msg model =
244 Dict.get messageId model.subscriptionIntMapping
244 Dict.get messageId model.subscriptionIntMapping
245 |> Maybe.andThen (\key -> Dict.get key model.subscriptions)
245 |> Maybe.andThen (\key -> Dict.get key model.subscriptions)
246 of
246 of
247− Just { deconstructor, sink } ->
247+ Just { deconstructor } ->
248− if sink then
249− case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink) |> D.map (\( (), ( (), s ) ) -> s)) rest of
248+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructor)) rest of
250− Just (Flush noun) ->
251− ( { model | sinks = Dict.insert messageId noun model.sinks }
252− , case D.run deconstructor noun of
253− Just subMsg ->
249+ Just subMsg ->
254− pureCmd (AppMsg subMsg)
255−
256− -- Got garbage
257− Nothing ->
258− Cmd.none
259− )
260−
261− Just (Drain patch) ->
262− case Dict.get messageId model.sinks of
263− Just oldNoun ->
264− let
265− newNoun =
266− Ur.NounDiff.apply patch oldNoun
267− in
268− ( { model | sinks = Dict.insert messageId newNoun model.sinks }
269− , case D.run deconstructor newNoun of
270− Just subMsg ->
271− pureCmd (AppMsg subMsg)
250+ ( model_, pureCmd (AppMsg subMsg) )
272−
251+
273− Nothing ->
274− Cmd.none
275− )
276−
277− Nothing ->
278− ( model, Cmd.none )
279−
280− -- Got garbage
281− Nothing ->
282− ( model, Cmd.none )
283−
284− else
285− case D.run (D.cell D.ignore (D.cell D.ignore deconstructor) |> D.map (\( (), ( (), m ) ) -> m)) rest of
286− Just subMsg ->
287− ( model_, pureCmd (AppMsg subMsg) )
288−
289− -- Got gargbage
252+ -- Got gargbage
290− Nothing ->
253+ Nothing ->
291− ( model_, ackCmd )
254+ ( model_, ackCmd )
292
255
293 -- Got a fact for a subscription we do not hold
256 -- Got a fact for a subscription we do not hold
294 Nothing ->
257 Nothing ->
@@ -313,11 +276,11 @@ update inp msg model =
313 NeedsActivation ->
276 NeedsActivation ->
314 let
277 let
315 ( eventId, reqs ) =
278 ( eventId, reqs ) =
316− [ Poke { ship = "", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
279+ [ Poke { ship = "~zod", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
317 |> tag model.eventId
280 |> tag model.eventId
318 in
281 in
319 ( { model | eventId = eventId }
282 ( { model | eventId = eventId }
320− , send { url = url, requests = reqs, success = OpenConnection, error = Noop }
283+ , send { url = url, requests = reqs, success = OpenConnection, error = NeedsActivation }
321 )
284 )
322
285
323 Noop ->
286 Noop ->
@@ -344,19 +307,6 @@ update inp msg model =
344 )
307 )
345
308
346
309
347−type Sink
348− = Flush Noun
349− | Drain Patch
350−
351−
352−deconstructSink : D.Deconstructor Sink
353−deconstructSink =
354− D.oneOf
355− [ D.cell (D.const D.cord "flush") D.tar |> D.map (\( (), n ) -> Flush n)
356− , D.cell (D.const D.cord "drain") (D.lazy deconstructPatch) |> D.map (\( (), patch ) -> Drain patch)
357− ]
358−
359−
360processCmd : EventId -> Ur.Cmd.Cmd msg -> ( EventId, Cmd (Msg msg), List ( EventId, UrbitRequest ) )
310processCmd : EventId -> Ur.Cmd.Cmd msg -> ( EventId, Cmd (Msg msg), List ( EventId, UrbitRequest ) )
361processCmd eventId urCmds =
311processCmd eventId urCmds =
362 let
312 let
@@ -386,10 +336,10 @@ pureCmd msg =
386
336
387processUrSubs :
337processUrSubs :
388 EventId
338 EventId
389− -> Dict ( Ship, Agent, Path ) { deconstructor : d, number : EventId, sink : Bool }
339+ -> Dict ( Ship, Agent, Path ) { deconstructor : d, number : EventId }
390− -> Dict ( Ship, Agent, Path ) { deconstructor : d, sink : Bool }
340+ -> Dict ( Ship, Agent, Path ) d
391 ->
341 ->
392− { subscriptions : Dict ( Ship, Agent, Path ) { deconstructor : d, number : EventId, sink : Bool }
342+ { subscriptions : Dict ( Ship, Agent, Path ) { deconstructor : d, number : EventId }
393 , eventId : EventId
343 , eventId : EventId
394 , subscriptionRequests : List ( EventId, UrbitRequest )
344 , subscriptionRequests : List ( EventId, UrbitRequest )
395 , subscriptionIntMapping : Dict EventId ( Ship, Agent, Path )
345 , subscriptionIntMapping : Dict EventId ( Ship, Agent, Path )
@@ -398,7 +348,7 @@ processUrSubs eventId existingSubscriptions urbitSubs_ =
398 let
348 let
399 urbitSubs =
349 urbitSubs =
400 urbitSubs_
350 urbitSubs_
401− |> Dict.map (\_ { deconstructor, sink } -> { deconstructor = deconstructor, sink = sink })
351+ |> Dict.map (\_ deconstructor -> { deconstructor = deconstructor })
402
352
403 ( eventId_, newSubscriptionActions ) =
353 ( eventId_, newSubscriptionActions ) =
404 Dict.diff urbitSubs existingSubscriptions
354 Dict.diff urbitSubs existingSubscriptions
@@ -421,11 +371,10 @@ processUrSubs eventId existingSubscriptions urbitSubs_ =
421 newSubscriptions =
371 newSubscriptions =
422 Dict.merge
372 Dict.merge
423 (\_ _ x -> x)
373 (\_ _ x -> x)
424− (\key number { deconstructor, sink } ->
374+ (\key number { deconstructor } ->
425 Dict.insert key
375 Dict.insert key
426 { deconstructor = deconstructor
376 { deconstructor = deconstructor
427 , number = number
377 , number = number
428− , sink = sink
429 }
378 }
430 )
379 )
431 (\_ _ x -> x)
380 (\_ _ x -> x)
@@ -468,7 +417,6 @@ init inp ( app, appCmds ) flags =
468 inp.urbitUrl app ++ "/~/channel/" ++ flags.uid
417 inp.urbitUrl app ++ "/~/channel/" ++ flags.uid
469 in
418 in
470 ( { subscriptions = subsResult.subscriptions
419 ( { subscriptions = subsResult.subscriptions
471− , sinks = Dict.empty
472 , subscriptionIntMapping = subsResult.subscriptionIntMapping
420 , subscriptionIntMapping = subsResult.subscriptionIntMapping
473 , app = app
421 , app = app
474 , connected = False
422 , connected = False
@@ -491,7 +439,7 @@ init inp ( app, appCmds ) flags =
491
439
492subscriptions :
440subscriptions :
493 { a | subscriptions : b -> Sub msg, onEventSourceMsg : (JD.Value -> Msg c) -> Sub (Msg msg) }
441 { a | subscriptions : b -> Sub msg, onEventSourceMsg : (JD.Value -> Msg c) -> Sub (Msg msg) }
494− -> { d | app : b, requestsToRetry : List e, connected : Bool }
442+ -> { d | app : b, requestsToRetry : List e }
495 -> Sub (Msg msg)
443 -> Sub (Msg msg)
496subscriptions inp model =
444subscriptions inp model =
497 Sub.batch
445 Sub.batch
@@ -502,9 +450,4 @@ subscriptions inp model =
502
450
503 else
451 else
504 Time.every 1000 (always RetryRequests)
452 Time.every 1000 (always RetryRequests)
505− , if not model.connected then
506− Time.every 10000 (always NeedsActivation)
507−
508− else
509− Sub.none
510 ]
453 ]
src/Ur/Sub.elm+9−36
@@ -1,8 +1,8 @@
1−module Ur.Sub exposing (Sub, subscribe, none, batch, sink)
1+module Ur.Sub exposing (Sub, subscribe, none, batch)
2
2
3−{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
3+{-| This module is conceptually similar to `Platform.Sub`, but also you to subscribe to Urbit channels.
4
4
5−@docs Sub, subscribe, none, batch, sink
5+@docs Sub, subscribe, none, batch
6
6
7-}
7-}
8
8
@@ -11,51 +11,24 @@ import Ur.Deconstructor as D
11import Ur.Sub.Internal
11import Ur.Sub.Internal
12
12
13
13
14−{-| Like `Sub` from `Platform.Sub` but for Urbit subscriptions.
15−-}
14+{-| -}
16type alias Sub msg =
15type alias Sub msg =
17 Ur.Sub.Internal.Sub msg
16 Ur.Sub.Internal.Sub msg
18
17
19
18
20−{-| Creates an Urbit subscription.
21−
22− subscribe
23− { ship = ship
24− , app = "journal"
25− , path = [ "updates" ]
26− , deconstructor = decodeJournalUpdate |> D.map GotUpdate
27− }
28−
29−-}
19+{-| -}
30−subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
20+subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
31subscribe { ship, app, path, deconstructor } =
21subscribe { ship, app, path, deconstructor } =
32− Dict.singleton ( ship, app, path )
22+ Dict.singleton ( ship, app, path ) deconstructor |> Ur.Sub.Internal.Sub
33− { deconstructor = deconstructor
34− , sink = False
35− }
36− |> Ur.Sub.Internal.Sub
37
23
38
24
39−{-| Creates a %sink subscription.
40−-}
41−sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
42−sink { ship, app, path, deconstructor } =
43− Dict.singleton ( ship, app, path )
44− { deconstructor = deconstructor
45− , sink = True
46− }
47− |> Ur.Sub.Internal.Sub
48−
49−
50−{-| A subscription that does exactly nothing. (Does not subscribe to anything)
51−-}
25+{-| -}
52none : Sub msg
26none : Sub msg
53none =
27none =
54 Ur.Sub.Internal.Sub Dict.empty
28 Ur.Sub.Internal.Sub Dict.empty
55
29
56
30
57−{-| Batch multiple subscriptions into one.
58−-}
31+{-| -}
59batch : List (Sub msg) -> Sub msg
32batch : List (Sub msg) -> Sub msg
60batch subs =
33batch subs =
61 subs |> List.map (\(Ur.Sub.Internal.Sub dict) -> dict) |> List.foldl Dict.union Dict.empty |> Ur.Sub.Internal.Sub
34 subs |> List.map (\(Ur.Sub.Internal.Sub dict) -> dict) |> List.foldl Dict.union Dict.empty |> Ur.Sub.Internal.Sub
src/Ur/Sub/Internal.elm+1−3
@@ -10,7 +10,5 @@ type Sub msg
10 (Dict
10 (Dict
11 -- key is (ship, app, path)
11 -- key is (ship, app, path)
12 ( Ship, Agent, Path )
12 ( Ship, Agent, Path )
13− { deconstructor : D.Deconstructor msg
13+ (D.Deconstructor (msg -> msg) msg)
14− , sink : Bool
15− }
16 )
14 )
src/Ur/Uw.elm+8−8
@@ -1,6 +1,6 @@
1module Ur.Uw exposing (decode, encode)
1module Ur.Uw exposing (decode, encode)
2
2
3−{-| This module works with Urbit base-64 encoded strings aka `@uw`.
3+{-| This module works with Urbit base-64 encoded strings.
4
4
5@docs decode, encode
5@docs decode, encode
6
6
@@ -29,24 +29,24 @@ decode string =
29 -- 0w
29 -- 0w
30 |> List.drop 2
30 |> List.drop 2
31
31
32− go : List Char -> List Int -> BW.BitWriter -> BW.BitWriter
32+ go : List Char -> BW.BitWriter -> BW.BitWriter
33− go cs append writer =
33+ go cs writer =
34 case cs of
34 case cs of
35 [] ->
35 [] ->
36− BW.bits append writer
36+ writer
37
37
38 '.' :: rest ->
38 '.' :: rest ->
39− go rest append writer
39+ go rest writer
40
40
41 c :: rest ->
41 c :: rest ->
42 case Dict.get c charToBits of
42 case Dict.get c charToBits of
43 Just bits ->
43 Just bits ->
44− go rest (bits ++ append) writer
44+ go rest writer |> BW.bits bits
45
45
46 Nothing ->
46 Nothing ->
47− go rest [] writer
47+ go rest writer
48 in
48 in
49− BW.run (go chars [] BW.empty)
49+ BW.run (go chars BW.empty)
50
50
51
51
52{-| -}
52{-| -}
tests/Test/Urbit.elm+11−11
@@ -77,7 +77,7 @@ tests =
77 Expect.equal
77 Expect.equal
78 (Just ( 1, 2 ))
78 (Just ( 1, 2 ))
79 (D.runBytes
79 (D.runBytes
80− (D.cell D.int D.int)
80+ (D.cell D.int D.int |> D.map Tuple.pair)
81 (Bytes.fromByteValues [ 0x31, 0x12 ])
81 (Bytes.fromByteValues [ 0x31, 0x12 ])
82 )
82 )
83 )
83 )
@@ -96,7 +96,7 @@ tests =
96 Expect.equal
96 Expect.equal
97 (Just ( 4, [ 1, 2, 3 ] ))
97 (Just ( 4, [ 1, 2, 3 ] ))
98 (D.runBytes
98 (D.runBytes
99− (D.cell D.int (D.list D.int))
99+ (D.cell D.int (D.list D.int) |> D.map Tuple.pair)
100 (Bytes.fromByteValues [ 0x61, 0xC6, 0x21, 0x43, 0x0B ])
100 (Bytes.fromByteValues [ 0x61, 0xC6, 0x21, 0x43, 0x0B ])
101 )
101 )
102 )
102 )
@@ -105,7 +105,7 @@ tests =
105 Expect.equal
105 Expect.equal
106 (Just ( 8, 11 ))
106 (Just ( 8, 11 ))
107 (D.runBytes
107 (D.runBytes
108− (D.cell D.float32 D.float32)
108+ (D.cell D.float32 D.float32 |> D.map Tuple.pair)
109 (Bytes.fromByteValues [ 0x01, 0x1F, 0x00, 0x00, 0x20, 0x08, 0x7C, 0x00, 0x00, 0x98, 0x20 ])
109 (Bytes.fromByteValues [ 0x01, 0x1F, 0x00, 0x00, 0x20, 0x08, 0x7C, 0x00, 0x00, 0x98, 0x20 ])
110 )
110 )
111 )
111 )
@@ -114,7 +114,7 @@ tests =
114 Expect.equal
114 Expect.equal
115 (Just ( 8, "hi" ))
115 (Just ( 8, "hi" ))
116 (D.runBytes
116 (D.runBytes
117− (D.cell D.int D.cord)
117+ (D.cell D.int D.cord |> D.map Tuple.pair)
118 (Bytes.fromByteValues [ 0x41, 0x10, 0x3C, 0x5A, 0x1A ])
118 (Bytes.fromByteValues [ 0x41, 0x10, 0x3C, 0x5A, 0x1A ])
119 )
119 )
120 )
120 )
@@ -123,7 +123,7 @@ tests =
123 Expect.equal
123 Expect.equal
124 (Just ( 8, "hi" ))
124 (Just ( 8, "hi" ))
125 (D.runBytes
125 (D.runBytes
126− (D.cell D.int D.tape)
126+ (D.cell D.int D.tape |> D.map Tuple.pair)
127 (Bytes.fromByteValues [ 0x41, 0x30, 0x38, 0x3A, 0x78, 0x5A ])
127 (Bytes.fromByteValues [ 0x41, 0x30, 0x38, 0x3A, 0x78, 0x5A ])
128 )
128 )
129 )
129 )
@@ -134,8 +134,8 @@ tests =
134 (Just "hi")
134 (Just "hi")
135 (D.runBytes
135 (D.runBytes
136 (D.oneOf
136 (D.oneOf
137− [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
137+ [ D.cell (D.const D.cord "tape") D.tape
138− , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
138+ , D.cell (D.const D.cord "cord") D.cord
139 ]
139 ]
140 )
140 )
141 (Bytes.fromByteValues [ 0x01, 0x9F, 0x2E, 0x0C, 0xAE, 0x1C, 0x1C, 0x1D, 0x3C, 0x2D ])
141 (Bytes.fromByteValues [ 0x01, 0x9F, 0x2E, 0x0C, 0xAE, 0x1C, 0x1C, 0x1D, 0x3C, 0x2D ])
@@ -147,8 +147,8 @@ tests =
147 (Just "hi")
147 (Just "hi")
148 (D.runBytes
148 (D.runBytes
149 (D.oneOf
149 (D.oneOf
150− [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
150+ [ D.cell (D.const D.cord "tape") D.tape
151− , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
151+ , D.cell (D.const D.cord "cord") D.cord
152 ]
152 ]
153 )
153 )
154 (Bytes.fromByteValues [ 0x01, 0x7F, 0xEC, 0x4D, 0x8E, 0x0C, 0x1E, 0x2D, 0x0D ])
154 (Bytes.fromByteValues [ 0x01, 0x7F, 0xEC, 0x4D, 0x8E, 0x0C, 0x1E, 0x2D, 0x0D ])
@@ -160,7 +160,7 @@ tests =
160 Expect.equal
160 Expect.equal
161 (Just ( 8, -8 ))
161 (Just ( 8, -8 ))
162 (D.runBytes
162 (D.runBytes
163− (D.cell D.signedInt D.signedInt)
163+ (D.cell D.signedInt D.signedInt |> D.map Tuple.pair)
164 (Bytes.fromByteValues [ 0xC1, 0x20, 0xE4, 0x01 ])
164 (Bytes.fromByteValues [ 0xC1, 0x20, 0xE4, 0x01 ])
165 )
165 )
166 )
166 )
@@ -190,7 +190,7 @@ tests =
190 (\( f, i, ui ) ->
190 (\( f, i, ui ) ->
191 Expect.equal (Just ( f, i, ui ))
191 Expect.equal (Just ( f, i, ui ))
192 (D.run
192 (D.run
193− (D.cell D.float64 (D.cell D.signedInt D.int) |> D.map (\(a, (b, c)) -> ( a, b, c )))
193+ (D.cell D.float64 (D.cell D.signedInt D.int) |> D.map (\a b c -> ( a, b, c )))
194 (C.cell (C.float64 f) (C.cell (C.signedInt i) (C.int ui)))
194 (C.cell (C.float64 f) (C.cell (C.signedInt i) (C.int ui)))
195 )
195 )
196 )
196 )