on 8168841 help sign in
20 files changed+389−444
since splitsnapshots
unifiedsplit
LICENSE−7 README.md+1−8 docs/sink.md−101 elm.json+2−11 example/src/Sink.elm+6−1 example/src/Vanilla.elm+9−10 example/urbit/app/journal.hoon+8−7 example/urbit/lib/sink.hoon+24−23 src/BitWriter.elm+5−5 src/Ur.elm+2−2 src/Ur/Constructor.elm+2−2 src/Ur/Deconstructor.elm+225−207 src/Ur/NounDiff.elm+61−21 src/Ur/Phonemic.elm+1−1 src/Ur/Requests.elm+1−1 src/Ur/Run.elm+12−10 src/Ur/Sub.elm+10−7 src/Ur/Sub/Internal.elm+1−1 src/Ur/Uw.elm+8−8 tests/Test/Urbit.elm+11−11
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+1−8
@@ -6,17 +6,10 @@
66
77### Demo
88
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).
9+[![Demo](https://img.youtube.com/vi/KV49E-D-SOY/default.jpg)](https://youtu.be/KV49E-D-SOY)
1510
1611### FFI
1712
18−This is for setting up this library from scratch.
19−
2013Urbit subscriptions require [server-side event (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
2114Unfortunately, Elm does not provide a native implementation.
2215This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
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 @@
33 "name": "ilyakooo0/airlock",
44 "summary": "Urbit airlock",
55 "license": "MIT",
6− "version": "1.1.1",
6+ "version": "1.0.0",
77 "exposed-modules": [
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"
8+ "Ur", "Ur.Cmd", "Ur.Sub", "Ur.Constructor", "Ur.Deconstructor", "Ur.Phonemic", "Ur.Da", "Ur.Uw", "Ur.Run", "Ur.Types"
189 ],
1910 "elm-version": "0.19.0 <= v < 0.20.0",
2011 "dependencies": {
example/src/Sink.elm+6−1
@@ -14,6 +14,7 @@ import Ur.Constructor as C
1414import Ur.Deconstructor as D
1515import Ur.Run
1616import Ur.Sub
17+import Ur.Types exposing (Noun)
1718import Widget
1819import Widget.Icon as Icon
1920import Widget.Material as Material
@@ -55,7 +56,7 @@ main =
5556 , app = "journal"
5657 , path = [ "sync" ]
5758 , deconstructor =
58− D.list (D.cell D.bigint D.cord)
59+ D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b )))
5960 |> D.map GotListings
6061 }
6162 ]
@@ -79,6 +80,7 @@ type alias Model =
7980
8081type Msg
8182 = Noop
83+ | GotSink Noun
8284 | Error String
8385 | GotListings (List ( BigInt, String ))
8486 | UpdateNewEntry String
@@ -94,6 +96,9 @@ update msg model =
9496 Noop ->
9597 ( model, Ur.Cmd.none )
9698
99+ GotSink _ ->
100+ ( model, Ur.Cmd.none )
101+
97102 Error err ->
98103 ( { model | error = err }, Ur.Cmd.none )
99104
example/src/Vanilla.elm+9−10
@@ -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))
49+ (D.list (D.cell D.bigint D.cord |> D.map (\a b -> ( a, b ))))
50+ |> D.map GotListings
5051 )
51− |> D.map (\( (), ( (), listings ) ) -> GotListings listings)
5252 }
5353 ]
5454 |> Ur.Cmd.cmd
@@ -177,15 +177,14 @@ type JournalUpdate
177177 | Delete BigInt
178178
179179
180−decodeJournalUpdate : D.Deconstructor JournalUpdate
180+decodeJournalUpdate : D.Deconstructor (JournalUpdate -> a) a
181181decodeJournalUpdate =
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)
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+ ]
189188
190189
191190view : Model -> Document Msg
example/urbit/app/journal.hoon+8−7
@@ -21,11 +21,12 @@
2121
2222%- agent:dbug
2323=/ state *state-0
24−=* entries (tap:j-orm journal.state)
25−=/ snik (sink ~[/sync])
26−=/ sink (snik entries)
24+=/ snik
25+ %+ sink ~[/sync]
26+ |=(stat=versioned-state (tap:j-orm journal.stat))
27+=/ sink (snik state)
2728^- agent:gall
28−::
29+
2930|_ =bowl:gall
3031+* this .
3132 def ~(. (default-agent this %|) bowl)
@@ -39,7 +40,7 @@
3940 |= old-vase=vase
4041 ^- (quip card _this)
4142 =/ state !<(versioned-state old-vase)
42− `this(state state, sink (snik entries))
43+ `this(state state, sink (snik state))
4344::
4445++ on-poke
4546 |= [=mark =vase]
@@ -50,7 +51,7 @@
5051 =/ now=@ (unique-time now.bowl log.state)
5152 =/ act !<(action vase)
5253 =. state (poke-action act)
53− =^ card sink (sync:sink entries)
54+ =^ card sink (sync:sink state)
5455 :_ this(log.state (put:log-orm log.state now act))
5556 ~[(fact:io journal-update+!>(`update`[now act]) ~[/updates]) card]
5657 ::
@@ -92,7 +93,7 @@
9293 [%all ~]
9394 :^ ~ ~ %journal-update
9495 !> ^- update
95− [now %jrnl entries]
96+ [now %jrnl (tap:j-orm journal.state)]
9697 ::
9798 [%before @ @ ~]
9899 =/ before=@ (rash i.t.t.t.path dem)
example/urbit/lib/sink.hoon+24−23
@@ -1,26 +1,27 @@
11/+ noun-diff
22|%
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− --
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+ --
2627--
src/BitWriter.elm+5−5
@@ -14,17 +14,17 @@ import Bytes.Encode as BE
1414
1515empty : BitWriter
1616empty =
17− BitWriter { running = Nothing, collected = [], offset = 0 }
17+ BitWriter { running = Nothing, collected = BE.sequence [], offset = 0 }
1818
1919
2020run : BitWriter -> Bytes
2121run (BitWriter { running, collected }) =
2222 case running of
2323 Nothing ->
24− BE.encode (collected |> List.reverse |> BE.sequence)
24+ BE.encode collected
2525
2626 Just { value } ->
27− BE.sequence (BE.unsignedInt8 value :: collected |> List.reverse) |> BE.encode
27+ BE.sequence [ collected, BE.unsignedInt8 value ] |> BE.encode
2828
2929
3030type BitWriter
@@ -35,7 +35,7 @@ type BitWriter
3535 , length : Int
3636 }
3737 , offset : Int
38− , collected : List BE.Encoder
38+ , collected : 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.unsignedInt8 newValue :: collected
71+ , collected = BE.sequence [ collected, BE.unsignedInt8 newValue ]
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
80+ , success : D.Deconstructor (msg -> msg) 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
94+ , success : D.Deconstructor (msg -> msg) 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+225−207
@@ -6,16 +6,24 @@ module Ur.Deconstructor exposing
66 , int, signedInt, bigint
77 , float32, float64
88 , cord, tape
9− , bytes, sig, ignore, tar, lazy, debug
9+ , bytes, sig, ignore, tar, lazy
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+
1416You would parse a `[%edit @ cord]` like this:
1517
1618 type alias Edit = {id: Int, text : String}
1719
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`.
1927
2028@docs Deconstructor
2129
@@ -55,7 +63,7 @@ You would parse a `[%edit @ cord]` like this:
5563
5664# Miscellaneous
5765
58−@docs bytes, sig, ignore, tar, lazy, debug
66+@docs bytes, sig, ignore, tar, lazy
5967
6068-}
6169
@@ -66,74 +74,139 @@ import Bytes exposing (Bytes, Endianness(..))
6674import Bytes.Decode as BD
6775import Bytes.Encode as BE
6876import Bytes.Extra
69−import Ur.Jam exposing (cue, isSig)
77+import Ur.Jam exposing (cue)
7078import Ur.Types exposing (..)
7179
7280
7381{-| A `Noun` deconstructor.
7482-}
75−type alias Deconstructor a =
76− Noun -> Maybe a
83+type Deconstructor a b
84+ = Deconstructor (Noun -> a -> Maybe b)
7785
7886
7987{-| Executes a `Deconstructor` on a `Noun`.
8088-}
81−run : Deconstructor a -> Noun -> Maybe a
82−run f noun =
83− f noun
89+run : Deconstructor (a -> a) a -> Noun -> Maybe a
90+run (Deconstructor f) noun =
91+ f noun identity
8492
8593
8694{-| Executes a deconstructor on a `jam`med `Noun`.
8795-}
88−runBytes : Deconstructor a -> Bytes -> Maybe a
89−runBytes f bs =
90− cue bs |> Maybe.andThen f
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)
91101
92102
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.
94104
95−The first argument is a `Deconstructor` for the given type.
105+The first argument is a `Deconstructor` for the gven tyoe.
96106
97107The second argument is the value to compare with.
98108
99109This is useful to match on `term`s when there are multiple possible cases in a head-tagged union.
100110
101111-}
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 ()
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
108120
109− else
110− Nothing
121+ else
122+ Nothing
111123
112− Nothing ->
113− Nothing
124+ Nothing ->
125+ Nothing
126+ )
114127
115128
116−{-| Extracts a `BigInt`.
129+{-| Extracts a `cord` at the current location.
117130-}
118−bigint : Deconstructor BigInt
119−bigint x =
120− case x of
121− Atom bs ->
122− BigInt.Bytes.decode bs |> Just
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+ )
123143
124− Cell _ ->
125− Nothing
144+
145+{-| Extracts a `tape` at the current location.
146+-}
147+tape : Deconstructor (String -> a) a
148+tape =
149+ list cord |> fmap String.concat
126150
127151
128−{-| Extracts a 32-bit signed `Int`.
152+{-| Extracts a 32-bit unsigned `Int` at the given location.
129153
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.
131155
132156-}
133−signedInt : Deconstructor Int
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
134207signedInt =
135208 int
136− |> map
209+ |> fmap
137210 (\i ->
138211 if Bitwise.and 1 i == 1 then
139212 -(Bitwise.shiftRightBy 1 (1 + i))
@@ -143,85 +216,105 @@ signedInt =
143216 )
144217
145218
146−{-| Extracts a 32-bit `Float`.
219+{-| Extracts a 32-bit `Float` at the given location.
147220-}
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
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+ )
156233
157234
158−{-| Extracts a 64-bit `Float`.
235+{-| Extracts a 64-bit `Float` at the given location.
159236-}
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
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+ )
168249
169250
170−{-| Extracts the raw `Byte`s of an `Atom`.
251+{-| Extracts the raw `Byte`s of an `Atom` at the current location.
171252-}
172−bytes : Deconstructor Bytes
173−bytes n =
174− case n of
175− Atom bs ->
176− Just bs
253+bytes : Deconstructor (Bytes -> a) a
254+bytes =
255+ Deconstructor
256+ (\n f ->
257+ case n of
258+ Atom bs ->
259+ f bs |> Just
177260
178− Cell _ ->
179− Nothing
261+ Cell _ ->
262+ Nothing
263+ )
180264
181265
182−{-| Asserts the the `Atom` should be exactly `sig` (`~`).
266+{-| Asserts the the `Atom` at the current position should be exactly `sig` (`~`).
183267-}
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 ()
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
190276
191− else
192− Nothing
277+ else
278+ Nothing
193279
194− Cell _ ->
195− Nothing
280+ Cell _ ->
281+ Nothing
282+ )
196283
197284
198−{-| Extracts a sig-terminated list of arbitrary elements.
285+{-| Extracts a sig-terminated list of arbitrary elements at the current position.
199286
200287The first argument is a `Deconstructor` of the elements of the list.
201288
202289-}
203−list : Deconstructor a -> Deconstructor (List a)
204−list f n =
205− let
206− go n_ =
207− case n_ of
208− Atom _ ->
209− Just []
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 []
210299
211− Cell ( el, tail ) ->
212− Maybe.map2 (\a b -> a :: b) (f el) (go tail)
213− in
214− go n
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+ )
215305
216306
217−alt : Deconstructor a -> Deconstructor a -> Deconstructor a
218−alt f g n =
219− case f n of
220− Just x ->
221− Just x
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
222314
223− Nothing ->
224− g n
315+ Nothing ->
316+ g n a
317+ )
225318
226319
227320{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
@@ -229,150 +322,75 @@ alt f g n =
229322This is especially useful for deconstructing head-tagged unions from Hoon.
230323
231324-}
232−oneOf : List (Deconstructor a) -> Deconstructor a
325+oneOf : List (Deconstructor a b) -> Deconstructor a b
233326oneOf l =
234327 case l of
235328 [] ->
236− always Nothing
329+ Deconstructor (\_ _ -> Nothing)
237330
238331 x :: xs ->
239332 alt x (oneOf xs)
240333
241334
242−{-| Ignore any value.
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
335+{-| Extract the raw `Noun` at the current position.
275336
337+Always succeeds.
276338
277−{-| Maps over the result of the deconstructor.
278339-}
279−map : (a -> b) -> Deconstructor a -> Deconstructor b
280−map g f noun =
281− f noun |> Maybe.map g
340+tar : Deconstructor (Noun -> a) a
341+tar =
342+ Deconstructor (\noun f -> Just (f noun))
282343
283344
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
345+{-| Ignore any value at the current position.
294346
347+This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
295348
296−{-| Extracts a `tape`.
297349-}
298−tape : Deconstructor String
299−tape =
300− list cord |> map String.concat
350+ignore : Deconstructor a a
351+ignore =
352+ Deconstructor (\_ f -> Just f)
301353
302354
303−{-| Extracts a 32-bit unsigned `Int`.
355+{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
304356
305−If the `Atom` is larger than 32 bits the the behaviour is undefined.
357+Accepts two arbitrary `Deconstructor`s that form a Cell.
306358
307359-}
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
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)
324367
325− _ ->
368+ Atom _ ->
326369 Nothing
327−
328− Cell _ ->
329− Nothing
370+ )
330371
331372
332−{-| Extract the raw `Noun`.
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+ )
333382
334−Always succeeds.
335383
336−-}
337−tar : Deconstructor Noun
338−tar =
339− Just
384+{-| Maps (applies) a function to all of the values deconstructed.
340385
386+This is useful when you want to create a data type with extracted values as fields.
341387
342−{-| Debug a deconstructor.
343−If the deconstructor from the second argument fails then the failed nock will be printed to the console.
344388-}
345−debug : (String -> String) -> Deconstructor a -> Deconstructor a
346−debug log f noun =
347− case f noun of
348− Just x ->
349− Just x
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)
350392
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− "~"
367393
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
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))
src/Ur/NounDiff.elm+61−21
@@ -11,17 +11,29 @@ type Patch
1111 | Diff DelDiff InsDiff
1212
1313
14−deconstructPatch : () -> D.Deconstructor Patch
15−deconstructPatch () =
14+deconstructPatch : D.Deconstructor (Patch -> c) c
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 (\( (), ( 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)
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
2537 ]
2638
2739
@@ -81,17 +93,31 @@ type DelDiff
8193 | DelCell DelDiff DelDiff
8294
8395
84−deconstructDel : () -> D.Deconstructor DelDiff
85−deconstructDel () =
96+deconstructDel : D.Deconstructor (DelDiff -> c) c
97+deconstructDel =
8698 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)
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
89101 , D.cell (D.const D.cord "cell")
90102 (D.cell
91− (D.lazy deconstructDel)
92− (D.lazy deconstructDel)
103+ deconstructDel_
104+ deconstructDel_
93105 )
94− |> D.map (\( (), ( lhs, rhs ) ) -> DelCell lhs rhs)
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
95121 ]
96122
97123
@@ -101,15 +127,29 @@ type InsDiff
101127 | InsCell InsDiff InsDiff
102128
103129
104−deconstructIns : () -> D.Deconstructor InsDiff
105−deconstructIns () =
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_ =
106146 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)
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
109149 , D.cell (D.const D.cord "cell")
110150 (D.cell
111− (D.lazy deconstructIns)
112− (D.lazy deconstructIns)
151+ (D.lazy (\() -> deconstructIns))
152+ (D.lazy (\() -> deconstructIns))
113153 )
114− |> D.map (\( (), ( a, b ) ) -> InsCell a b)
154+ |> D.map InsCell
115155 ]
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 "unsubscribe") <|
101+ C.cell (C.cord "usubscribe") <|
102102 C.cell (C.int eventId) (C.int subId)
103103
104104 Poke { ship, agent, mark, noun } ->
src/Ur/Run.elm+12−10
@@ -31,7 +31,7 @@ type alias SubDict msg =
3131 Dict
3232 -- (ship, agent, path)
3333 ( String, String, List String )
34− { deconstructor : D.Deconstructor msg
34+ { deconstructor : D.Deconstructor (msg -> msg) 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 appModel |> (\(Ur.Sub.Internal.Sub x) -> x))
181+ (inp.urbitSubscriptions model.app |> (\(Ur.Sub.Internal.Sub x) -> x))
182182
183183 ( eventId_, cmds, urReqs ) =
184184 processCmd subsResult.eventId appCmds
@@ -211,10 +211,12 @@ update inp msg model =
211211 Ok string ->
212212 case
213213 D.runBytes
214− (D.cell D.int (D.cell D.cord D.tar))
214+ (D.cell D.int (D.cell D.cord D.tar)
215+ |> D.map (\a b c -> ( a, b, c ))
216+ )
215217 (Ur.Uw.decode string)
216218 of
217− Just ( messageId, ( messageType, rest ) ) ->
219+ Just ( messageId, messageType, rest ) ->
218220 let
219221 ( eventId, ackReqs ) =
220222 tag model.eventId [ Ack messageId ]
@@ -246,7 +248,7 @@ update inp msg model =
246248 of
247249 Just { deconstructor, sink } ->
248250 if sink then
249− case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink) |> D.map (\( (), ( (), s ) ) -> s)) rest of
251+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructSink)) rest of
250252 Just (Flush noun) ->
251253 ( { model | sinks = Dict.insert messageId noun model.sinks }
252254 , case D.run deconstructor noun of
@@ -282,7 +284,7 @@ update inp msg model =
282284 ( model, Cmd.none )
283285
284286 else
285− case D.run (D.cell D.ignore (D.cell D.ignore deconstructor) |> D.map (\( (), ( (), m ) ) -> m)) rest of
287+ case D.run (D.cell D.ignore (D.cell D.ignore deconstructor)) rest of
286288 Just subMsg ->
287289 ( model_, pureCmd (AppMsg subMsg) )
288290
@@ -313,7 +315,7 @@ update inp msg model =
313315 NeedsActivation ->
314316 let
315317 ( eventId, reqs ) =
316− [ Poke { ship = "", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
318+ [ Poke { ship = "~zod", agent = "hood", mark = "helm-hi", noun = C.cord "Opening airlock!" } ]
317319 |> tag model.eventId
318320 in
319321 ( { model | eventId = eventId }
@@ -349,11 +351,11 @@ type Sink
349351 | Drain Patch
350352
351353
352−deconstructSink : D.Deconstructor Sink
354+deconstructSink : D.Deconstructor (Sink -> c) c
353355deconstructSink =
354356 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+ [ D.cell (D.const D.cord "flush") D.tar |> D.map Flush
358+ , D.cell (D.const D.cord "drain") deconstructPatch |> D.map Drain
357359 ]
358360
359361
src/Ur/Sub.elm+10−7
@@ -1,8 +1,11 @@
1−module Ur.Sub exposing (Sub, subscribe, none, batch, sink)
1+module Ur.Sub exposing
2+ ( Sub, subscribe, none, batch
3+ , sink
4+ )
25
3−{-| This module is conceptually similar to `Platform.Sub`, but allows you to subscribe to Urbit channels.
6+{-| This module is conceptually similar to `Platform.Sub`, but also you to subscribe to Urbit channels.
47
5−@docs Sub, subscribe, none, batch, sink
8+@docs Sub, subscribe, none, batch
69
710-}
811
@@ -11,7 +14,7 @@ import Ur.Deconstructor as D
1114import Ur.Sub.Internal
1215
1316
14−{-| Like `Sub` from `Platform.Sub` but for Urbit subscriptions.
17+{-| Like `Sub` from `Platform.Sub`, but for Urbit subscriptions.
1518-}
1619type alias Sub msg =
1720 Ur.Sub.Internal.Sub msg
@@ -27,7 +30,7 @@ type alias Sub msg =
2730 }
2831
2932-}
30−subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
33+subscribe : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
3134subscribe { ship, app, path, deconstructor } =
3235 Dict.singleton ( ship, app, path )
3336 { deconstructor = deconstructor
@@ -36,9 +39,9 @@ subscribe { ship, app, path, deconstructor } =
3639 |> Ur.Sub.Internal.Sub
3740
3841
39−{-| Creates a %sink subscription.
42+{-| Create a Sink subscription.
4043-}
41−sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor msg } -> Sub msg
44+sink : { ship : String, app : String, path : List String, deconstructor : D.Deconstructor (msg -> msg) msg } -> Sub msg
4245sink { ship, app, path, deconstructor } =
4346 Dict.singleton ( ship, app, path )
4447 { 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
13+ { deconstructor : D.Deconstructor (msg -> msg) 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 aka `@uw`.
3+{-| This module works with Urbit base-64 encoded strings.
44
55@docs decode, encode
66
@@ -29,24 +29,24 @@ decode string =
2929 -- 0w
3030 |> List.drop 2
3131
32− go : List Char -> List Int -> BW.BitWriter -> BW.BitWriter
33− go cs append writer =
32+ go : List Char -> BW.BitWriter -> BW.BitWriter
33+ go cs writer =
3434 case cs of
3535 [] ->
36− BW.bits append writer
36+ writer
3737
3838 '.' :: rest ->
39− go rest append writer
39+ go rest writer
4040
4141 c :: rest ->
4242 case Dict.get c charToBits of
4343 Just bits ->
44− go rest (bits ++ append) writer
44+ go rest writer |> BW.bits bits
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)
80+ (D.cell D.int D.int |> D.map Tuple.pair)
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))
99+ (D.cell D.int (D.list D.int) |> D.map Tuple.pair)
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)
108+ (D.cell D.float32 D.float32 |> D.map Tuple.pair)
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)
117+ (D.cell D.int D.cord |> D.map Tuple.pair)
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)
126+ (D.cell D.int D.tape |> D.map Tuple.pair)
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 |> D.map (\((), t) -> t)
138− , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
137+ [ D.cell (D.const D.cord "tape") D.tape
138+ , D.cell (D.const D.cord "cord") D.cord
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 |> D.map (\((), t) -> t)
151− , D.cell (D.const D.cord "cord") D.cord |> D.map (\((), c) -> c)
150+ [ D.cell (D.const D.cord "tape") D.tape
151+ , D.cell (D.const D.cord "cord") D.cord
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)
163+ (D.cell D.signedInt D.signedInt |> D.map Tuple.pair)
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 )