||| quantities count how many times a bound variable is used [@nuttin; @qtt]. ||| ||| i tried grtt [@grtt] for a bit but i think it was more complex than ||| it's worth in a language that has other stuff going on too module Quox.Syntax.Qty import Quox.Pretty import Quox.Decidable import Data.DPair import Derive.Prelude %default total %language ElabReflection ||| the possibilities we care about are: ||| ||| - 0: a variable is used only at compile time, not run time ||| - 1: a variable is used exactly once at run time ||| - ω (or #): don't care. an ω variable *can* also be used 0/1 time public export data Qty = Zero | One | Any %runElab derive "Qty" [Eq, Ord, Show] %name Qty.Qty pi, rh export prettyQty : {opts : _} -> Qty -> Eff Pretty (Doc opts) prettyQty Zero = hl Qty $ text "0" prettyQty One = hl Qty $ text "1" prettyQty Any = hl Qty =<< ifUnicode (text "ω") (text "#") ||| prints in a form that can be a suffix of "case" public export prettySuffix : {opts : _} -> Qty -> Eff Pretty (Doc opts) prettySuffix = prettyQty ||| e.g. if in the expression `(s, t)`, the variable `x` is ||| used π times in `s` and ρ times in `t`, then it's used ||| (π + ρ) times in the whole expression public export (+) : Qty -> Qty -> Qty Zero + rh = rh pi + Zero = pi _ + _ = Any ||| e.g. if a function `f` uses its argument π times, ||| and `f x` occurs in a σ context, then `x` is used `πσ` times overall public export (*) : Qty -> Qty -> Qty Zero * _ = Zero _ * Zero = Zero One * rh = rh pi * One = pi Any * Any = Any ||| "π ≤ ρ" ||| ||| if a variable is bound with quantity ρ, then it can be used with a total ||| quantity π as long as π ≤ ρ. for example, an ω variable can be used any ||| number of times, so π ≤ ω for any π. public export compat : Qty -> Qty -> Bool compat pi Any = True compat pi rh = pi == rh ||| "π ∨ ρ" ||| ||| returns a quantity τ with π ≤ τ and ρ ≤ τ. ||| if π = ρ, then it's that, otherwise it's ω. public export lub : Qty -> Qty -> Qty lub p q = if p == q then p else Any ||| to maintain subject reduction, only 0 or 1 can occur ||| for the subject of a typing judgment. see @qtt, §2.3 for more detail public export isSubj : Qty -> Bool isSubj Zero = True isSubj One = True isSubj Any = False public export SQty : Type SQty = Subset Qty $ So . isSubj public export %inline szero, sone : SQty szero = Element Zero Oh sone = Element One Oh ||| "σ ⨴ π" ||| ||| σ ⨭ π is 0 if either of σ or π are, otherwise it is σ. public export subjMult : SQty -> Qty -> SQty subjMult _ Zero = szero subjMult sg _ = sg ||| it doesn't make much sense for a top level declaration to have a ||| quantity of 1, so the only distinction is whether it is present ||| at runtime at all or not public export isGlobal : Qty -> Bool isGlobal Zero = True isGlobal One = False isGlobal Any = True public export GQty : Type GQty = Subset Qty $ So . isGlobal public export gzero, gany : GQty gzero = Element Zero Oh gany = Element Any Oh ||| when checking a definition, a 0 definition is checked at 0, ||| but an ω definition is checked at 1 since ω isn't a subject quantity public export %inline globalToSubj : GQty -> SQty globalToSubj (Element Zero _) = szero globalToSubj (Element Any _) = sone