git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Support nested record update (#2566)
1326949Liam Goodacre
examples/passing/NestedRecordUpdate.pursnew file+23
@@ -0,0 +1,23 @@
1+module Main where
2+
3+import Prelude
4+import Control.Monad.Eff.Console
5+
6+type T = { foo :: Int, bar :: { baz :: Int, qux :: Int } }
7+
8+init :: T
9+init = { foo: 1, bar: { baz: 2, qux: 3 } }
10+
11+updated :: T
12+updated = init { foo = 10, bar.baz = 20, bar.qux = 30 }
13+
14+expected :: T
15+expected = { foo: 10, bar: { baz: 20, qux: 30 } }
16+
17+check l r =
18+ l.foo == r.foo &&
19+ l.bar.baz == r.bar.baz &&
20+ l.bar.qux == r.bar.qux
21+
22+main = do
23+ when (check updated expected) $ log "Done"
examples/passing/NestedRecordUpdateWildcards.pursnew file+20
@@ -0,0 +1,20 @@
1+module Main where
2+
3+import Prelude
4+import Control.Monad.Eff.Console
5+
6+update = _ { foo = _, bar.baz = _, bar.qux = _ }
7+
8+init = { foo: 1, bar: { baz: 2, qux: 3 } }
9+
10+after = update init 10 20 30
11+
12+expected = { foo: 10, bar: { baz: 20, qux: 30 } }
13+
14+check l r =
15+ l.foo == r.foo &&
16+ l.bar.baz == r.bar.baz &&
17+ l.bar.qux == r.bar.qux
18+
19+main = do
20+ when (check after expected) $ log "Done"
src/Language/PureScript/AST/Declarations.hs+6
@@ -12,6 +12,7 @@ import Control.Monad.Identity
12import Data.Aeson.TH
12import Data.Aeson.TH
13import qualified Data.Map as M
13import qualified Data.Map as M
14import Data.Text (Text)
14import Data.Text (Text)
15+import Data.List.NonEmpty (NonEmpty(..))
15
16
16import Language.PureScript.AST.Binders
17import Language.PureScript.AST.Binders
17import Language.PureScript.AST.Literals
18import Language.PureScript.AST.Literals
@@ -583,6 +584,11 @@ data Expr
583 --
584 --
584 | ObjectUpdate Expr [(PSString, Expr)]
585 | ObjectUpdate Expr [(PSString, Expr)]
585 -- |
586 -- |
587+ -- Object updates with nested support: `x { foo.bar = e }`
588+ -- Replaced during desugaring into a `Let` and nested `ObjectUpdate`s
589+ --
590+ | ObjectUpdateNested Expr [(NonEmpty PSString, Expr)]
591+ -- |
586 -- Function introduction
592 -- Function introduction
587 --
593 --
588 | Abs (Either Ident Binder) Expr
594 | Abs (Either Ident Binder) Expr
src/Language/PureScript/AST/Traversals.hs+7
@@ -48,6 +48,7 @@ everywhereOnValues f g h = (f', g', h')
48 g' (TypeClassDictionaryConstructorApp name v) = g (TypeClassDictionaryConstructorApp name (g' v))
48 g' (TypeClassDictionaryConstructorApp name v) = g (TypeClassDictionaryConstructorApp name (g' v))
49 g' (Accessor prop v) = g (Accessor prop (g' v))
49 g' (Accessor prop v) = g (Accessor prop (g' v))
50 g' (ObjectUpdate obj vs) = g (ObjectUpdate (g' obj) (map (fmap g') vs))
50 g' (ObjectUpdate obj vs) = g (ObjectUpdate (g' obj) (map (fmap g') vs))
51+ g' (ObjectUpdateNested obj vs) = g (ObjectUpdateNested (g' obj) (map (fmap g') vs))
51 g' (Abs name v) = g (Abs name (g' v))
52 g' (Abs name v) = g (Abs name (g' v))
52 g' (App v1 v2) = g (App (g' v1) (g' v2))
53 g' (App v1 v2) = g (App (g' v1) (g' v2))
53 g' (IfThenElse v1 v2 v3) = g (IfThenElse (g' v1) (g' v2) (g' v3))
54 g' (IfThenElse v1 v2 v3) = g (IfThenElse (g' v1) (g' v2) (g' v3))
@@ -115,6 +116,7 @@ everywhereOnValuesTopDownM f g h = (f' <=< f, g' <=< g, h' <=< h)
115 g' (TypeClassDictionaryConstructorApp name v) = TypeClassDictionaryConstructorApp name <$> (g v >>= g')
116 g' (TypeClassDictionaryConstructorApp name v) = TypeClassDictionaryConstructorApp name <$> (g v >>= g')
116 g' (Accessor prop v) = Accessor prop <$> (g v >>= g')
117 g' (Accessor prop v) = Accessor prop <$> (g v >>= g')
117 g' (ObjectUpdate obj vs) = ObjectUpdate <$> (g obj >>= g') <*> traverse (sndM (g' <=< g)) vs
118 g' (ObjectUpdate obj vs) = ObjectUpdate <$> (g obj >>= g') <*> traverse (sndM (g' <=< g)) vs
119+ g' (ObjectUpdateNested obj vs) = ObjectUpdateNested <$> (g obj >>= g') <*> traverse (sndM (g' <=< g)) vs
118 g' (Abs name v) = Abs name <$> (g v >>= g')
120 g' (Abs name v) = Abs name <$> (g v >>= g')
119 g' (App v1 v2) = App <$> (g v1 >>= g') <*> (g v2 >>= g')
121 g' (App v1 v2) = App <$> (g v1 >>= g') <*> (g v2 >>= g')
120 g' (IfThenElse v1 v2 v3) = IfThenElse <$> (g v1 >>= g') <*> (g v2 >>= g') <*> (g v3 >>= g')
122 g' (IfThenElse v1 v2 v3) = IfThenElse <$> (g v1 >>= g') <*> (g v2 >>= g') <*> (g v3 >>= g')
@@ -182,6 +184,7 @@ everywhereOnValuesM f g h = (f', g', h')
182 g' (TypeClassDictionaryConstructorApp name v) = (TypeClassDictionaryConstructorApp name <$> g' v) >>= g
184 g' (TypeClassDictionaryConstructorApp name v) = (TypeClassDictionaryConstructorApp name <$> g' v) >>= g
183 g' (Accessor prop v) = (Accessor prop <$> g' v) >>= g
185 g' (Accessor prop v) = (Accessor prop <$> g' v) >>= g
184 g' (ObjectUpdate obj vs) = (ObjectUpdate <$> g' obj <*> traverse (sndM g') vs) >>= g
186 g' (ObjectUpdate obj vs) = (ObjectUpdate <$> g' obj <*> traverse (sndM g') vs) >>= g
187+ g' (ObjectUpdateNested obj vs) = (ObjectUpdateNested <$> g' obj <*> traverse (sndM g') vs) >>= g
185 g' (Abs name v) = (Abs name <$> g' v) >>= g
188 g' (Abs name v) = (Abs name <$> g' v) >>= g
186 g' (App v1 v2) = (App <$> g' v1 <*> g' v2) >>= g
189 g' (App v1 v2) = (App <$> g' v1 <*> g' v2) >>= g
187 g' (IfThenElse v1 v2 v3) = (IfThenElse <$> g' v1 <*> g' v2 <*> g' v3) >>= g
190 g' (IfThenElse v1 v2 v3) = (IfThenElse <$> g' v1 <*> g' v2 <*> g' v3) >>= g
@@ -254,6 +257,7 @@ everythingOnValues (<>) f g h i j = (f', g', h', i', j')
254 g' v@(TypeClassDictionaryConstructorApp _ v1) = g v <> g' v1
257 g' v@(TypeClassDictionaryConstructorApp _ v1) = g v <> g' v1
255 g' v@(Accessor _ v1) = g v <> g' v1
258 g' v@(Accessor _ v1) = g v <> g' v1
256 g' v@(ObjectUpdate obj vs) = foldl (<>) (g v <> g' obj) (map (g' . snd) vs)
259 g' v@(ObjectUpdate obj vs) = foldl (<>) (g v <> g' obj) (map (g' . snd) vs)
260+ g' v@(ObjectUpdateNested obj vs) = foldl (<>) (g v <> g' obj) (map (g' . snd) vs)
257 g' v@(Abs _ v1) = g v <> g' v1
261 g' v@(Abs _ v1) = g v <> g' v1
258 g' v@(App v1 v2) = g v <> g' v1 <> g' v2
262 g' v@(App v1 v2) = g v <> g' v1 <> g' v2
259 g' v@(IfThenElse v1 v2 v3) = g v <> g' v1 <> g' v2 <> g' v3
263 g' v@(IfThenElse v1 v2 v3) = g v <> g' v1 <> g' v2 <> g' v3
@@ -331,6 +335,7 @@ everythingWithContextOnValues s0 r0 (<>) f g h i j = (f'' s0, g'' s0, h'' s0, i'
331 g' s (TypeClassDictionaryConstructorApp _ v1) = g'' s v1
335 g' s (TypeClassDictionaryConstructorApp _ v1) = g'' s v1
332 g' s (Accessor _ v1) = g'' s v1
336 g' s (Accessor _ v1) = g'' s v1
333 g' s (ObjectUpdate obj vs) = foldl (<>) (g'' s obj) (map (g'' s . snd) vs)
337 g' s (ObjectUpdate obj vs) = foldl (<>) (g'' s obj) (map (g'' s . snd) vs)
338+ g' s (ObjectUpdateNested obj vs) = foldl (<>) (g'' s obj) (map (g'' s . snd) vs)
334 g' s (Abs _ v1) = g'' s v1
339 g' s (Abs _ v1) = g'' s v1
335 g' s (App v1 v2) = g'' s v1 <> g'' s v2
340 g' s (App v1 v2) = g'' s v1 <> g'' s v2
336 g' s (IfThenElse v1 v2 v3) = g'' s v1 <> g'' s v2 <> g'' s v3
341 g' s (IfThenElse v1 v2 v3) = g'' s v1 <> g'' s v2 <> g'' s v3
@@ -410,6 +415,7 @@ everywhereWithContextOnValuesM s0 f g h i j = (f'' s0, g'' s0, h'' s0, i'' s0, j
410 g' s (TypeClassDictionaryConstructorApp name v) = TypeClassDictionaryConstructorApp name <$> g'' s v
415 g' s (TypeClassDictionaryConstructorApp name v) = TypeClassDictionaryConstructorApp name <$> g'' s v
411 g' s (Accessor prop v) = Accessor prop <$> g'' s v
416 g' s (Accessor prop v) = Accessor prop <$> g'' s v
412 g' s (ObjectUpdate obj vs) = ObjectUpdate <$> g'' s obj <*> traverse (sndM (g'' s)) vs
417 g' s (ObjectUpdate obj vs) = ObjectUpdate <$> g'' s obj <*> traverse (sndM (g'' s)) vs
418+ g' s (ObjectUpdateNested obj vs) = ObjectUpdateNested <$> g'' s obj <*> traverse (sndM (g'' s)) vs
413 g' s (Abs name v) = Abs name <$> g'' s v
419 g' s (Abs name v) = Abs name <$> g'' s v
414 g' s (App v1 v2) = App <$> g'' s v1 <*> g'' s v2
420 g' s (App v1 v2) = App <$> g'' s v1 <*> g'' s v2
415 g' s (IfThenElse v1 v2 v3) = IfThenElse <$> g'' s v1 <*> g'' s v2 <*> g'' s v3
421 g' s (IfThenElse v1 v2 v3) = IfThenElse <$> g'' s v1 <*> g'' s v2 <*> g'' s v3
@@ -500,6 +506,7 @@ everythingWithScope f g h i j = (f'', g'', h'', i'', \s -> snd . j'' s)
500 g' s (TypeClassDictionaryConstructorApp _ v1) = g'' s v1
506 g' s (TypeClassDictionaryConstructorApp _ v1) = g'' s v1
501 g' s (Accessor _ v1) = g'' s v1
507 g' s (Accessor _ v1) = g'' s v1
502 g' s (ObjectUpdate obj vs) = g'' s obj <> foldMap (g'' s . snd) vs
508 g' s (ObjectUpdate obj vs) = g'' s obj <> foldMap (g'' s . snd) vs
509+ g' s (ObjectUpdateNested obj vs) = g'' s obj <> foldMap (g'' s . snd) vs
503 g' s (Abs (Left name) v1) =
510 g' s (Abs (Left name) v1) =
504 let s' = S.insert name s
511 let s' = S.insert name s
505 in g'' s' v1
512 in g'' s' v1
src/Language/PureScript/Parser/Declarations.hs+10−3
@@ -19,7 +19,10 @@ import Control.Applicative
19import Control.Arrow ((+++))
19import Control.Arrow ((+++))
20import Control.Monad.Error.Class (MonadError(..))
20import Control.Monad.Error.Class (MonadError(..))
21import Control.Parallel.Strategies (withStrategy, parList, rseq)
21import Control.Parallel.Strategies (withStrategy, parList, rseq)
22+import Data.Bifunctor (first)
22import Data.Functor (($>))
23import Data.Functor (($>))
24+import Data.List.NonEmpty (NonEmpty(..))
25+import qualified Data.List.NonEmpty as N
23import Data.Maybe (fromMaybe)
26import Data.Maybe (fromMaybe)
24import Data.Text (Text)
27import Data.Text (Text)
25import Language.PureScript.AST
28import Language.PureScript.AST
@@ -396,12 +399,13 @@ parseInfixExpr
396parseHole :: TokenParser Expr
399parseHole :: TokenParser Expr
397parseHole = Hole <$> holeLit
400parseHole = Hole <$> holeLit
398
401
399−parsePropertyUpdate :: TokenParser (PSString, Expr)
402+parsePropertyUpdate :: TokenParser (NonEmpty PSString, Expr)
400parsePropertyUpdate = do
403parsePropertyUpdate = do
401 name <- parseLabel
404 name <- parseLabel
405+ rest <- P.many (indented *> dot *> indented *> parseLabel)
402 _ <- indented *> equals
406 _ <- indented *> equals
403 value <- indented *> parseValue
407 value <- indented *> parseValue
404− return (name, value)
408+ return (name :| rest, value)
405
409
406parseAccessor :: Expr -> TokenParser Expr
410parseAccessor :: Expr -> TokenParser Expr
407parseAccessor (Constructor _) = P.unexpected "constructor"
411parseAccessor (Constructor _) = P.unexpected "constructor"
@@ -452,7 +456,10 @@ parseValue = withSourceSpan PositionedValue
452 ]
456 ]
453
457
454parseUpdaterBody :: Expr -> TokenParser Expr
458parseUpdaterBody :: Expr -> TokenParser Expr
455−parseUpdaterBody v = ObjectUpdate v <$> (indented *> braces (commaSep1 (indented *> parsePropertyUpdate)))
459+parseUpdaterBody v = objectUpdate <$> (indented *> braces (commaSep1 (indented *> parsePropertyUpdate)))
460+ where
461+ objectUpdate xs | all (null . N.tail . fst) xs = ObjectUpdate v (map (first N.head) xs)
462+ | otherwise = ObjectUpdateNested v xs
456
463
457parseAnonymousArgument :: TokenParser Expr
464parseAnonymousArgument :: TokenParser Expr
458parseAnonymousArgument = underscore *> pure AnonymousArgument
465parseAnonymousArgument = underscore *> pure AnonymousArgument
src/Language/PureScript/Pretty/Values.hs+11−1
@@ -12,6 +12,7 @@ import Prelude.Compat
12import Control.Arrow (second)
12import Control.Arrow (second)
13
13
14import qualified Data.Monoid as Monoid ((<>))
14import qualified Data.Monoid as Monoid ((<>))
15+import Data.List.NonEmpty (NonEmpty(..))
15
16
16import qualified Data.Text as T
17import qualified Data.Text as T
17import Data.Text (Text)
18import Data.Text (Text)
@@ -47,6 +48,12 @@ prettyPrintObject d = list '{' '}' prettyPrintObjectProperty
47 prettyPrintObjectProperty :: (PSString, Maybe Expr) -> Box
48 prettyPrintObjectProperty :: (PSString, Maybe Expr) -> Box
48 prettyPrintObjectProperty (key, value) = textT (prettyPrintObjectKey key Monoid.<> ": ") <> maybe (text "_") (prettyPrintValue (d - 1)) value
49 prettyPrintObjectProperty (key, value) = textT (prettyPrintObjectKey key Monoid.<> ": ") <> maybe (text "_") (prettyPrintValue (d - 1)) value
49
50
51+prettyPrintObjectUpdate :: forall k. (k -> Box) -> Int -> Expr -> [(k, Expr)] -> Box
52+prettyPrintObjectUpdate printKey d o ps =
53+ prettyPrintValueAtom (d - 1) o `beforeWithSpace` list '{' '}' printEntry ps
54+ where
55+ printEntry (key, val) = printKey key <> text " = " <> prettyPrintValue (d - 1) val
56+
50-- | Pretty-print an expression
57-- | Pretty-print an expression
51prettyPrintValue :: Int -> Expr -> Box
58prettyPrintValue :: Int -> Expr -> Box
52prettyPrintValue d _ | d < 0 = text "..."
59prettyPrintValue d _ | d < 0 = text "..."
@@ -56,7 +63,10 @@ prettyPrintValue d (IfThenElse cond th el) =
56 , text "else " <> prettyPrintValueAtom (d - 1) el
63 , text "else " <> prettyPrintValueAtom (d - 1) el
57 ])
64 ])
58prettyPrintValue d (Accessor prop val) = prettyPrintValueAtom (d - 1) val `before` textT ("." Monoid.<> prettyPrintObjectKey prop)
65prettyPrintValue d (Accessor prop val) = prettyPrintValueAtom (d - 1) val `before` textT ("." Monoid.<> prettyPrintObjectKey prop)
59−prettyPrintValue d (ObjectUpdate o ps) = prettyPrintValueAtom (d - 1) o `beforeWithSpace` list '{' '}' (\(key, val) -> textT (prettyPrintObjectKey key Monoid.<> " = ") <> prettyPrintValue (d - 1) val) ps
66+prettyPrintValue d (ObjectUpdate o ps) = prettyPrintObjectUpdate (textT . prettyPrintObjectKey) d o ps
67+prettyPrintValue d (ObjectUpdateNested o ps) = prettyPrintObjectUpdate printPath d o ps where
68+ printPath (hd :| tl) = foldl combine (textT (prettyPrintObjectKey hd)) tl
69+ combine acc key = acc <> textT ("." Monoid.<> prettyPrintObjectKey key)
60prettyPrintValue d (App val arg) = prettyPrintValueAtom (d - 1) val `beforeWithSpace` prettyPrintValueAtom (d - 1) arg
70prettyPrintValue d (App val arg) = prettyPrintValueAtom (d - 1) val `beforeWithSpace` prettyPrintValueAtom (d - 1) arg
61prettyPrintValue d (Abs (Left arg) val) = text ('\\' : T.unpack (showIdent arg) ++ " -> ") // moveRight 2 (prettyPrintValue (d - 1) val)
71prettyPrintValue d (Abs (Left arg) val) = text ('\\' : T.unpack (showIdent arg) ++ " -> ") // moveRight 2 (prettyPrintValue (d - 1) val)
62prettyPrintValue d (Abs (Right arg) val) = text ('\\' : T.unpack (prettyPrintBinder arg) ++ " -> ") // moveRight 2 (prettyPrintValue (d - 1) val)
72prettyPrintValue d (Abs (Right arg) val) = text ('\\' : T.unpack (prettyPrintBinder arg) ++ " -> ") // moveRight 2 (prettyPrintValue (d - 1) val)
src/Language/PureScript/Sugar/ObjectWildcards.hs+99−16
@@ -3,19 +3,48 @@ module Language.PureScript.Sugar.ObjectWildcards
3 , desugarDecl
3 , desugarDecl
4 ) where
4 ) where
5
5
6−import Prelude.Compat
6+import Prelude.Compat
7
7
8−import Control.Monad (forM)
8+import Control.Monad (forM, foldM)
9−import Control.Monad.Error.Class (MonadError(..))
9+import Control.Monad.Error.Class (MonadError(..))
10−import Control.Monad.Supply.Class
10+import Control.Monad.Supply.Class
11+import Data.List (partition)
12+import Data.List.NonEmpty (NonEmpty(..))
13+import Data.Map (Map)
14+import qualified Data.Map as M
15+import Data.Maybe (catMaybes)
16+import Language.PureScript.AST
17+import Language.PureScript.Environment (NameKind(..))
18+import Language.PureScript.Errors
19+import Language.PureScript.Label (Label(..))
20+import Language.PureScript.Names
21+import Language.PureScript.PSString (PSString)
11
22
12−import Data.List (partition)
23+-- `PathNode` and `PathTree` are used as an intermediate form when desugaring a nested object update.
13−import Data.Maybe (catMaybes)
24+-- For an update such as:
14−
25+--
15−import Language.PureScript.AST
26+-- x { foo = 0
16−import Language.PureScript.Errors
27+-- , bar.baz = 1
17−import Language.PureScript.Names
28+-- , bar.qux = 2 }
18−import Language.PureScript.PSString (PSString)
29+--
30+-- We represent the updates as the `PathTree`:
31+--
32+-- M.fromList [ ("foo", Leaf 3)
33+-- , ("bar", Branch (M.fromList [ ("baz", Leaf 1)
34+-- , ("qux", Leaf 2) ]) ])
35+--
36+-- Which we then convert to an expression representing the following:
37+--
38+-- let x' = x
39+-- in x' { foo = 0
40+-- , bar = x'.bar { baz = 1
41+-- , qux = 2 } }
42+--
43+-- The `let` here is required to prevent re-evaluating the object expression `x`.
44+-- However we don't generate this when using an anonymous argument for the object.
45+--
46+type PathTree = Map PSString PathNode
47+data PathNode = Leaf Expr | Branch PathTree
19
48
20desugarObjectConstructors
49desugarObjectConstructors
21 :: forall m
50 :: forall m
@@ -46,6 +75,7 @@ desugarDecl other = fn other
46 obj <- freshIdent'
75 obj <- freshIdent'
47 Abs (Left obj) <$> wrapLambda (ObjectUpdate (argToExpr obj)) ps
76 Abs (Left obj) <$> wrapLambda (ObjectUpdate (argToExpr obj)) ps
48 desugarExpr (ObjectUpdate obj ps) = wrapLambda (ObjectUpdate obj) ps
77 desugarExpr (ObjectUpdate obj ps) = wrapLambda (ObjectUpdate obj) ps
78+ desugarExpr (ObjectUpdateNested obj ps) = transformNestedUpdate obj ps
49 desugarExpr (Accessor prop u)
79 desugarExpr (Accessor prop u)
50 | Just props <- peelAnonAccessorChain u = do
80 | Just props <- peelAnonAccessorChain u = do
51 arg <- freshIdent'
81 arg <- freshIdent'
@@ -62,14 +92,67 @@ desugarDecl other = fn other
62 return $ foldr (Abs . Left) if_ (catMaybes [u', t', f'])
92 return $ foldr (Abs . Left) if_ (catMaybes [u', t', f'])
63 desugarExpr e = return e
93 desugarExpr e = return e
64
94
65− wrapLambda :: ([(PSString, Expr)] -> Expr) -> [(PSString, Expr)] -> m Expr
95+ transformNestedUpdate :: Expr -> [(NonEmpty PSString, Expr)] -> m Expr
96+ transformNestedUpdate obj ps = do
97+ -- If we don't have an anonymous argument then we need to generate a let wrapper
98+ -- so that the object expression isn't re-evaluated for each nested update.
99+ val <- freshIdent'
100+ if isAnonymousArgument obj
101+ then Abs (Left val) <$> wrapLambdaM (build val) ps
102+ else wrapLambdaM (fmap (buildLet val) . build val) ps
103+ where
104+ build val xs = buildUpdates (argToExpr val) <$> foldM buildTree M.empty xs
105+ buildLet val = Let [ValueDeclaration val Public [] (Right obj)]
106+
107+ -- Here we build up the intermediate `PathTree` data structure and check that
108+ -- the paths are valid relative to each other - for example, the update
109+ -- `{ foo.bar = 2, foo = {bar: 3} }` is invalid because there are conflicting
110+ -- paths.
111+ buildTree
112+ :: PathTree
113+ -> (NonEmpty PSString, Expr)
114+ -> m PathTree
115+ buildTree pathTree (path, e) = go pathTree path where
116+ go tree (key :| [])
117+ -- path already exists
118+ | key `M.member` tree = throwError . errorMessage $ DuplicateLabel (Label key) (Just (ObjectUpdateNested obj ps))
119+ -- create new path
120+ | otherwise = return (M.insert key (Leaf e) tree)
121+ go tree (key :| (x : xs)) = do
122+ branch <- case M.lookup key tree of
123+ -- nothing at this path yet
124+ Nothing -> return M.empty
125+ -- already a map at this path
126+ Just (Branch branch) -> return branch
127+ -- sub-path already exists
128+ Just (Leaf _) -> throwError . errorMessage $ DuplicateLabel (Label key) (Just (ObjectUpdateNested obj ps))
129+ M.insert key . Branch <$> go branch (x :| xs) <*> pure tree
130+
131+ -- Now we have a valid collection of updates in the form of a `PathTree`
132+ -- we can recursively build up the nested `ObjectUpdate` expressions.
133+ buildUpdates :: Expr -> PathTree -> Expr
134+ buildUpdates val vs = ObjectUpdate val (goLayer [] <$> M.toList vs) where
135+ goLayer :: [PSString] -> (PSString, PathNode) -> (PSString, Expr)
136+ goLayer _ (key, Leaf expr) = (key, expr)
137+ goLayer path (key, Branch branch) =
138+ let path' = path ++ [key]
139+ updates = goLayer path' <$> M.toList branch
140+ accessor = foldr Accessor val path'
141+ objectUpdate = ObjectUpdate accessor updates
142+ in (key, objectUpdate)
143+
144+ wrapLambdaM :: forall k. ([(k, Expr)] -> m Expr) -> [(k, Expr)] -> m Expr
66− wrapLambda mkVal ps =
145+ wrapLambdaM mkVal ps =
67 let (args, props) = partition (isAnonymousArgument . snd) ps
146 let (args, props) = partition (isAnonymousArgument . snd) ps
68 in if null args
147 in if null args
69− then return $ mkVal props
148+ then mkVal props
70 else do
149 else do
71 (args', ps') <- unzip <$> mapM mkProp ps
150 (args', ps') <- unzip <$> mapM mkProp ps
151+ val <- mkVal ps'
72− return $ foldr (Abs . Left) (mkVal ps') (catMaybes args')
152+ return $ foldr (Abs . Left) val (catMaybes args')
153+
154+ wrapLambda :: forall k. ([(k, Expr)] -> Expr) -> [(k, Expr)] -> m Expr
155+ wrapLambda mkVal = wrapLambdaM (return . mkVal)
73
156
74 stripPositionInfo :: Expr -> Expr
157 stripPositionInfo :: Expr -> Expr
75 stripPositionInfo (PositionedValue _ _ e) = stripPositionInfo e
158 stripPositionInfo (PositionedValue _ _ e) = stripPositionInfo e
@@ -86,7 +169,7 @@ desugarDecl other = fn other
86 isAnonymousArgument (PositionedValue _ _ e) = isAnonymousArgument e
169 isAnonymousArgument (PositionedValue _ _ e) = isAnonymousArgument e
87 isAnonymousArgument _ = False
170 isAnonymousArgument _ = False
88
171
89− mkProp :: (PSString, Expr) -> m (Maybe Ident, (PSString, Expr))
172+ mkProp :: forall k. (k, Expr) -> m (Maybe Ident, (k, Expr))
90 mkProp (name, e) = do
173 mkProp (name, e) = do
91 arg <- freshIfAnon e
174 arg <- freshIfAnon e
92 return (arg, (name, maybe e argToExpr arg))
175 return (arg, (name, maybe e argToExpr arg))