diff options
Diffstat (limited to 'lux-bootstrapper/src/lux/type.clj')
-rw-r--r-- | lux-bootstrapper/src/lux/type.clj | 973 |
1 files changed, 973 insertions, 0 deletions
diff --git a/lux-bootstrapper/src/lux/type.clj b/lux-bootstrapper/src/lux/type.clj new file mode 100644 index 000000000..8853224b5 --- /dev/null +++ b/lux-bootstrapper/src/lux/type.clj @@ -0,0 +1,973 @@ +(ns lux.type + (:refer-clojure :exclude [deref apply merge bound?]) + (:require [clojure.template :refer [do-template]] + clojure.core.match + clojure.core.match.array + (lux [base :as & :refer [|do return* return assert! |let |case]]) + [lux.type.host :as &&host])) + +(declare show-type + type=) + +;; [Utils] +(defn |list? [xs] + (|case xs + (&/$Nil) + true + + (&/$Cons x xs*) + (|list? xs*) + + _ + false)) + +(def max-stack-size 256) + +(def empty-env &/$Nil) + +(def I64 (&/$Named (&/T ["lux" "I64"]) + (&/$UnivQ empty-env + (&/$Primitive "#I64" (&/|list (&/$Parameter 1)))))) +(def Nat* (&/$Primitive &&host/nat-data-tag &/$Nil)) +(def Rev* (&/$Primitive &&host/rev-data-tag &/$Nil)) +(def Int* (&/$Primitive &&host/int-data-tag &/$Nil)) + +(def Bit (&/$Named (&/T ["lux" "Bit"]) (&/$Primitive "#Bit" &/$Nil))) +(def Nat (&/$Named (&/T ["lux" "Nat"]) (&/$Apply Nat* I64))) +(def Rev (&/$Named (&/T ["lux" "Rev"]) (&/$Apply Rev* I64))) +(def Int (&/$Named (&/T ["lux" "Int"]) (&/$Apply Int* I64))) +(def Frac (&/$Named (&/T ["lux" "Frac"]) (&/$Primitive "#Frac" &/$Nil))) +(def Text (&/$Named (&/T ["lux" "Text"]) (&/$Primitive "#Text" &/$Nil))) +(def Ident (&/$Named (&/T ["lux" "Ident"]) (&/$Product Text Text))) + +(defn Array [elemT] + (&/$Primitive "#Array" (&/|list elemT))) + +(def Nothing + (&/$Named (&/T ["lux" "Nothing"]) + (&/$UnivQ empty-env + (&/$Parameter 1)))) + +(def Any + (&/$Named (&/T ["lux" "Any"]) + (&/$ExQ empty-env + (&/$Parameter 1)))) + +(def IO + (&/$Named (&/T ["lux/control/io" "IO"]) + (&/$UnivQ empty-env + (&/$Primitive "lux/type/abstract.Abstraction lux/control/io.IO" (&/|list (&/$Parameter 1)))))) + +(def List + (&/$Named (&/T ["lux" "List"]) + (&/$UnivQ empty-env + (&/$Sum + ;; lux;Nil + Any + ;; lux;Cons + (&/$Product (&/$Parameter 1) + (&/$Apply (&/$Parameter 1) + (&/$Parameter 0))))))) + +(def Maybe + (&/$Named (&/T ["lux" "Maybe"]) + (&/$UnivQ empty-env + (&/$Sum + ;; lux;None + Any + ;; lux;Some + (&/$Parameter 1)) + ))) + +(def Type + (&/$Named (&/T ["lux" "Type"]) + (let [Type (&/$Apply (&/$Parameter 1) (&/$Parameter 0)) + TypeList (&/$Apply Type List) + TypePair (&/$Product Type Type)] + (&/$Apply Nothing + (&/$UnivQ empty-env + (&/$Sum + ;; Primitive + (&/$Product Text TypeList) + (&/$Sum + ;; Sum + TypePair + (&/$Sum + ;; Product + TypePair + (&/$Sum + ;; Function + TypePair + (&/$Sum + ;; Parameter + Nat + (&/$Sum + ;; Var + Nat + (&/$Sum + ;; Ex + Nat + (&/$Sum + ;; UnivQ + (&/$Product TypeList Type) + (&/$Sum + ;; ExQ + (&/$Product TypeList Type) + (&/$Sum + ;; App + TypePair + ;; Named + (&/$Product Ident Type))))))))))) + ))))) + +(def Location + (&/$Named (&/T ["lux" "Location"]) + (&/$Product Text (&/$Product Nat Nat)))) + +(def Meta + (&/$Named (&/T ["lux" "Meta"]) + (&/$UnivQ empty-env + (&/$UnivQ empty-env + (&/$Product (&/$Parameter 3) + (&/$Parameter 1)))))) + +(def Code* + (&/$Named (&/T ["lux" "Code'"]) + (let [Code (&/$Apply (&/$Apply (&/$Parameter 1) + (&/$Parameter 0)) + (&/$Parameter 1)) + Code-List (&/$Apply Code List)] + (&/$UnivQ empty-env + (&/$Sum ;; "lux;Bit" + Bit + (&/$Sum ;; "lux;Nat" + Nat + (&/$Sum ;; "lux;Int" + Int + (&/$Sum ;; "lux;Rev" + Rev + (&/$Sum ;; "lux;Frac" + Frac + (&/$Sum ;; "lux;Text" + Text + (&/$Sum ;; "lux;Identifier" + Ident + (&/$Sum ;; "lux;Tag" + Ident + (&/$Sum ;; "lux;Form" + Code-List + (&/$Sum ;; "lux;Tuple" + Code-List + ;; "lux;Record" + (&/$Apply (&/$Product Code Code) List) + )))))))))) + )))) + +(def Code + (&/$Named (&/T ["lux" "Code"]) + (let [w (&/$Apply Location Meta)] + (&/$Apply (&/$Apply w Code*) w)))) + +(def Macro + (&/$Named (&/T ["lux" "Macro"]) + (&/$Primitive "#Macro" &/$Nil))) + +(defn bound? [id] + (fn [state] + (if-let [type (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) (&/|get id))] + (|case type + (&/$Some type*) + (return* state true) + + (&/$None) + (return* state false)) + ((&/fail-with-loc (str "[Type Error] Unknown type-var: " id)) + state)))) + +(defn deref [id] + (fn [state] + (if-let [type* (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) (&/|get id))] + (|case type* + (&/$Some type) + (return* state type) + + (&/$None) + ((&/fail-with-loc (str "[Type Error] Un-bound type-var: " id)) + state)) + ((&/fail-with-loc (str "[Type Error] Unknown type-var: " id)) + state)))) + +(defn deref+ [type] + (|case type + (&/$Var id) + (deref id) + + _ + (&/fail-with-loc (str "[Type Error] Type is not a variable: " (show-type type))) + )) + +(defn set-var [id type] + (fn [state] + (if-let [tvar (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) (&/|get id))] + (|case tvar + (&/$Some bound) + (if (type= type bound) + (return* state nil) + ((&/fail-with-loc (str "[Type Error] Cannot re-bind type var: " id " | Current type: " (show-type bound))) + state)) + + (&/$None) + (return* (&/update$ &/$type-context (fn [ts] (&/update$ &/$var-bindings #(&/|put id (&/$Some type) %) + ts)) + state) + nil)) + ((&/fail-with-loc (str "[Type Error] Unknown type-var: " id " | " (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) &/|length))) + state)))) + +(defn reset-var [id type] + (fn [state] + (if-let [tvar (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) (&/|get id))] + (return* (&/update$ &/$type-context (fn [ts] (&/update$ &/$var-bindings #(&/|put id (&/$Some type) %) + ts)) + state) + nil) + ((&/fail-with-loc (str "[Type Error] Unknown type-var: " id " | " (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) &/|length))) + state)))) + +(defn unset-var [id] + (fn [state] + (if-let [tvar (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) (&/|get id))] + (return* (&/update$ &/$type-context (fn [ts] (&/update$ &/$var-bindings #(&/|put id &/$None %) + ts)) + state) + nil) + ((&/fail-with-loc (str "[Type Error] Unknown type-var: " id " | " (->> state (&/get$ &/$type-context) (&/get$ &/$var-bindings) &/|length))) + state)))) + +;; [Exports] +;; Type vars +(def reset-mappings + (fn [state] + (return* (&/update$ &/$type-context #(->> % + (&/set$ &/$var-counter 0) + (&/set$ &/$var-bindings (&/|table))) + state) + nil))) + +(def create-var + (fn [state] + (let [id (->> state (&/get$ &/$type-context) (&/get$ &/$var-counter))] + (return* (&/update$ &/$type-context #(->> % + (&/update$ &/$var-counter inc) + (&/update$ &/$var-bindings (fn [ms] (&/|put id &/$None ms)))) + state) + id)))) + +(def existential + ;; (Lux Type) + (fn [compiler] + (return* (&/update$ &/$type-context + (fn [context] + (&/update$ &/$ex-counter inc context)) + compiler) + (->> compiler + (&/get$ &/$type-context) + (&/get$ &/$ex-counter) + &/$Ex)))) + +(defn with-var [k] + (|do [id create-var] + (k (&/$Var id)))) + +(defn clean* [?tid type] + (|case type + (&/$Var ?id) + (if (= ?tid ?id) + (|do [? (bound? ?id)] + (if ? + (deref ?id) + (return type))) + (|do [? (bound? ?id)] + (if ? + (|do [=type (deref ?id) + ==type (clean* ?tid =type)] + (|case ==type + (&/$Var =id) + (if (= ?tid =id) + (|do [_ (unset-var ?id)] + (return type)) + (|do [_ (reset-var ?id ==type)] + (return type))) + + _ + (|do [_ (reset-var ?id ==type)] + (return ==type)))) + (return type))) + ) + + (&/$Primitive ?name ?params) + (|do [=params (&/map% (partial clean* ?tid) ?params)] + (return (&/$Primitive ?name =params))) + + (&/$Function ?arg ?return) + (|do [=arg (clean* ?tid ?arg) + =return (clean* ?tid ?return)] + (return (&/$Function =arg =return))) + + (&/$Apply ?param ?lambda) + (|do [=lambda (clean* ?tid ?lambda) + =param (clean* ?tid ?param)] + (return (&/$Apply =param =lambda))) + + (&/$Product ?left ?right) + (|do [=left (clean* ?tid ?left) + =right (clean* ?tid ?right)] + (return (&/$Product =left =right))) + + (&/$Sum ?left ?right) + (|do [=left (clean* ?tid ?left) + =right (clean* ?tid ?right)] + (return (&/$Sum =left =right))) + + (&/$UnivQ ?env ?body) + (|do [=env (&/map% (partial clean* ?tid) ?env) + body* (clean* ?tid ?body)] ;; TODO: DO NOT CLEAN THE BODY + (return (&/$UnivQ =env body*))) + + (&/$ExQ ?env ?body) + (|do [=env (&/map% (partial clean* ?tid) ?env) + body* (clean* ?tid ?body)] ;; TODO: DO NOT CLEAN THE BODY + (return (&/$ExQ =env body*))) + + _ + (return type) + )) + +(defn clean [tvar type] + (|case tvar + (&/$Var ?id) + (clean* ?id type) + + _ + (&/fail-with-loc (str "[Type Error] Not type-var: " (show-type tvar))))) + +(defn ^:private unravel-fun [type] + (|case type + (&/$Function ?in ?out) + (|let [[??out ?args] (unravel-fun ?out)] + (&/T [??out (&/$Cons ?in ?args)])) + + _ + (&/T [type &/$Nil]))) + +(defn ^:private unravel-app + ([fun-type tail] + (|case fun-type + (&/$Apply ?arg ?func) + (unravel-app ?func (&/$Cons ?arg tail)) + + _ + (&/T [fun-type tail]))) + ([fun-type] + (unravel-app fun-type &/$Nil))) + +(do-template [<tag> <flatten> <at> <desc>] + (do (defn <flatten> + "(-> Type (List Type))" + [type] + (|case type + (<tag> left right) + (&/$Cons left (<flatten> right)) + + _ + (&/|list type))) + + (defn <at> + "(-> Int Type (Lux Type))" + [tag type] + (|case type + (&/$Named ?name ?type) + (<at> tag ?type) + + (<tag> ?left ?right) + (|case (&/T [tag ?right]) + [0 _] (return ?left) + [1 (<tag> ?left* _)] (return ?left*) + [1 _] (return ?right) + [_ (<tag> _ _)] (<at> (dec tag) ?right) + _ (&/fail-with-loc (str "[Type Error] " <desc> " lacks member: " tag " | " (show-type type)))) + + _ + (&/fail-with-loc (str "[Type Error] Type is not a " <desc> ": " (show-type type)))))) + + &/$Sum flatten-sum sum-at "Sum" + &/$Product flatten-prod prod-at "Product" + ) + +(do-template [<name> <ctor> <unit>] + (defn <name> + "(-> (List Type) Type)" + [types] + (|case (&/|reverse types) + (&/$Cons last prevs) + (&/fold (fn [right left] (<ctor> left right)) last prevs) + + (&/$Nil) + <unit>)) + + Variant$ &/$Sum Nothing + Tuple$ &/$Product Any + ) + +(defn show-type [^objects type] + (|case type + (&/$Primitive name params) + (|case params + (&/$Nil) + (str "(primitive " (pr-str name) ")") + + _ + (str "(primitive " (pr-str name) " " (->> params (&/|map show-type) (&/|interpose " ") (&/fold str "")) ")")) + + (&/$Product _) + (str "[" (->> (flatten-prod type) (&/|map show-type) (&/|interpose " ") (&/fold str "")) "]") + + (&/$Sum _) + (str "(| " (->> (flatten-sum type) (&/|map show-type) (&/|interpose " ") (&/fold str "")) ")") + + (&/$Function input output) + (|let [[?out ?ins] (unravel-fun type)] + (str "(-> " (->> ?ins (&/|map show-type) (&/|interpose " ") (&/fold str "")) " " (show-type ?out) ")")) + + (&/$Var id) + (str "⌈v:" id "⌋") + + (&/$Ex ?id) + (str "⟨e:" ?id "⟩") + + (&/$Parameter idx) + (str idx) + + (&/$Apply _ _) + (|let [[?call-fun ?call-args] (unravel-app type)] + (str "(" (show-type ?call-fun) " " (->> ?call-args (&/|map show-type) (&/|interpose " ") (&/fold str "")) ")")) + + (&/$UnivQ ?env ?body) + (str "(All " "{" (->> ?env (&/|map show-type) (&/|interpose " ") (&/fold str "")) "} " + (show-type ?body) ")") + + (&/$ExQ ?env ?body) + (str "(Ex " "{" (->> ?env (&/|map show-type) (&/|interpose " ") (&/fold str "")) "} " + (show-type ?body) ")") + + (&/$Named ?name ?type) + (&/ident->text ?name) + + _ + (assert false (prn-str 'show-type (&/adt->text type))))) + +(defn type= [x y] + (or (clojure.lang.Util/identical x y) + (let [output (|case [x y] + [(&/$Named [?xmodule ?xname] ?xtype) (&/$Named [?ymodule ?yname] ?ytype)] + (and (= ?xmodule ?ymodule) + (= ?xname ?yname)) + + [(&/$Primitive xname xparams) (&/$Primitive yname yparams)] + (and (.equals ^Object xname yname) + (= (&/|length xparams) (&/|length yparams)) + (&/fold2 #(and %1 (type= %2 %3)) true xparams yparams)) + + [(&/$Product xL xR) (&/$Product yL yR)] + (and (type= xL yL) + (type= xR yR)) + + [(&/$Sum xL xR) (&/$Sum yL yR)] + (and (type= xL yL) + (type= xR yR)) + + [(&/$Function xinput xoutput) (&/$Function yinput youtput)] + (and (type= xinput yinput) + (type= xoutput youtput)) + + [(&/$Var xid) (&/$Var yid)] + (= xid yid) + + [(&/$Parameter xidx) (&/$Parameter yidx)] + (= xidx yidx) + + [(&/$Ex xid) (&/$Ex yid)] + (= xid yid) + + [(&/$Apply xparam xlambda) (&/$Apply yparam ylambda)] + (and (type= xparam yparam) (type= xlambda ylambda)) + + [(&/$UnivQ xenv xbody) (&/$UnivQ yenv ybody)] + (type= xbody ybody) + + [(&/$Named ?xname ?xtype) _] + (type= ?xtype y) + + [_ (&/$Named ?yname ?ytype)] + (type= x ?ytype) + + [_ _] + false + )] + output))) + +(defn ^:private fp-get [k fixpoints] + (|let [[e a] k] + (|case fixpoints + (&/$Nil) + &/$None + + (&/$Cons [[e* a*] v*] fixpoints*) + (if (and (type= e e*) + (type= a a*)) + (&/$Some v*) + (fp-get k fixpoints*)) + ))) + +(defn ^:private fp-put [k v fixpoints] + (&/$Cons (&/T [k v]) fixpoints)) + +(defn show-type+ [type] + (|case type + (&/$Var ?id) + (fn [state] + (|case ((deref ?id) state) + (&/$Right state* bound) + (return* state (str (show-type type) " = " (show-type bound))) + + (&/$Left _) + (return* state (show-type type)))) + + _ + (return (show-type type)))) + +(defn ^:private check-error [err expected actual] + (|do [=expected (show-type+ expected) + =actual (show-type+ actual)] + (&/fail-with-loc (str (if (= "" err) err (str err "\n")) + "[Type Checker Error]\n" + "Expected: " =expected "\n\n" + " Actual: " =actual + "\n")))) + +(defn beta-reduce [env type] + (|case type + (&/$Primitive ?name ?params) + (&/$Primitive ?name (&/|map (partial beta-reduce env) ?params)) + + (&/$Sum ?left ?right) + (&/$Sum (beta-reduce env ?left) (beta-reduce env ?right)) + + (&/$Product ?left ?right) + (&/$Product (beta-reduce env ?left) (beta-reduce env ?right)) + + (&/$Apply ?type-arg ?type-fn) + (&/$Apply (beta-reduce env ?type-arg) (beta-reduce env ?type-fn)) + + (&/$UnivQ ?local-env ?local-def) + (|case ?local-env + (&/$Nil) + (&/$UnivQ env ?local-def) + + _ + type) + + (&/$ExQ ?local-env ?local-def) + (|case ?local-env + (&/$Nil) + (&/$ExQ env ?local-def) + + _ + type) + + (&/$Function ?input ?output) + (&/$Function (beta-reduce env ?input) (beta-reduce env ?output)) + + (&/$Parameter ?idx) + (|case (&/|at ?idx env) + (&/$Some parameter) + (beta-reduce env parameter) + + _ + (assert false (str "[Type Error] Unknown var: " ?idx " | " (&/->seq (&/|map show-type env))))) + + _ + type + )) + +(defn apply-type [type-fn param] + (|case type-fn + (&/$UnivQ local-env local-def) + (return (beta-reduce (->> local-env + (&/$Cons param) + (&/$Cons type-fn)) + local-def)) + + (&/$ExQ local-env local-def) + (return (beta-reduce (->> local-env + (&/$Cons param) + (&/$Cons type-fn)) + local-def)) + + (&/$Apply A F) + (|do [type-fn* (apply-type F A)] + (apply-type type-fn* param)) + + (&/$Named ?name ?type) + (apply-type ?type param) + + ;; TODO: This one must go... + (&/$Ex id) + (return (&/$Apply param type-fn)) + + (&/$Var id) + (|do [=type-fun (deref id)] + (apply-type =type-fun param)) + + _ + (&/fail-with-loc (str "[Type System] Not a type function:\n" (show-type type-fn) "\n" + "for arg: " (show-type param))))) + +(def ^:private init-fixpoints &/$Nil) + +(defn ^:private check* [fixpoints invariant?? expected actual] + (if (clojure.lang.Util/identical expected actual) + (return fixpoints) + (&/with-attempt + (|case [expected actual] + [(&/$Var ?eid) (&/$Var ?aid)] + (if (= ?eid ?aid) + (return fixpoints) + (|do [ebound (fn [state] + (|case ((deref ?eid) state) + (&/$Right state* ebound) + (return* state* (&/$Some ebound)) + + (&/$Left _) + (return* state &/$None))) + abound (fn [state] + (|case ((deref ?aid) state) + (&/$Right state* abound) + (return* state* (&/$Some abound)) + + (&/$Left _) + (return* state &/$None)))] + (|case [ebound abound] + [(&/$None _) (&/$None _)] + (|do [_ (set-var ?eid actual)] + (return fixpoints)) + + [(&/$Some etype) (&/$None _)] + (check* fixpoints invariant?? etype actual) + + [(&/$None _) (&/$Some atype)] + (check* fixpoints invariant?? expected atype) + + [(&/$Some etype) (&/$Some atype)] + (check* fixpoints invariant?? etype atype)))) + + [(&/$Var ?id) _] + (fn [state] + (|case ((set-var ?id actual) state) + (&/$Right state* _) + (return* state* fixpoints) + + (&/$Left _) + ((|do [bound (deref ?id)] + (check* fixpoints invariant?? bound actual)) + state))) + + [_ (&/$Var ?id)] + (fn [state] + (|case ((set-var ?id expected) state) + (&/$Right state* _) + (return* state* fixpoints) + + (&/$Left _) + ((|do [bound (deref ?id)] + (check* fixpoints invariant?? expected bound)) + state))) + + [(&/$Apply eA (&/$Ex eid)) (&/$Apply aA (&/$Ex aid))] + (if (= eid aid) + (check* fixpoints invariant?? eA aA) + (check-error "" expected actual)) + + [(&/$Apply A1 (&/$Var ?id)) (&/$Apply A2 F2)] + (fn [state] + (|case ((|do [F1 (deref ?id)] + (check* fixpoints invariant?? (&/$Apply A1 F1) actual)) + state) + (&/$Right state* output) + (return* state* output) + + (&/$Left _) + (|case F2 + (&/$UnivQ (&/$Cons _) _) + ((|do [actual* (apply-type F2 A2)] + (check* fixpoints invariant?? expected actual*)) + state) + + (&/$Ex _) + ((|do [fixpoints* (check* fixpoints invariant?? (&/$Var ?id) F2)] + (check* fixpoints* invariant?? A1 A2)) + state) + + _ + ((|do [fixpoints* (check* fixpoints invariant?? (&/$Var ?id) F2) + e* (apply-type F2 A1) + a* (apply-type F2 A2)] + (check* fixpoints* invariant?? e* a*)) + state)))) + + [(&/$Apply A1 F1) (&/$Apply A2 (&/$Var ?id))] + (fn [state] + (|case ((|do [F2 (deref ?id)] + (check* fixpoints invariant?? expected (&/$Apply A2 F2))) + state) + (&/$Right state* output) + (return* state* output) + + (&/$Left _) + ((|do [fixpoints* (check* fixpoints invariant?? F1 (&/$Var ?id)) + e* (apply-type F1 A1) + a* (apply-type F1 A2)] + (check* fixpoints* invariant?? e* a*)) + state))) + + [(&/$Apply A F) _] + (let [fp-pair (&/T [expected actual]) + _ (when (> (&/|length fixpoints) max-stack-size) + (&/|log! (print-str 'FIXPOINTS (->> (&/|keys fixpoints) + (&/|map (fn [pair] + (|let [[e a] pair] + (str (show-type e) ":+:" + (show-type a))))) + (&/|interpose "\n\n") + (&/fold str "")))) + (assert false (prn-str 'check* '[(&/$Apply A F) _] (&/|length fixpoints) (show-type expected) (show-type actual))))] + (|case (fp-get fp-pair fixpoints) + (&/$Some ?) + (if ? + (return fixpoints) + (check-error "" expected actual)) + + (&/$None) + (|do [expected* (apply-type F A)] + (check* (fp-put fp-pair true fixpoints) invariant?? expected* actual)))) + + [_ (&/$Apply A (&/$Ex aid))] + (check-error "" expected actual) + + [_ (&/$Apply A F)] + (|do [actual* (apply-type F A)] + (check* fixpoints invariant?? expected actual*)) + + [(&/$UnivQ _) _] + (|do [$arg existential + expected* (apply-type expected $arg)] + (check* fixpoints invariant?? expected* actual)) + + [_ (&/$UnivQ _)] + (with-var + (fn [$arg] + (|do [actual* (apply-type actual $arg) + =output (check* fixpoints invariant?? expected actual*) + _ (clean $arg expected)] + (return =output)))) + + [(&/$ExQ e!env e!def) _] + (with-var + (fn [$arg] + (|do [expected* (apply-type expected $arg) + =output (check* fixpoints invariant?? expected* actual) + _ (clean $arg actual)] + (return =output)))) + + [_ (&/$ExQ a!env a!def)] + (|do [$arg existential + actual* (apply-type actual $arg)] + (check* fixpoints invariant?? expected actual*)) + + [(&/$Primitive e!data) (&/$Primitive a!data)] + (|do [? &/jvm?] + (if ? + (|do [class-loader &/loader] + (&&host/check-host-types (partial check* fixpoints true) + check-error + fixpoints + existential + class-loader + invariant?? + e!data + a!data)) + (|let [[e!name e!params] e!data + [a!name a!params] a!data] + (if (and (= e!name a!name) + (= (&/|length e!params) (&/|length a!params))) + (|do [_ (&/map2% (partial check* fixpoints true) e!params a!params)] + (return fixpoints)) + (check-error "" expected actual))))) + + [(&/$Function eI eO) (&/$Function aI aO)] + (|do [fixpoints* (check* fixpoints invariant?? aI eI)] + (check* fixpoints* invariant?? eO aO)) + + [(&/$Product eL eR) (&/$Product aL aR)] + (|do [fixpoints* (check* fixpoints invariant?? eL aL)] + (check* fixpoints* invariant?? eR aR)) + + [(&/$Sum eL eR) (&/$Sum aL aR)] + (|do [fixpoints* (check* fixpoints invariant?? eL aL)] + (check* fixpoints* invariant?? eR aR)) + + [(&/$Ex e!id) (&/$Ex a!id)] + (if (= e!id a!id) + (return fixpoints) + (check-error "" expected actual)) + + [(&/$Named _ ?etype) _] + (check* fixpoints invariant?? ?etype actual) + + [_ (&/$Named _ ?atype)] + (check* fixpoints invariant?? expected ?atype) + + [_ _] + (&/fail "")) + (fn [err] + (check-error err expected actual))))) + +(defn check [expected actual] + (|do [_ (check* init-fixpoints false expected actual)] + (return nil))) + +(defn actual-type + "(-> Type (Lux Type))" + [type] + (|case type + (&/$Apply ?param ?all) + (|do [type* (apply-type ?all ?param)] + (actual-type type*)) + + (&/$Var id) + (|do [=type (deref id)] + (actual-type =type)) + + (&/$Named ?name ?type) + (actual-type ?type) + + _ + (return type) + )) + +(defn type-name + "(-> Type (Lux Ident))" + [type] + (|case type + (&/$Named name _) + (return name) + + _ + (&/fail-with-loc (str "[Type Error] Type is not named: " (show-type type))) + )) + +(defn unknown? + "(-> Type (Lux Bit))" + [type] + (|case type + (&/$Var id) + (|do [? (bound? id)] + (return (not ?))) + + _ + (return false))) + +(defn resolve-type + "(-> Type (Lux Type))" + [type] + (|case type + (&/$Var id) + (|do [? (bound? id)] + (if ? + (deref id) + (return type))) + + _ + (return type))) + +(defn tuple-types-for + "(-> Int Type [Int (List Type)])" + [size-members type] + (|let [?member-types (flatten-prod type) + size-types (&/|length ?member-types)] + (if (>= size-types size-members) + (&/T [size-members (&/|++ (&/|take (dec size-members) ?member-types) + (&/|list (|case (->> ?member-types (&/|drop (dec size-members)) (&/|reverse)) + (&/$Cons last prevs) + (&/fold (fn [right left] (&/$Product left right)) + last prevs))))]) + (&/T [size-types ?member-types]) + ))) + +(do-template [<name> <zero> <plus>] + (defn <name> [types] + (|case (&/|reverse types) + (&/$Nil) + <zero> + + (&/$Cons type (&/$Nil)) + type + + (&/$Cons last prevs) + (&/fold (fn [r l] (<plus> l r)) last prevs))) + + fold-prod Any &/$Product + fold-sum Nothing &/$Sum + ) + +(def create-var+ + (|do [id create-var] + (return (&/$Var id)))) + +(defn ^:private push-app [inf-type inf-var] + (|case inf-type + (&/$Apply inf-var* inf-type*) + (&/$Apply inf-var* (push-app inf-type* inf-var)) + + _ + (&/$Apply inf-var inf-type))) + +(defn ^:private push-name [name inf-type] + (|case inf-type + (&/$Apply inf-var* inf-type*) + (&/$Apply inf-var* (push-name name inf-type*)) + + _ + (&/$Named name inf-type))) + +(defn ^:private push-univq [env inf-type] + (|case inf-type + (&/$Apply inf-var* inf-type*) + (&/$Apply inf-var* (push-univq env inf-type*)) + + _ + (&/$UnivQ env inf-type))) + +(defn instantiate-inference [type] + (|case type + (&/$Named ?name ?type) + (|do [output (instantiate-inference ?type)] + (return (push-name ?name output))) + + (&/$UnivQ _aenv _abody) + (|do [inf-var create-var + output (instantiate-inference _abody)] + (return (push-univq _aenv (push-app output (&/$Var inf-var))))) + + _ + (return type))) |