aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/abstract/monad/indexed.lux
blob: 92db5f04586a89d71cb8d0e9cfddeefdad0775db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
(.module:
  [library
   [lux #*
    [control
     [monad]
     ["p" parser
      ["s" code (#+ Parser)]]]
    [data
     [collection
      ["." list ("#\." functor fold)]]]
    ["." macro
     [syntax (#+ syntax:)]
     ["." code]]]])

(interface: #export (IxMonad m)
  (: (All [p a]
       (-> a (m p p a)))
     wrap)

  (: (All [ii it io vi vo]
       (-> (-> vi (m it io vo))
           (m ii it vi)
           (m ii io vo)))
     bind))

(type: Binding [Code Code])

(def: binding
  (Parser Binding)
  (p.and s.any s.any))

(type: Context
  (#Let (List Binding))
  (#Bind Binding))

(def: context
  (Parser Context)
  (p.or (p.after (s.this! (' #let))
                 (s.tuple (p.some binding)))
        binding))

(def: (pair_list [binding value])
  (All [a] (-> [a a] (List a)))
  (list binding value))

(def: named_monad
  (Parser [(Maybe Text) Code])
  (p.either (s.record (p.and (\ p.monad map (|>> #.Some)
                                s.local_identifier)
                             s.any))
            (\ p.monad map (|>> [#.None])
               s.any)))

(syntax: #export (do {[?name monad] ..named_monad}
                   {context (s.tuple (p.some context))}
                   expression)
  (macro.with_gensyms [g!_ g!bind]
    (let [body (list\fold (function (_ context next)
                            (case context
                              (#Let bindings)
                              (` (let [(~+ (|> bindings
                                               (list\map pair_list)
                                               list.concat))]
                                   (~ next)))
                              
                              (#Bind [binding value])
                              (` ((~ g!bind)
                                  (.function ((~ g!_) (~ binding))
                                    (~ next))
                                  (~ value)))))
                          expression
                          (list.reverse context))]
      (wrap (list (case ?name
                    (#.Some name)
                    (let [name (code.local_identifier name)]
                      (` (let [(~ name) (~ monad)
                               {#..wrap (~' wrap)
                                #..bind (~ g!bind)} (~ name)]
                           (~ body))))

                    #.None
                    (` (let [{#..wrap (~' wrap)
                              #..bind (~ g!bind)} (~ monad)]
                         (~ body)))))))))