aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/lux/macro/poly/functor.lux
blob: f5bf71c398e9c5beb4350e82544f30d091a4cde7 (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
(;module:
  lux
  (lux (control monad
                [functor])
       (data [text]
             text/format
             (coll [list "List/" Monad<List>]
                   [dict #+ Dict])
             [number]
             [product]
             [bool]
             [char]
             [maybe]
             [ident "Ident/" Codec<Text,Ident>]
             error)
       [compiler #+ Monad<Lux> with-gensyms]
       (macro [ast]
              [syntax #+ syntax: Syntax]
              (syntax [common])
              [poly #+ poly:])
       [type]
       ))

## [Derivers]
(poly: #export (Functor<?> env :x:)
  (with-gensyms [g!type-fun g!func g!input]
    (do @
      [#let [g!map (' map)]
       [g!vars _] (poly;polymorphic :x:)
       #let [num-vars (list;size g!vars)
             new-env (poly;extend-env [:x: g!type-fun]
                                      (list;zip2 (|> g!vars list;size poly;type-var-indices) g!vars)
                                      env)]
       _ (compiler;assert "Functors must have at least 1 type-variable."
                          (n.> +0 num-vars))]
      (let [->Functor (: (-> AST AST)
                         (lambda [.type.]
                           (if (n.= +1 num-vars)
                             (` (functor;Functor (~ .type.)))
                             (let [type-params (|> num-vars n.dec list;indices (List/map (|>. %n ast;local-symbol)))]
                               (` (All [(~@ type-params)] (functor;Functor ((~ .type.) (~@ type-params)))))))))
            Arg<?> (: (-> AST (poly;Matcher AST))
                      (lambda Arg<?> [value :type:]
                        ($_ compiler;either
                            ## Nothing to do.
                            (do @
                              [_ (poly;primitive :type:)]
                              (wrap value))
                            ## Type-var
                            (do @
                              [_ (poly;var new-env (|> num-vars (n.* +2) n.dec) :type:)]
                              (wrap (` ((~ g!func) (~ value)))))
                            ## Bound type-variables
                            (do @
                              [_ (poly;bound new-env :type:)]
                              (wrap value))
                            ## Tuples/records
                            (do @
                              [[g!vars members] (poly;tuple :type:)
                               pm (mapM @
                                        (lambda [:slot:]
                                          (do @
                                            [g!slot (compiler;gensym "g!slot")
                                             body (Arg<?> g!slot :slot:)]
                                            (wrap [g!slot body])))
                                        members)]
                              (wrap (` (case (~ value)
                                         [(~@ (List/map product;left pm))]
                                         [(~@ (List/map product;right pm))])
                                       )))
                            ## Recursion
                            (do @
                              [_ (poly;recur new-env :type:)]
                              (wrap (` ((~ g!map) (~ g!func) (~ value)))))
                            )))]
        ($_ compiler;either
            ## Variants
            (do @
              [[g!vars cases] (poly;variant :x:)
               pattern-matching (mapM @
                                      (lambda [[name :case:]]
                                        (do @
                                          [#let [analysis (` ((~ (ast;tag name)) (~ g!input)))]
                                           synthesis (Arg<?> g!input :case:)]
                                          (wrap (list analysis
                                                      (` ((~ (ast;tag name)) (~ synthesis)))))))
                                      cases)]
              (wrap (` (: (~ (->Functor (type;to-ast :x:)))
                          (struct (def: ((~ g!map) (~ g!func) (~ g!input))
                                    (case (~ g!input)
                                      (~@ (List/join pattern-matching)))))
                          ))))
            ## Tuples/Records
            (do @
              [[g!vars members] (poly;tuple :x:)
               pm (mapM @
                        (lambda [:slot:]
                          (do @
                            [g!slot (compiler;gensym "g!slot")
                             body (Arg<?> g!slot :slot:)]
                            (wrap [g!slot body])))
                        members)]
              (wrap (` (: (~ (->Functor (type;to-ast :x:)))
                          (struct (def: ((~ g!map) (~ g!func) (~ g!input))
                                    (case (~ g!input)
                                      [(~@ (List/map product;left pm))]
                                      [(~@ (List/map product;right pm))])))
                          ))))
            ## Functions
            (with-gensyms [g!out]
              (do @
                [[g!vars [:ins: :out:]] (poly;function :x:)
                 .out. (Arg<?> g!out :out:)
                 g!envs (seqM @
                              (list;repeat (list;size :ins:)
                                           (compiler;gensym "g!envs")))]
                (wrap (` (: (~ (->Functor (type;to-ast :x:)))
                            (struct (def: ((~ g!map) (~ g!func) (~ g!input))
                                      (lambda [(~@ g!envs)]
                                        (let [(~ g!out) ((~ g!input) (~@ g!envs))]
                                          (~ .out.))))))))))
            ## No structure (as you'd expect from Identity)
            (do @
              [_ (poly;var new-env num-vars :x:)]
              (wrap (` (: (~ (->Functor (type;to-ast :x:)))
                          (struct (def: ((~ g!map) (~ g!func) (~ g!input))
                                    ((~ g!func) (~ g!input))))))))
            ## Failure...
            (compiler;fail (format "Can't create Functor for: " (%type :x:)))
            ))
      )))