on 0b17a24 help sign in
LICENSEdeleted−7
since splitsnapshots
unifiedsplit
@@ -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 @@
6
6
7### Demo
7### Demo
8
8
9−[![DEMO](https://img.youtube.com/vi/tynCzifvIzE/0.jpg)](https://www.youtube.com/watch?v=tynCzifvIzE)
9+[![Demo](https://img.youtube.com/vi/KV49E-D-SOY/default.jpg)](https://youtu.be/KV49E-D-SOY)
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
10
16### FFI
11### FFI
17
12
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).
13Urbit 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.
14Unfortunately, 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).
15This means that we will need to use [JS FFI (aka ports)](https://guide.elm-lang.org/interop/ports.html).
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/src/Sink.elm+2−1
@@ -14,6 +14,7 @@ import Ur.Constructor as C
14import Ur.Deconstructor as D
14import Ur.Deconstructor as D
15import Ur.Run
15import Ur.Run
16import Ur.Sub
16import Ur.Sub
17+import Ur.Types exposing (Noun)
17import Widget
18import Widget
18import Widget.Icon as Icon
19import Widget.Icon as Icon
19import Widget.Material as Material
20import Widget.Material as Material
@@ -55,7 +56,7 @@ main =
55 , app = "journal"
56 , app = "journal"
56 , path = [ "sync" ]
57 , path = [ "sync" ]
57 , deconstructor =
58 , 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 )))
59 |> D.map GotListings
60 |> D.map GotListings
60 }
61 }
61 ]
62 ]
example/src/Vanilla.elm+9−10
@@ -46,9 +46,9 @@ main =
46 , success =
46 , success =
47 D.cell D.ignore
47 D.cell D.ignore
48 (D.cell (D.const D.cord "jrnl")
48 (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
50 )
51 )
51− |> D.map (\( (), ( (), listings ) ) -> GotListings listings)
52 }
52 }
53 ]
53 ]
54 |> Ur.Cmd.cmd
54 |> Ur.Cmd.cmd
@@ -177,15 +177,14 @@ type JournalUpdate
177 | Delete BigInt
177 | Delete BigInt
178
178
179
179
180−decodeJournalUpdate : D.Deconstructor JournalUpdate
180+decodeJournalUpdate : D.Deconstructor (JournalUpdate -> a) a
181decodeJournalUpdate =
181decodeJournalUpdate =
182+ D.cell D.ignore <|
182− D.oneOf
183+ 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 "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)
185+ , (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)
186+ , D.cell (D.const D.cord "del") D.bigint |> D.map Delete
186− ]
187+ ]
187− |> D.cell D.ignore
188− |> D.map (\( (), upd ) -> upd)
189
188
190
189
191view : Model -> Document Msg
190view : Model -> Document Msg
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+2−2
@@ -77,7 +77,7 @@ scry :
77 , agent : Agent
77 , agent : Agent
78 , path : Path
78 , path : Path
79 , error : msg
79 , error : msg
80− , success : D.Deconstructor msg
80+ , success : D.Deconstructor (msg -> msg) msg
81 }
81 }
82 -> Cmd msg
82 -> Cmd msg
83scry args =
83scry args =
@@ -91,7 +91,7 @@ scryTask :
91 , agent : Agent
91 , agent : Agent
92 , path : Path
92 , path : Path
93 , error : msg
93 , error : msg
94− , success : D.Deconstructor msg
94+ , success : D.Deconstructor (msg -> msg) msg
95 }
95 }
96 -> Task a msg
96 -> Task a msg
97scryTask { url, agent, path, error, success } =
97scryTask { url, agent, path, error, success } =
src/Ur/Deconstructor.elm+226−206
@@ -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, lazy
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+The principal (and types) are very similar to `Url.Parser` from `elm/url`.
15+
14You would parse a `[%edit @ cord]` like this:
16You would parse a `[%edit @ cord]` like this:
15
17
16 type alias Edit = {id: Int, text : String}
18 type alias Edit = {id: Int, text : String}
17
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, lazy
59
67
60-}
68-}
61
69
@@ -66,31 +74,33 @@ 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
95The first argument is a `Deconstructor` for the given type.
105The first argument is a `Deconstructor` for the given type.
96
106
@@ -99,41 +109,104 @@ The second argument is the value to compare with.
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,77 @@ 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
327−
370+ )
328− Cell _ ->
329− Nothing
330−
331−
332−{-| Extract the raw `Noun`.
333−
334−Always succeeds.
335−
336−-}
337−tar : Deconstructor Noun
338−tar =
339− Just
340
371
341
372
342−{-| Debug a deconstructor.
373+{-| "Lazily" applies a deconstructor.
343−If the deconstructor from the second argument fails then the failed nock will be printed to the console.
374+This is useful when you are defining a recursive `Deconstructor` which needs to call itself.
344-}
375-}
345−debug : (String -> String) -> Deconstructor a -> Deconstructor a
376+lazy : (() -> Deconstructor a b) -> Deconstructor a b
346−debug log f noun =
377+lazy f =
378+ Deconstructor
379+ (\noun a ->
347− case f noun of
380+ case f () of
348− Just x ->
381+ Deconstructor g ->
349− Just x
382+ g noun a
383+ )
350
384
351− Nothing ->
352− let
353− _ =
354− log (prettyNoun noun)
355− in
356− Nothing
357
385
386+{-| Maps (applies) a function to all of the values deconstructed.
358
387
359−prettyNoun : Noun -> String
388+This is useful when you want to create a data type with extracted values as fields.
360−prettyNoun noun =
361− let
362− go isRhs n =
363− case n of
364− Atom a ->
365− if isSig a then
366− "~"
367
389
368− else
390+-}
369− "@"
391+map : a -> Deconstructor a b -> Deconstructor (b -> c) c
392+map a (Deconstructor f) =
393+ Deconstructor (\noun g -> f noun a |> Maybe.map g)
370
394
371− Cell ( lhs, rhs ) ->
372− if isRhs then
373− go False lhs ++ " " ++ go True rhs
374
395
375− else
396+fmap : (a -> b) -> Deconstructor (a -> c) c -> Deconstructor (b -> c) c
376− "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"
397+fmap f (Deconstructor g) =
377− in
398+ Deconstructor (\noun h -> g noun (f >> h))
378− go False noun
src/Ur/NounDiff.elm+61−21
@@ -11,17 +11,29 @@ type Patch
11 | Diff DelDiff InsDiff
11 | Diff DelDiff InsDiff
12
12
13
13
14−deconstructPatch : () -> D.Deconstructor Patch
14+deconstructPatch : D.Deconstructor (Patch -> c) c
15−deconstructPatch () =
15+deconstructPatch =
16 D.oneOf
16 D.oneOf
17 [ D.cell (D.const D.cord "cell")
17 [ D.cell (D.const D.cord "cell")
18 (D.cell
18 (D.cell
19− (D.lazy deconstructPatch)
19+ (D.lazy (\() -> deconstructPatch_))
20− (D.lazy deconstructPatch)
20+ (D.lazy (\() -> deconstructPatch_))
21 )
21 )
22− |> D.map (\( (), ( a, b ) ) -> PatchCell a b)
22+ |> D.map PatchCell
23− , D.cell (D.const D.cord "diff") (D.cell (D.lazy deconstructDel) (D.lazy deconstructIns))
23+ , D.cell (D.const D.cord "diff") (D.cell deconstructDel deconstructIns) |> D.map Diff
24− |> D.map (\( (), ( a, b ) ) -> Diff a b)
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
25 ]
37 ]
26
38
27
39
@@ -81,17 +93,31 @@ type DelDiff
81 | DelCell DelDiff DelDiff
93 | DelCell DelDiff DelDiff
82
94
83
95
84−deconstructDel : () -> D.Deconstructor DelDiff
96+deconstructDel : D.Deconstructor (DelDiff -> c) c
85−deconstructDel () =
97+deconstructDel =
86 D.oneOf
98 D.oneOf
87− [ D.cell (D.const D.cord "ignore") D.ignore |> D.map (\( (), () ) -> Ignore)
99+ [ 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)
100+ , D.cell (D.const D.cord "hole") D.int |> D.map DelHole
89 , D.cell (D.const D.cord "cell")
101 , D.cell (D.const D.cord "cell")
90 (D.cell
102 (D.cell
91− (D.lazy deconstructDel)
103+ deconstructDel_
92− (D.lazy deconstructDel)
104+ deconstructDel_
93 )
105 )
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
95 ]
121 ]
96
122
97
123
@@ -101,15 +127,29 @@ type InsDiff
101 | InsCell InsDiff InsDiff
127 | InsCell InsDiff InsDiff
102
128
103
129
104−deconstructIns : () -> D.Deconstructor InsDiff
130+deconstructIns : D.Deconstructor (InsDiff -> c) c
105−deconstructIns () =
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_ =
106 D.oneOf
146 D.oneOf
107− [ D.cell (D.const D.cord "hole") D.int |> D.map (\( (), x ) -> InsHole x)
147+ [ D.cell (D.const D.cord "hole") D.int |> D.map InsHole
108− , D.cell (D.const D.cord "atom") D.bytes |> D.map (\( (), bs ) -> InsAtom bs)
148+ , D.cell (D.const D.cord "atom") D.bytes |> D.map InsAtom
109 , D.cell (D.const D.cord "cell")
149 , D.cell (D.const D.cord "cell")
110 (D.cell
150 (D.cell
111− (D.lazy deconstructIns)
151+ (D.lazy (\() -> deconstructIns))
112− (D.lazy deconstructIns)
152+ (D.lazy (\() -> deconstructIns))
113 )
153 )
114− |> D.map (\( (), ( a, b ) ) -> InsCell a b)
154+ |> D.map InsCell
115 ]
155 ]
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+12−10
@@ -31,7 +31,7 @@ type alias SubDict msg =
31 Dict
31 Dict
32 -- (ship, agent, path)
32 -- (ship, agent, path)
33 ( String, String, List String )
33 ( String, String, List String )
34− { deconstructor : D.Deconstructor msg
34+ { deconstructor : D.Deconstructor (msg -> msg) msg
35 , number : Int
35 , number : Int
36 , sink : Bool
36 , sink : Bool
37 }
37 }
@@ -178,7 +178,7 @@ update inp msg model =
178 processUrSubs
178 processUrSubs
179 model.eventId
179 model.eventId
180 model.subscriptions
180 model.subscriptions
181− (inp.urbitSubscriptions appModel |> (\(Ur.Sub.Internal.Sub x) -> x))
181+ (inp.urbitSubscriptions model.app |> (\(Ur.Sub.Internal.Sub x) -> x))
182
182
183 ( eventId_, cmds, urReqs ) =
183 ( eventId_, cmds, urReqs ) =
184 processCmd subsResult.eventId appCmds
184 processCmd subsResult.eventId appCmds
@@ -211,10 +211,12 @@ update inp msg model =
211 Ok string ->
211 Ok string ->
212 case
212 case
213 D.runBytes
213 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+ )
215 (Ur.Uw.decode string)
217 (Ur.Uw.decode string)
216 of
218 of
217− Just ( messageId, ( messageType, rest ) ) ->
219+ Just ( messageId, messageType, rest ) ->
218 let
220 let
219 ( eventId, ackReqs ) =
221 ( eventId, ackReqs ) =
220 tag model.eventId [ Ack messageId ]
222 tag model.eventId [ Ack messageId ]
@@ -246,7 +248,7 @@ update inp msg model =
246 of
248 of
247 Just { deconstructor, sink } ->
249 Just { deconstructor, sink } ->
248 if sink then
250 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
250 Just (Flush noun) ->
252 Just (Flush noun) ->
251 ( { model | sinks = Dict.insert messageId noun model.sinks }
253 ( { model | sinks = Dict.insert messageId noun model.sinks }
252 , case D.run deconstructor noun of
254 , case D.run deconstructor noun of
@@ -282,7 +284,7 @@ update inp msg model =
282 ( model, Cmd.none )
284 ( model, Cmd.none )
283
285
284 else
286 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
286 Just subMsg ->
288 Just subMsg ->
287 ( model_, pureCmd (AppMsg subMsg) )
289 ( model_, pureCmd (AppMsg subMsg) )
288
290
@@ -313,7 +315,7 @@ update inp msg model =
313 NeedsActivation ->
315 NeedsActivation ->
314 let
316 let
315 ( eventId, reqs ) =
317 ( 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!" } ]
317 |> tag model.eventId
319 |> tag model.eventId
318 in
320 in
319 ( { model | eventId = eventId }
321 ( { model | eventId = eventId }
@@ -349,11 +351,11 @@ type Sink
349 | Drain Patch
351 | Drain Patch
350
352
351
353
352−deconstructSink : D.Deconstructor Sink
354+deconstructSink : D.Deconstructor (Sink -> c) c
353deconstructSink =
355deconstructSink =
354 D.oneOf
356 D.oneOf
355− [ D.cell (D.const D.cord "flush") D.tar |> D.map (\( (), n ) -> Flush n)
357+ [ D.cell (D.const D.cord "flush") D.tar |> D.map Flush
356− , D.cell (D.const D.cord "drain") (D.lazy deconstructPatch) |> D.map (\( (), patch ) -> Drain patch)
358+ , D.cell (D.const D.cord "drain") deconstructPatch |> D.map Drain
357 ]
359 ]
358
360
359
361
src/Ur/Sub.elm+7−4
@@ -1,8 +1,11 @@
1+module Ur.Sub exposing
1−module Ur.Sub exposing (Sub, subscribe, none, batch, sink)
2+ ( Sub, subscribe, none, batch
3+ , sink
4+ )
2
5
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 allows you to subscribe to Urbit channels.
4
7
5−@docs Sub, subscribe, none, batch, sink
8+@docs Sub, subscribe, none, batch
6
9
7-}
10-}
8
11
@@ -27,7 +30,7 @@ type alias Sub msg =
27 }
30 }
28
31
29-}
32-}
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
31subscribe { ship, app, path, deconstructor } =
34subscribe { ship, app, path, deconstructor } =
32 Dict.singleton ( ship, app, path )
35 Dict.singleton ( ship, app, path )
33 { deconstructor = deconstructor
36 { deconstructor = deconstructor
@@ -38,7 +41,7 @@ subscribe { ship, app, path, deconstructor } =
38
41
39{-| Creates a %sink subscription.
42{-| Creates a %sink subscription.
40-}
43-}
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
42sink { ship, app, path, deconstructor } =
45sink { ship, app, path, deconstructor } =
43 Dict.singleton ( ship, app, path )
46 Dict.singleton ( ship, app, path )
44 { deconstructor = deconstructor
47 { deconstructor = deconstructor
src/Ur/Sub/Internal.elm+1−1
@@ -10,7 +10,7 @@ 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+ { deconstructor : D.Deconstructor (msg -> msg) msg
14 , sink : Bool
14 , sink : Bool
15 }
15 }
16 )
16 )
src/Ur/Uw.elm+7−7
@@ -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 )