on master help sign in
20 files changed+444−389
since splitsnapshots
unifiedsplit
LICENSE+7 README.md+8−1 docs/sink.md+101 elm.json+11−2 example/src/Sink.elm+1−6 example/src/Vanilla.elm+10−9 example/urbit/app/journal.hoon+7−8 example/urbit/lib/sink.hoon+23−24 src/BitWriter.elm+5−5 src/Ur.elm+2−2 src/Ur/Constructor.elm+2−2 src/Ur/Deconstructor.elm+207−225 src/Ur/NounDiff.elm+21−61 src/Ur/Phonemic.elm+1−1 src/Ur/Requests.elm+1−1 src/Ur/Run.elm+10−12 src/Ur/Sub.elm+7−10 src/Ur/Sub/Internal.elm+1−1 src/Ur/Uw.elm+8−8 tests/Test/Urbit.elm+11−11
LICENSEnew file+7
@@ -0,0 +1,7 @@
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+8−1
@@ -6,10 +6,17 @@
66
77### Demo
88
9−[![Demo](https://img.youtube.com/vi/KV49E-D-SOY/default.jpg)](https://youtu.be/KV49E-D-SOY)
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).
1015
1116### FFI
1217
18+This is for setting up this library from scratch.
19+
1320Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
1421Unfortunately, Elm does not provide a native implementation.
1522This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
docs/sink.mdnew file+101
@@ -0,0 +1,101 @@
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+11−2
@@ -3,9 +3,18 @@
33 "name": "ilyakooo0/airlock",
44 "summary": "Urbit airlock",
55 "license": "MIT",
6− "version": "1.0.0",
6+ "version": "1.1.1",
77 "exposed-modules": [
8− "Ur", "Ur.Cmd", "Ur.Sub", "Ur.Constructor", "Ur.Deconstructor", "Ur.Phonemic", "Ur.Da", "Ur.Uw", "Ur.Run", "Ur.Types"
8+ "Ur",
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"
918 ],
1019 "elm-version": "0.19.0 <= v < 0.20.0",
1120 "dependencies": {
example/src/Sink.elm+1−6
@@ -14,7 +14,6 @@ import Ur.Constructor as C
1414import Ur.Deconstructor as D
1515import Ur.Run
1616import Ur.Sub
17−import Ur.Types exposing (Noun)
1817import Widget
1918import Widget.Icon as Icon
2019import Widget.Material as Material
@@ -56,7 +55,7 @@ main =
5655 , app = "journal"
5756 , path = [ "sync" ]
5857 , deconstructor =
59− D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b )))
58+ D.list (D.cell D.bigint D.cord)
6059 |> D.map GotListings
6160 }
6261 ]
@@ -80,7 +79,6 @@ type alias Model =
8079
8180type Msg
8281 = Noop
83− | GotSink Noun
8482 | Error String
8583 | GotListings (List ( BigInt, String ))
8684 | UpdateNewEntry String
@@ -96,9 +94,6 @@ update msg model =
9694 Noop ->
9795 ( model, Ur.Cmd.none )
9896
99− GotSink _ ->
100− ( model, Ur.Cmd.none )
101−
10297 Error err ->
10398 ( { model | error = err }, Ur.Cmd.none )
10499
example/src/Vanilla.elm+10−9
@@ -46,9 +46,9 @@ main =
4646 , success =
4747 D.cell D.ignore
4848 (D.cell (D.const D.cord "jrnl")
49− (D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b ))))
50− |> D.map GotListings
49+ (D.list (D.cell D.bigint D.cord))
5150 )
51+ |> D.map (\( (), ( (), listings ) ) -> GotListings listings)
5252 }
5353 ]
5454 |> Ur.Cmd.cmd
@@ -177,14 +177,15 @@ type JournalUpdate
177177 | Delete BigInt
178178
179179
180−decodeJournalUpdate : D.Deconstructor (JournalUpdate -> a) a
180+decodeJournalUpdate : D.Deconstructor JournalUpdate
181181decodeJournalUpdate =
182− D.cell D.ignore <|
183− D.oneOf
184− [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map Add
185− , (D.cell (D.const D.cord "edit") <| D.cell D.bigint D.cord) |> D.map Edit
186− , D.cell (D.const D.cord "del") D.bigint |> D.map Delete
187− ]
182+ D.oneOf
183+ [ (D.cell (D.const D.cord "add") <| D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Add id txt)
184+ , D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
185+ , D.cell (D.const D.cord "del") D.bigint |> D.map (\( (), id ) -> Delete id)
186+ ]
187+ |> D.cell D.ignore
188+ |> D.map (\( (), upd ) -> upd)
188189
189190
190191view : Model -> Document Msg
example/urbit/app/journal.hoon+7−8
@@ -21,12 +21,11 @@
2121
2222%- agent:dbug
2323=/ state *state-0
24−=/ snik
25− %+ sink ~[/sync]
26− |=(stat=versioned-state (tap:j-orm journal.stat))
27−=/ sink (snik state)
24+=* entries (tap:j-orm journal.state)
25+=/ snik (sink ~[/sync])
26+=/ sink (snik entries)
2827^- agent:gall
29−
28+::
3029|_ =bowl:gall
3130+* this .
3231 def ~(. (default-agent this %|) bowl)
@@ -40,7 +39,7 @@
4039 |= old-vase=vase
4140 ^- (quip card _this)
4241 =/ state !<(versioned-state old-vase)
43− `this(state state, sink (snik state))
42+ `this(state state, sink (snik entries))
4443::
4544++ on-poke
4645 |= [=mark =vase]
@@ -51,7 +50,7 @@
5150 =/ now=@ (unique-time now.bowl log.state)
5251 =/ act !<(action vase)
5352 =. state (poke-action act)
54− =^ card sink (sync:sink state)
53+ =^ card sink (sync:sink entries)
5554 :_ this(log.state (put:log-orm log.state now act))
5655 ~[(fact:io journal-update+!>(`update`[now act]) ~[/updates]) card]
5756 ::
@@ -93,7 +92,7 @@
9392 [%all ~]
9493 :^ ~ ~ %journal-update
9594 !> ^- update
96− [now %jrnl (tap:j-orm journal.state)]
95+ [now %jrnl entries]
9796 ::
9897 [%before @ @ ~]
9998 =/ before=@ (rash i.t.t.t.path dem)
example/urbit/lib/sink.hoon+23−24
@@ -1,27 +1,26 @@
11/+ noun-diff
22|%
3− ++ clog
4− |$ [stut]
5− $%
6− [%flush stut]
7− [%drain patch:noun-diff]
8− ==
9− ++ sink
10− |* [pats=(list path) extract=$-(* *)]
11− |* stat=*
12− |@
13− ++ sync
14− |= [stat=_stat]
15− ^- [card:agent:gall _..sync]
16− =/ dif
17− %+ diff:noun-diff (extract ^stat) (extract stat)
18− :-
19− ~& [%give %fact pats %noun !>(^-((clog) [%drain dif]))]
20− [%give %fact pats %noun !>(^-((clog) [%drain dif]))]
21− ..sync(stat stat)
22− ++ paths pats
23− ++ flush
24− ^- card:agent:gall
25− [%give %fact pats %noun !>(^-((clog) [%flush (extract stat)]))]
26− --
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+ --
2726--
src/BitWriter.elm+5−5
@@ -14,17 +14,17 @@ import Bytes.Encode as BE
1414
1515empty : BitWriter
1616empty =
17− BitWriter { running = Nothing, collected = BE.sequence [], offset = 0 }
17+ BitWriter { running = Nothing, collected = [], offset = 0 }
1818
1919
2020run : BitWriter -> Bytes
2121run (BitWriter { running, collected }) =
2222 case running of
2323 Nothing ->
24− BE.encode collected
24+ BE.encode (collected |> List.reverse |> BE.sequence)
2525
2626 Just { value } ->
27− BE.sequence [ collected, BE.unsignedInt8 value ] |> BE.encode
27+ BE.sequence (BE.unsignedInt8 value :: collected |> List.reverse) |> BE.encode
2828
2929
3030type BitWriter
@@ -35,7 +35,7 @@ type BitWriter
3535 , length : Int
3636 }
3737 , offset : Int
38− , collected : BE.Encoder
38+ , collected : List BE.Encoder
3939 }
4040
4141
@@ -68,7 +68,7 @@ bit b (BitWriter { running, collected, offset }) =
6868 if length == 7 then
6969 BitWriter
7070 { running = Nothing
71− , collected = BE.sequence [ collected, BE.unsignedInt8 newValue ]
71+ , collected = BE.unsignedInt8 newValue :: collected
7272 , offset = newOffset
7373 }
7474
src/Ur.elm+2−2
@@ -77,7 +77,7 @@ scry :
7777 , agent : Agent
7878 , path : Path
7979 , error : msg
80− , success : D.Deconstructor (msg -> msg) msg
80+ , success : D.Deconstructor msg
8181 }
8282 -> Cmd msg
8383scry args =
@@ -91,7 +91,7 @@ scryTask :
9191 , agent : Agent
9292 , path : Path
9393 , error : msg
94− , success : D.Deconstructor (msg -> msg) msg
94+ , success : D.Deconstructor msg
9595 }
9696 -> Task a msg
9797scryTask { url, agent, path, error, success } =
src/Ur/Constructor.elm+2−2
@@ -89,7 +89,7 @@ signedInt i =
8989 )
9090
9191
92−{-| Constructs an `Atom` from `Bytes`
92+{-| Constructs an `Atom` from `Bytes`.
9393-}
9494bytes : Bytes -> Noun
9595bytes =
@@ -103,7 +103,7 @@ cord s =
103103 Atom (BE.encode (BE.string s))
104104
105105
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`.
107107-}
108108tape : String -> Noun
109109tape s =
src/Ur/Deconstructor.elm+207−225
@@ -6,24 +6,16 @@ module Ur.Deconstructor exposing
66 , int, signedInt, bigint
77 , float32, float64
88 , cord, tape
9− , bytes, sig, ignore, tar, lazy
9+ , bytes, sig, ignore, tar, lazy, debug
1010 )
1111
1212{-| This module provides an API to deconstruct `Noun`s into arbitrary Elm data structures.
1313
14−The principal (and types) are very similar to `Url.Parser` from `elm/url`.
15−
1614You would parse a `[%edit @ cord]` like this:
1715
1816 type alias Edit = {id: Int, text : String}
1917
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`.
18+ (D.cell (D.const D.cord "edit") (D.cell D.bigint D.cord)) |> D.map (\( (), ( id, txt ) ) -> Edit id txt)
2719
2820@docs Deconstructor
2921
@@ -63,7 +55,7 @@ that "capture" a value: `D.int` and `D.cord`.
6355
6456# Miscellaneous
6557
66−@docs bytes, sig, ignore, tar, lazy
58+@docs bytes, sig, ignore, tar, lazy, debug
6759
6860-}
6961
@@ -74,139 +66,74 @@ import Bytes exposing (Bytes, Endianness(..))
7466import Bytes.Decode as BD
7567import Bytes.Encode as BE
7668import Bytes.Extra
77−import Ur.Jam exposing (cue)
69+import Ur.Jam exposing (cue, isSig)
7870import Ur.Types exposing (..)
7971
8072
8173{-| A `Noun` deconstructor.
8274-}
83−type Deconstructor a b
84− = Deconstructor (Noun -> a -> Maybe b)
75+type alias Deconstructor a =
76+ Noun -> Maybe a
8577
8678
8779{-| Executes a `Deconstructor` on a `Noun`.
8880-}
89−run : Deconstructor (a -> a) a -> Noun -> Maybe a
90−run (Deconstructor f) noun =
91− f noun identity
81+run : Deconstructor a -> Noun -> Maybe a
82+run f noun =
83+ f noun
9284
9385
9486{-| Executes a deconstructor on a `jam`med `Noun`.
9587-}
96−runBytes : Deconstructor (a -> a) a -> Bytes -> Maybe a
97−runBytes (Deconstructor f) bs =
98− cue bs
99− |> Maybe.andThen
100− (\noun -> f noun identity)
88+runBytes : Deconstructor a -> Bytes -> Maybe a
89+runBytes f bs =
90+ cue bs |> Maybe.andThen f
10191
10292
103−{-| Asserts that the value at the current position should be exactly equal to the second argument.
93+{-| Asserts that the value should be exactly equal to the second argument.
10494
105−The first argument is a `Deconstructor` for the gven tyoe.
95+The first argument is a `Deconstructor` for the given type.
10696
10797The second argument is the value to compare with.
10898
10999This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
110100
111101-}
112−const : Deconstructor (a -> a) a -> a -> Deconstructor c c
113−const (Deconstructor f) value =
114− Deconstructor
115− (\noun c ->
116− case f noun identity of
117− Just a ->
118− if a == value then
119− Just c
102+const : Deconstructor a -> a -> Deconstructor ()
103+const f value noun =
104+ case f noun of
105+ Just a ->
106+ if a == value then
107+ Just ()
120108
121− else
122− Nothing
109+ else
110+ Nothing
123111
124− Nothing ->
125− Nothing
126− )
112+ Nothing ->
113+ Nothing
127114
128115
129−{-| Extracts a `cord` at the current location.
116+{-| Extracts a `BigInt`.
130117-}
131−cord : Deconstructor (String -> a) a
132−cord =
133− Deconstructor
134− (\x f ->
135− case x of
136− Atom bs ->
137− BD.decode (BD.string (Bytes.width bs)) bs
138− |> Maybe.map f
139−
140− Cell _ ->
141− Nothing
142− )
118+bigint : Deconstructor BigInt
119+bigint x =
120+ case x of
121+ Atom bs ->
122+ BigInt.Bytes.decode bs |> Just
143123
144−
145−{-| Extracts a `tape` at the current location.
146−-}
147−tape : Deconstructor (String -> a) a
148−tape =
149− list cord |> fmap String.concat
124+ Cell _ ->
125+ Nothing
150126
151127
152−{-| Extracts a 32-bit unsigned `Int` at the given location.
128+{-| Extracts a 32-bit signed `Int`.
153129
154−If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
130+If the `Atom` is larger than 32 bits the the behaviour is undefined.
155131
156132-}
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−-}
206−signedInt : Deconstructor (Int -> a) a
133+signedInt : Deconstructor Int
207134signedInt =
208135 int
209− |> fmap
136+ |> map
210137 (\i ->
211138 if Bitwise.and 1 i == 1 then
212139 -(Bitwise.shiftRightBy 1 (1 + i))
@@ -216,105 +143,85 @@ signedInt =
216143 )
217144
218145
219−{-| Extracts a 32-bit `Float` at the given location.
146+{-| Extracts a 32-bit `Float`.
220147-}
221−float32 : Deconstructor (Float -> a) a
222−float32 =
223− Deconstructor
224− (\x f ->
225− case x of
226− Atom bs ->
227− BD.decode (BD.float32 LE) bs
228− |> Maybe.map f
229−
230− Cell _ ->
231− Nothing
232− )
148+float32 : Deconstructor Float
149+float32 x =
150+ case x of
151+ Atom bs ->
152+ BD.decode (BD.float32 LE) bs
153+
154+ Cell _ ->
155+ Nothing
233156
234157
235−{-| Extracts a 64-bit `Float` at the given location.
158+{-| Extracts a 64-bit `Float`.
236159-}
237−float64 : Deconstructor (Float -> a) a
238−float64 =
239− Deconstructor
240− (\x f ->
241− case x of
242− Atom bs ->
243− BD.decode (BD.float64 LE) bs
244− |> Maybe.map f
245−
246− Cell _ ->
247− Nothing
248− )
160+float64 : Deconstructor Float
161+float64 x =
162+ case x of
163+ Atom bs ->
164+ BD.decode (BD.float64 LE) bs
165+
166+ Cell _ ->
167+ Nothing
249168
250169
251−{-| Extracts the raw `Byte`s of an `Atom` at the current location.
170+{-| Extracts the raw `Byte`s of an `Atom`.
252171-}
253−bytes : Deconstructor (Bytes -> a) a
254−bytes =
255− Deconstructor
256− (\n f ->
257− case n of
258− Atom bs ->
259− f bs |> Just
172+bytes : Deconstructor Bytes
173+bytes n =
174+ case n of
175+ Atom bs ->
176+ Just bs
260177
261− Cell _ ->
262− Nothing
263− )
178+ Cell _ ->
179+ Nothing
264180
265181
266−{-| Asserts the the `Atom` at the current position should be exactly `sig` (`~`).
182+{-| Asserts the the `Atom` should be exactly `sig` (`~`).
267183-}
268−sig : Deconstructor a a
269−sig =
270− Deconstructor
271− (\n a ->
272− case n of
273− Atom b ->
274− if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
275− Just a
184+sig : Deconstructor ()
185+sig n =
186+ case n of
187+ Atom b ->
188+ if (Bytes.Extra.toByteValues b |> List.filter (\x -> x /= 0)) == [] then
189+ Just ()
276190
277− else
278− Nothing
191+ else
192+ Nothing
279193
280− Cell _ ->
281− Nothing
282− )
194+ Cell _ ->
195+ Nothing
283196
284197
285−{-| Extracts a sig-terminated list of arbitrary elements at the current position.
198+{-| Extracts a sig-terminated list of arbitrary elements.
286199
287200The first argument is a `Deconstructor` of the elements of the list.
288201
289202-}
290−list : Deconstructor (a -> a) a -> Deconstructor (List a -> b) b
291−list (Deconstructor f) =
292− Deconstructor
293− (\n g ->
294− let
295− go n_ =
296− case n_ of
297− Atom _ ->
298− Just []
203+list : Deconstructor a -> Deconstructor (List a)
204+list f n =
205+ let
206+ go n_ =
207+ case n_ of
208+ Atom _ ->
209+ Just []
299210
300− Cell ( el, tail ) ->
301− Maybe.map2 (\a b -> a :: b) (f el identity) (go tail)
302− in
303− go n |> Maybe.map g
304− )
211+ Cell ( el, tail ) ->
212+ Maybe.map2 (\a b -> a :: b) (f el) (go tail)
213+ in
214+ go n
305215
306216
307−alt : Deconstructor a b -> Deconstructor a b -> Deconstructor a b
308−alt (Deconstructor f) (Deconstructor g) =
309− Deconstructor
310− (\n a ->
311− case f n a of
312− Just x ->
313− Just x
217+alt : Deconstructor a -> Deconstructor a -> Deconstructor a
218+alt f g n =
219+ case f n of
220+ Just x ->
221+ Just x
314222
315− Nothing ->
316− g n a
317− )
223+ Nothing ->
224+ g n
318225
319226
320227{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
@@ -322,75 +229,150 @@ alt (Deconstructor f) (Deconstructor g) =
322229This is especially useful for deconstructing head-tagged unions from Hoon.
323230
324231-}
325−oneOf : List (Deconstructor a b) -> Deconstructor a b
232+oneOf : List (Deconstructor a) -> Deconstructor a
326233oneOf l =
327234 case l of
328235 [] ->
329− Deconstructor (\_ _ -> Nothing)
236+ always Nothing
330237
331238 x :: xs ->
332239 alt x (oneOf xs)
333240
334241
335−{-| Extract the raw `Noun` at the current position.
242+{-| Ignore any value.
336243
337−Always succeeds.
244+This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
338245
339246-}
340−tar : Deconstructor (Noun -> a) a
341−tar =
342− Deconstructor (\noun f -> Just (f noun))
247+ignore : Deconstructor ()
248+ignore =
249+ always (Just ())
343250
344251
345−{-| Ignore any value at the current position.
252+{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
346253
347−This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
254+Accepts two arbitrary `Deconstructor`s that form a Cell.
348255
349256-}
350−ignore : Deconstructor a a
351−ignore =
352− Deconstructor (\_ f -> Just f)
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 )))
353262
263+ Atom _ ->
264+ Nothing
354265
355−{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
356266
357−Accepts two arbitrary `Deconstructor`s that form a Cell.
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.
358270
359271-}
360−cell : Deconstructor a b -> Deconstructor b c -> Deconstructor a c
361−cell (Deconstructor l) (Deconstructor r) =
362− Deconstructor
363− (\noun a ->
364− case noun of
365− Cell ( lhs, rhs ) ->
366− l lhs a |> Maybe.andThen (\b -> r rhs b)
272+lazy : (() -> Deconstructor a) -> Deconstructor a
273+lazy f noun =
274+ f () noun
367275
368− Atom _ ->
276+
277+{-| Maps over the result of the deconstructor.
278+-}
279+map : (a -> b) -> Deconstructor a -> Deconstructor b
280+map g f noun =
281+ f noun |> Maybe.map g
282+
283+
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+
292+ Cell _ ->
293+ Nothing
294+
295+
296+{-| Extracts a `tape`.
297+-}
298+tape : Deconstructor String
299+tape =
300+ list cord |> map String.concat
301+
302+
303+{-| Extracts a 32-bit unsigned `Int`.
304+
305+If the `Atom` is larger than 32 bits the the behaviour is undefined.
306+
307+-}
308+int : Deconstructor Int
309+int x =
310+ case x of
311+ Atom bs ->
312+ case Bytes.width bs of
313+ 1 ->
314+ BD.decode BD.unsignedInt8 bs
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+
325+ _ ->
369326 Nothing
370− )
371327
328+ Cell _ ->
329+ Nothing
372330
373−{-| -}
374−lazy : (() -> Deconstructor a b) -> Deconstructor a b
375−lazy f =
376− Deconstructor
377− (\noun a ->
378− case f () of
379− Deconstructor g ->
380− g noun a
381− )
382331
332+{-| Extract the raw `Noun`.
383333
384−{-| Maps (applies) a function to all of the values deconstructed.
334+Always succeeds.
335+
336+-}
337+tar : Deconstructor Noun
338+tar =
339+ Just
385340
386−This is useful when you want to create a data type with extracted values as fields.
387341
342+{-| Debug a deconstructor.
343+If the deconstructor from the second argument fails then the failed nock will be printed to the console.
388344-}
389−map : a -> Deconstructor a b -> Deconstructor (b -> c) c
390−map a (Deconstructor f) =
391− Deconstructor (\noun g -> f noun a |> Maybe.map g)
345+debug : (String -> String) -> Deconstructor a -> Deconstructor a
346+debug log f noun =
347+ case f noun of
348+ Just x ->
349+ Just x
392350
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+ "~"
393367
394−fmap : (a -> b) -> Deconstructor (a -> c) c -> Deconstructor (b -> c) c
395−fmap f (Deconstructor g) =
396− Deconstructor (\noun h -> g noun (f >> h))
368+ else
369+ "@"
370+
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.elm+21−61
@@ -11,29 +11,17 @@ type Patch
1111 | Diff DelDiff InsDiff
1212
1313
14−deconstructPatch : D.Deconstructor (Patch -> c) c
15−deconstructPatch =
14+deconstructPatch : () -> D.Deconstructor Patch
15+deconstructPatch () =
1616 D.oneOf
1717 [ D.cell (D.const D.cord "cell")
1818 (D.cell
19− (D.lazy (\() -> deconstructPatch_))
20− (D.lazy (\() -> deconstructPatch_))
19+ (D.lazy deconstructPatch)
20+ (D.lazy deconstructPatch)
2121 )
22− |> D.map PatchCell
23− , D.cell (D.const D.cord "diff") (D.cell deconstructDel deconstructIns) |> D.map Diff
24− ]
25−
26−
27−deconstructPatch_ : D.Deconstructor (Patch -> c) c
28−deconstructPatch_ =
29− D.oneOf
30− [ D.cell (D.const D.cord "cell")
31− (D.cell
32− deconstructPatch
33− deconstructPatch
34− )
35− |> D.map PatchCell
36− , D.cell (D.const D.cord "diff") (D.cell deconstructDel deconstructIns) |> D.map Diff
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)
3725 ]
3826
3927
@@ -93,31 +81,17 @@ type DelDiff
9381 | DelCell DelDiff DelDiff
9482
9583
96−deconstructDel : D.Deconstructor (DelDiff -> c) c
97−deconstructDel =
84+deconstructDel : () -> D.Deconstructor DelDiff
85+deconstructDel () =
9886 D.oneOf
99− [ D.cell (D.const D.cord "ignore") D.ignore |> D.map Ignore
100− , D.cell (D.const D.cord "hole") D.int |> D.map DelHole
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)
10189 , D.cell (D.const D.cord "cell")
10290 (D.cell
103− deconstructDel_
104− deconstructDel_
91+ (D.lazy deconstructDel)
92+ (D.lazy deconstructDel)
10593 )
106− |> D.map DelCell
107− ]
108−
109−
110−deconstructDel_ : D.Deconstructor (DelDiff -> c) c
111−deconstructDel_ =
112− D.oneOf
113− [ D.const D.cord "ignore" |> D.map Ignore
114− , D.cell (D.const D.cord "hole") D.int |> D.map DelHole
115− , D.cell (D.const D.cord "cell")
116− (D.cell
117− (D.lazy (\() -> deconstructDel))
118− (D.lazy (\() -> deconstructDel))
119− )
120− |> D.map DelCell
94+ |> D.map (\( (), ( lhs, rhs ) ) -> DelCell lhs rhs)
12195 ]
12296
12397
@@ -127,29 +101,15 @@ type InsDiff
127101 | InsCell InsDiff InsDiff
128102
129103
130−deconstructIns : D.Deconstructor (InsDiff -> c) c
131−deconstructIns =
132− D.oneOf
133− [ D.cell (D.const D.cord "hole") D.int |> D.map InsHole
134− , D.cell (D.const D.cord "atom") D.bytes |> D.map InsAtom
135− , D.cell (D.const D.cord "cell")
136− (D.cell
137− deconstructIns_
138− deconstructIns_
139− )
140− |> D.map InsCell
141− ]
142−
143−
144−deconstructIns_ : D.Deconstructor (InsDiff -> c) c
145−deconstructIns_ =
104+deconstructIns : () -> D.Deconstructor InsDiff
105+deconstructIns () =
146106 D.oneOf
147− [ D.cell (D.const D.cord "hole") D.int |> D.map InsHole
148− , D.cell (D.const D.cord "atom") D.bytes |> D.map InsAtom
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)
149109 , D.cell (D.const D.cord "cell")
150110 (D.cell
151− (D.lazy (\() -> deconstructIns))
152− (D.lazy (\() -> deconstructIns))
111+ (D.lazy deconstructIns)
112+ (D.lazy deconstructIns)
153113 )
154− |> D.map InsCell
114+ |> D.map (\( (), ( a, b ) ) -> InsCell a b)
155115 ]
src/Ur/Phonemic.elm+1−1
@@ -13,7 +13,7 @@ import Urbit.Encoding.Atom exposing (toBigInt)
1313import Urbit.Encoding.Phonemic exposing (..)
1414
1515
16−{-| Converts a ship name like `~zod` into an Atom.
16+{-| Converts a ship name like `~zod` into an `Atom`.
1717-}
1818fromString : Ship -> Atom
1919fromString s =
src/Ur/Requests.elm+1−1
@@ -98,7 +98,7 @@ toNoun eventId req =
9898 C.cell (C.cord app) (C.listOf C.cord path)
9999
100100 Unsubscribe subId ->
101− C.cell (C.cord "usubscribe") <|
101+ C.cell (C.cord "unsubscribe") <|
102102 C.cell (C.int eventId) (C.int subId)
103103
104104 Poke { ship, agent, mark, noun } ->
src/Ur/Run.elm+10−12
@@ -31,7 +31,7 @@ type alias SubDict msg =
3131 Dict
3232 -- (ship, agent, path)
3333 ( String, String, List String )
34− { deconstructor : D.Deconstructor (msg -> msg) msg
34+ { deconstructor : D.Deconstructor msg
3535 , number : Int
3636 , sink : Bool
3737 }
@@ -178,7 +178,7 @@ update inp msg model =
178178 processUrSubs
179179 model.eventId
180180 model.subscriptions
181− (inp.urbitSubscriptions model.app |> (\(Ur.Sub.Internal.Sub x) -> x))
181+ (inp.urbitSubscriptions appModel |> (\(Ur.Sub.Internal.Sub x) -> x))
182182
183183 ( eventId_, cmds, urReqs ) =
184184 processCmd subsResult.eventId appCmds
@@ -211,12 +211,10 @@ update inp msg model =
211211 Ok string ->
212212 case
213213 D.runBytes
214− (D.cell D.int (D.cell D.cord D.tar)
215− |> D.map (\a b c -> ( a, b, c ))
216− )
214+ (D.cell D.int (D.cell D.cord D.tar))
217215 (Ur.Uw.decode string)
218216 of
219− Just ( messageId, messageType, rest ) ->
217+ Just ( messageId, ( messageType, rest ) ) ->
220218 let
221219 ( eventId, ackReqs ) =
222220 tag model.eventId [ Ack messageId ]
@@ -248,7 +246,7 @@ update inp msg model =
248246 of
249247 Just { deconstructor, sink } ->
250248 if sink then
251− case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink)) rest of
249+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink) |> D.map (\( (), ( (), s ) ) -> s)) rest of
252250 Just (Flush noun) ->
253251 ( { model | sinks = Dict.insert messageId noun model.sinks }
254252 , case D.run deconstructor noun of
@@ -284,7 +282,7 @@ update inp msg model =
284282 ( model, Cmd.none )
285283
286284 else
287− case D.run (D.cell D.ignore (D.cell D.ignore deconstructor)) rest of
285+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructor) |> D.map (\( (), ( (), m ) ) -> m)) rest of
288286 Just subMsg ->
289287 ( model_, pureCmd (AppMsg subMsg) )
290288
@@ -315,7 +313,7 @@ update inp msg model =
315313 NeedsActivation ->
316314 let
317315 ( eventId, reqs ) =
318− [ Poke { ship = "~zod", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
316+ [ Poke { ship = "", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
319317 |> tag model.eventId
320318 in
321319 ( { model | eventId = eventId }
@@ -351,11 +349,11 @@ type Sink
351349 | Drain Patch
352350
353351
354−deconstructSink : D.Deconstructor (Sink -> c) c
352+deconstructSink : D.Deconstructor Sink
355353deconstructSink =
356354 D.oneOf
357− [ D.cell (D.const D.cord "flush") D.tar |> D.map Flush
358− , D.cell (D.const D.cord "drain") deconstructPatch |> D.map Drain
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)
359357 ]
360358
361359
src/Ur/Sub.elm+7−10
@@ -1,11 +1,8 @@
1−module Ur.Sub exposing
2− ( Sub, subscribe, none, batch
3− , sink
4− )
1+module Ur.Sub exposing (Sub, subscribe, none, batch, sink)
52
6−{-| This module is conceptually similar to `Platform.Sub`, but also you to subscribe to Urbit channels.
3+{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
74
8−@docs Sub, subscribe, none, batch
5+@docs Sub, subscribe, none, batch, sink
96
107-}
118
@@ -14,7 +11,7 @@ import Ur.Deconstructor as D
1411import Ur.Sub.Internal
1512
1613
17−{-| Like `Sub` from `Platform.Sub`, but for Urbit subscriptions.
14+{-| Like `Sub` from `Platform.Sub` but for Urbit subscriptions.
1815-}
1916type alias Sub msg =
2017 Ur.Sub.Internal.Sub msg
@@ -30,7 +27,7 @@ type alias Sub msg =
3027 }
3128
3229-}
33−subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
30+subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
3431subscribe { ship, app, path, deconstructor } =
3532 Dict.singleton ( ship, app, path )
3633 { deconstructor = deconstructor
@@ -39,9 +36,9 @@ subscribe { ship, app, path, deconstructor } =
3936 |> Ur.Sub.Internal.Sub
4037
4138
42−{-| Create a Sink subscription.
39+{-| Creates a %sink subscription.
4340-}
44−sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
41+sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
4542sink { ship, app, path, deconstructor } =
4643 Dict.singleton ( ship, app, path )
4744 { deconstructor = deconstructor
src/Ur/Sub/Internal.elm+1−1
@@ -10,7 +10,7 @@ type Sub msg
1010 (Dict
1111 -- key is (ship, app, path)
1212 ( Ship, Agent, Path )
13− { deconstructor : D.Deconstructor (msg -> msg) msg
13+ { deconstructor : D.Deconstructor msg
1414 , sink : Bool
1515 }
1616 )
src/Ur/Uw.elm+8−8
@@ -1,6 +1,6 @@
11module Ur.Uw exposing (decode, encode)
22
3−{-| This module works with Urbit base-64 encoded strings.
3+{-| This module works with Urbit base-64 encoded strings aka `@uw`.
44
55@docs decode, encode
66
@@ -29,24 +29,24 @@ decode string =
2929 -- 0w
3030 |> List.drop 2
3131
32− go : List Char -> BW.BitWriter -> BW.BitWriter
33− go cs writer =
32+ go : List Char -> List Int -> BW.BitWriter -> BW.BitWriter
33+ go cs append writer =
3434 case cs of
3535 [] ->
36− writer
36+ BW.bits append writer
3737
3838 '.' :: rest ->
39− go rest writer
39+ go rest append writer
4040
4141 c :: rest ->
4242 case Dict.get c charToBits of
4343 Just bits ->
44− go rest writer |> BW.bits bits
44+ go rest (bits ++ append) writer
4545
4646 Nothing ->
47− go rest writer
47+ go rest [] writer
4848 in
49− BW.run (go chars BW.empty)
49+ BW.run (go chars [] BW.empty)
5050
5151
5252{-| -}
tests/Test/Urbit.elm+11−11
@@ -77,7 +77,7 @@ tests =
7777 Expect.equal
7878 (Just ( 1, 2 ))
7979 (D.runBytes
80− (D.cell D.int D.int |> D.map Tuple.pair)
80+ (D.cell D.int D.int)
8181 (Bytes.fromByteValues [ 0x31, 0x12 ])
8282 )
8383 )
@@ -96,7 +96,7 @@ tests =
9696 Expect.equal
9797 (Just ( 4, [ 1, 2, 3 ] ))
9898 (D.runBytes
99− (D.cell D.int (D.list D.int) |> D.map Tuple.pair)
99+ (D.cell D.int (D.list D.int))
100100 (Bytes.fromByteValues [ 0x61, 0xC6, 0x21, 0x43, 0x0B ])
101101 )
102102 )
@@ -105,7 +105,7 @@ tests =
105105 Expect.equal
106106 (Just ( 8, 11 ))
107107 (D.runBytes
108− (D.cell D.float32 D.float32 |> D.map Tuple.pair)
108+ (D.cell D.float32 D.float32)
109109 (Bytes.fromByteValues [ 0x01, 0x1F, 0x00, 0x00, 0x20, 0x08, 0x7C, 0x00, 0x00, 0x98, 0x20 ])
110110 )
111111 )
@@ -114,7 +114,7 @@ tests =
114114 Expect.equal
115115 (Just ( 8, "hi" ))
116116 (D.runBytes
117− (D.cell D.int D.cord |> D.map Tuple.pair)
117+ (D.cell D.int D.cord)
118118 (Bytes.fromByteValues [ 0x41, 0x10, 0x3C, 0x5A, 0x1A ])
119119 )
120120 )
@@ -123,7 +123,7 @@ tests =
123123 Expect.equal
124124 (Just ( 8, "hi" ))
125125 (D.runBytes
126− (D.cell D.int D.tape |> D.map Tuple.pair)
126+ (D.cell D.int D.tape)
127127 (Bytes.fromByteValues [ 0x41, 0x30, 0x38, 0x3A, 0x78, 0x5A ])
128128 )
129129 )
@@ -134,8 +134,8 @@ tests =
134134 (Just "hi")
135135 (D.runBytes
136136 (D.oneOf
137− [ D.cell (D.const D.cord "tape") D.tape
138− , D.cell (D.const D.cord "cord") D.cord
137+ [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
138+ , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
139139 ]
140140 )
141141 (Bytes.fromByteValues [ 0x01, 0x9F, 0x2E, 0x0C, 0xAE, 0x1C, 0x1C, 0x1D, 0x3C, 0x2D ])
@@ -147,8 +147,8 @@ tests =
147147 (Just "hi")
148148 (D.runBytes
149149 (D.oneOf
150− [ D.cell (D.const D.cord "tape") D.tape
151− , D.cell (D.const D.cord "cord") D.cord
150+ [ D.cell (D.const D.cord "tape") D.tape |> D.map (\((), t) -> t)
151+ , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
152152 ]
153153 )
154154 (Bytes.fromByteValues [ 0x01, 0x7F, 0xEC, 0x4D, 0x8E, 0x0C, 0x1E, 0x2D, 0x0D ])
@@ -160,7 +160,7 @@ tests =
160160 Expect.equal
161161 (Just ( 8, -8 ))
162162 (D.runBytes
163− (D.cell D.signedInt D.signedInt |> D.map Tuple.pair)
163+ (D.cell D.signedInt D.signedInt)
164164 (Bytes.fromByteValues [ 0xC1, 0x20, 0xE4, 0x01 ])
165165 )
166166 )
@@ -190,7 +190,7 @@ tests =
190190 (\( f, i, ui ) ->
191191 Expect.equal (Just ( f, i, ui ))
192192 (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 )))
194194 (C.cell (C.float64 f) (C.cell (C.signedInt i) (C.int ui)))
195195 )
196196 )