@@ -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
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.
14−You would parse a `[%edit @ cord]` like this:
14+The principal (and types) are very similar to `Url.Parser` from `elm/url`.
16− type alias Edit = {id: Int, text : String}
16+You would parse a `[%edit cord @]` like this:
18+ type alias Edit = {text : String, id: Int}
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
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`.
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`.
20@docs Deconstructor
28@docs Deconstructor
@@ -55,7 +63,7 @@ You would parse a `[%edit @ cord]` like this:
56# Miscellaneous
64# Miscellaneous
58−@docs bytes, sig, ignore, tar, lazy, debug
66+@docs bytes, sig, ignore, tar
@@ -66,74 +74,139 @@ import Bytes exposing (Bytes, Endianness(..))
66import Bytes.Decode as BD
74import Bytes.Decode as BD
67import Bytes.Encode as BE
75import Bytes.Encode as BE
68import Bytes.Extra
76import Bytes.Extra
69−import Ur.Jam exposing (cue, isSig)
77+import Ur.Jam exposing (cue)
70import Ur.Types exposing (..)
78import Ur.Types exposing (..)
73{-| A `Noun` deconstructor.
81{-| A `Noun` deconstructor.
75−type alias Deconstructor a =
83+type Deconstructor a b
76− Noun -> Maybe a
84+ = Deconstructor (Noun -> a -> Maybe b)
79{-| Executes a `Deconstructor` on a `Noun`.
87{-| Executes a `Deconstructor` on a `Noun`.
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
86{-| Executes a deconstructor on a `jam`med `Noun`.
94{-| Executes a deconstructor on a `jam`med `Noun`.
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 =
90− cue bs |> Maybe.andThen f
99+ |> Maybe.andThen
100+ (\noun -> f noun identity)
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.
95−The first argument is a `Deconstructor` for the given type.
105+The first argument is a `Deconstructor` for the gven tyoe.
97The second argument is the value to compare with.
107The 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.
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 =
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
112− Nothing ->
124+ Nothing ->
116−{-| Extracts a `BigInt`.
129+{-| Extracts a `cord` at the current location.
118−bigint : Deconstructor BigInt
131+cord : Deconstructor (String -> a) a
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
125− Nothing
145+{-| Extracts a `tape` at the current location.
147+tape : Deconstructor (String -> a) a
149+ list cord |> fmap String.concat
128−{-| Extracts a 32-bit signed `Int`.
152+{-| Extracts a 32-bit unsigned `Int` at the given location.
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.
157+int : Deconstructor (Int -> a) a
163+ (case Bytes.width bs of
165+ BD.decode BD.unsignedInt8 bs
168+ BD.decode (BD.unsignedInt16 LE) bs
171+ BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
174+ BD.decode (BD.unsignedInt32 LE) bs
186+{-| Extracts a `BigInt` at the current location.
188+bigint : Deconstructor (BigInt -> a) a
194+ BigInt.Bytes.decode bs |> f |> Just
201+{-| Extracts a 32-bit signed `Int` at the given location.
203+If the `Atom` at the current location is larger than 32 bits the the behaviour is undefined.
133−signedInt : Deconstructor Int
206+signedInt : Deconstructor (Int -> a) a
134signedInt =
207signedInt =
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 =
146−{-| Extracts a 32-bit `Float`.
219+{-| Extracts a 32-bit `Float` at the given location.
148−float32 : Deconstructor Float
221+float32 : Deconstructor (Float -> a) a
149−float32 x =
222+float32 =
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
154− Cell _ ->
230+ Cell _ ->
158−{-| Extracts a 64-bit `Float`.
235+{-| Extracts a 64-bit `Float` at the given location.
160−float64 : Deconstructor Float
237+float64 : Deconstructor (Float -> a) a
161−float64 x =
238+float64 =
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
166− Cell _ ->
246+ Cell _ ->
170−{-| Extracts the raw `Byte`s of an `Atom`.
251+{-| Extracts the raw `Byte`s of an `Atom` at the current location.
172−bytes : Deconstructor Bytes
253+bytes : Deconstructor (Bytes -> a) a
174− case n of
257+ case n of
175− Atom bs ->
258+ Atom bs ->
176− Just bs
259+ f bs |> Just
178− Cell _ ->
261+ Cell _ ->
182−{-| Asserts the the `Atom` should be exactly `sig` (`~`).
266+{-| Asserts the the `Atom` at the current position should be exactly `sig` (`~`).
184−sig : Deconstructor ()
268+sig : Deconstructor a 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
194− Cell _ ->
280+ Cell _ ->
198−{-| Extracts a sig-terminated list of arbitrary elements.
285+{-| Extracts a sig-terminated list of arbitrary elements at the current position.
200The first argument is a `Deconstructor` of the elements of the list.
287The first argument is a `Deconstructor` of the elements of the list.
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) =
207− case n_ of
296+ case n_ of
208− Atom _ ->
297+ Atom _ ->
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)
214− go n
303+ go n |> Maybe.map g
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 ->
223− Nothing ->
315+ Nothing ->
227{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
320{-| Try to execute all of the `Deconstructor`s in order until one succeeds.
@@ -229,150 +322,64 @@ alt f g n =
229This is especially useful for deconstructing head-tagged unions from Hoon.
322This is especially useful for deconstructing head-tagged unions from Hoon.
232−oneOf : List (Deconstructor a) -> Deconstructor a
325+oneOf : List (Deconstructor a b) -> Deconstructor a b
234 case l of
327 case l of
236− always Nothing
329+ Deconstructor (\_ _ -> Nothing)
238 x :: xs ->
331 x :: xs ->
239 alt x (oneOf xs)
332 alt x (oneOf xs)
242−{-| Ignore any value.
335+{-| Extract the raw `Noun` at the current position.
244−This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
247−ignore : Deconstructor ()
252−{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
254−Accepts two arbitrary `Deconstructor`s that form a Cell.
257−cell : Deconstructor a -> Deconstructor b -> Deconstructor ( a, b )
260− Cell ( lhs, rhs ) ->
261− l lhs |> Maybe.andThen (\a -> r rhs |> Maybe.map (\b -> ( a, b )))
267−{-| Maps (applies) a function to all of the values deconstructed.
269−This is useful when you want to create a data type with extracted values as fields.
272−lazy : (() -> Deconstructor a) -> Deconstructor a
277−{-| Maps over the result of the deconstructor.
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))
284−{-| Extracts a `cord`.
286−cord : Deconstructor String
290− BD.decode (BD.string (Bytes.width bs)) bs
292− Cell _ ->
345+{-| Ignore any value at the current position.
347+This is useful when you have a value you don't care about. `ignore` allows you to just skip the value.
296−{-| Extracts a `tape`.
298−tape : Deconstructor String
350+ignore : Deconstructor a a
300− list cord |> map String.concat
352+ Deconstructor (\_ f -> Just f)
303−{-| Extracts a 32-bit unsigned `Int`.
355+{-| Extracts a [`Cell`](https://developers.urbit.org/reference/glossary/cell) (pair) of two arbitrary values.
305−If the `Atom` is larger than 32 bits the the behaviour is undefined.
357+Accepts two arbitrary `Deconstructor`s that form a Cell.
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)
317− BD.decode (BD.unsignedInt16 LE) bs
320− BD.decode (BD.unsignedInt32 LE) (BE.encode (BE.sequence [ BE.bytes bs, BE.unsignedInt8 0 ]))
323− BD.decode (BD.unsignedInt32 LE) bs
332−{-| Extract the raw `Noun`.
336−-}
373+{-| Maps (applies) a function to all of the values deconstructed.
337−tar : Deconstructor Noun
375+This is useful when you want to create a data type with extracted values as fields.
342−{-| Debug a deconstructor.
343−If the deconstructor from the second argument fails then the failed nock will be printed to the console.
345−debug : (String -> String) -> Deconstructor a -> Deconstructor a
378+map : a -> Deconstructor a b -> Deconstructor (b -> c) c
346−debug log f noun =
379+map a (Deconstructor f) =
347− case f noun of
380+ Deconstructor (\noun g -> f noun a |> Maybe.map g)
354− log (prettyNoun noun)
359−prettyNoun : Noun -> String
368− else
383+fmap : (a -> b) -> Deconstructor (a -> c) c -> Deconstructor (b -> c) c
369− "@"
384+fmap f (Deconstructor g) =
370−
385+ Deconstructor (\noun h -> g noun (f >> h))
371− Cell ( lhs, rhs ) ->
373− go False lhs ++ " " ++ go True rhs
376− "[" ++ go False lhs ++ " " ++ go True rhs ++ "]"