ivo/src/Ivo/Syntax/Base.hs

937 lines
36 KiB
Haskell
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- | The language's abstract syntax.
--
-- The abstract syntax is very forward-thinking
-- in that it's designed to accomodate for features
-- which haven't been implemented yet,
-- partly to make sure that I won't need to change it in a way
-- that will involve rewriting lots of other code when I do implement those features.
-- Plus, this serves as documentation for what the syntax /will/
-- be and why I designed it to be the way it is.
--
-- That means that not all of this syntax is necessarily supported by the parser,
-- and that even if it /is/, that the associated feature isn't necessarily
-- implemented by the typechecker, interpreter, or compiler.
module Ivo.Syntax.Base
( Text, NonEmpty (..)
, Scope (..), TopLevel (..), Definition (..), PublicOrPrivate (..)
, Kind, Type, Expr (..), Sig (..), Sig' (..), Lit (..)
, Pattern (..), Guard, CaseBranch (..)
, FlexibleSeparatorSyntax
) where
import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
newtype Scope = Scope [TopLevel]
deriving Show
data TopLevel
-- | Bring the definitions in a module into scope.
--
-- > open Module1
--
-- An explicit import list is also permitted:
--
-- > open Module2 ( foo ; bar ; baz )
--
-- All of the imports can be re-exported using @pub open@,
-- or individual imports may be made public
-- by individually prefixing each with @pub@.
-- These syntaxes may not be combined.
--
-- > pub open Module1 ;
-- > open Module2 ( pub foo ; bar ; pub baz ) ;
--
-- Fields can additionally be renamed or hidden:
--
-- > open Module3 using ( foo ; bar ) renaming ( foo → baz ) ;
-- > open Module4 hiding ( foo ) ;
-- > open Module5 hiding ( foo ) renaming ( bar → baz ) ;
--
-- Combining an explicit import list with a hiding list is forbidden,
-- seeing as doing so would be completely pointless.
--
-- It is also possible to open signatures, but only into other signatures.
-- When opening into a signature, all declarations are added to
-- the enclosing signature's declarations /regardless/ of
-- whether the open was marked public,
-- but default definitions are imported only as part of a @pub open@.
--
-- Note that this feature may be used as part of this idiom for extending a module:
--
-- > Λ m → sig Monoid {
-- > pub open Semigroup m ;
-- > ∅ : m ;
-- > // ... identity laws ---
-- > }
--
-- Data constructor patterns are exported
-- separately from the constructors themselves.
-- The patterns are exported under the type constructor's name;
-- the constructors are exported like functions.
-- Exporting patterns implicitly exports the constructors as well,
-- but the constructors can subsequently be hidden.
-- Here's what I mean:
--
-- > // export both constructors and patterns:
-- > open data <sig> using ( MyType ( Con1 ; Con2 ) ) ;
-- > // export just constructors, not patterns:
-- > open data <sig> using ( MyType ; Con1 ; Con2 ) ;
-- > // export just patterns, not constructors:
-- > open data <sig> using ( MyType ( Con1 ; Con2 ) ) hiding ( Con1 ; Con2 ) ;
-- > // export the patterns and data constructors, but not the type constructor:
-- > open data <sig> using ( MyType ( Con1 ; Con2 ) ) hiding ( MyType ) ;
--
-- Combined with default definitions in the signature
-- (to whom both the constructors and patterns are /always/ in scope)
-- this can be used to implement opaque data types
-- with smart constructors and/or eliminators,
-- because the type could only be constructed via exported default functions,
-- but pattern matched against as usual (or vice versa, or both).
--
-- An "import all" syntax is also supported:
--
-- // import all constructors
-- > open data <sig> using ( MyType ( .. ) ) ;
-- // hide all constructors
-- > open data <sig> hiding ( MyType ( .. ) ) ;
--
-- This uses 'FlexibleSeparatorSyntax'.
= Open PublicOrPrivate Expr [(Text, Maybe (Maybe [Text]))] -- * @using@
[(Text, Maybe (Maybe [Text]))] -- * @hiding@
[(Text, Text)] -- * @renaming@
-- | See 'Definition'.
--
-- A definition may be exported from the enclosing module or signature
-- by prefixing it with the keyword @pub@:
--
-- > pub foo : bar = baz
| Define PublicOrPrivate Definition
-- | At the top level, @data <sig>@ is syntactic sugar for @open data <sig>@.
--
-- See 'Open' and 'Data' for more information..
| TLData Expr
-- | At the top level, @axiom <sig>@ is syntactic sugar for @open axiom <sig>@.
--
-- See 'Open' and 'Axiom' for more information.
| TLAxiom Expr
-- | At the top level, @adt <...>@, @gadt <...>@, and @sig <...>@
-- are syntactic sugar for @open data adt <...>@, @open data gadt <...>@,
-- and @open data sig <...>@ respectively.
--
-- See 'Open', 'Data', and 'Sig' for more information.
| TLSig Sig
deriving Show
-- | 'Public' is written with @pub@;
-- 'Private' indicates the abscence of an access modifier.
data PublicOrPrivate = Public | Private
deriving Show
-- | Define a name as an expression, with an optional type annotation.
--
-- > foo : type = <expression> ; // with type annotation
-- > bar = <expression> ; // with inferred type
data Definition = Definition Text (Maybe Type) Expr
deriving Show
-- | An expression which represents the type of a type.
type Kind = Type
deriving Show
-- | An expression which represents the type of a value.
type Type = Expr
deriving Show
data Expr
---
--- Expressions pertaining to signatures and modules
---
-- | Automatically implement a signature by creating (co)inductive data types
-- with the given constructors/eliminators, and additionally pattern matching.
--
-- > data <signature expression>
--
-- See 'ADT', 'GADT', and 'Sig' for examples of the data types this can implement.
--
-- @data@ can be used to implement things with kind @Type → ... → Sig@
-- by turning all type arguments into signature parameters,
-- but generally speaking, the use of signature parameters should be preferred
-- see 'Sig' (the type, not the constructor).
= Data Expr
-- | Automatically "implement" a signature by taking all of its definitions as axioms.
--
-- > axiom <signature expression>
--
-- Axiom "implementations" do not have any computational meaning
-- and can only be typechecked, but not executed.
-- It may be possible to execute a program which contains axioms,
-- but the program will crash when the axioms are encountered.
--
-- Axioms are useful when writing proofs which you are not interested in executing,
-- or as placeholders for functions you haven't implemented yet.
| Axiom Expr
-- | A signature literal. See 'ADT', 'GADT', and 'Sig' for examples.
| SigE Sig
-- | A module expression with an optional signature annotation.
-- Like with signatures, modules may have type parameters.
--
-- If a module has a signature annotation,
-- then only the names in the signature will be exported
-- (and /all/ of those names will, even if they were not marked public
-- at their definition site).
-- All names exposed by the signature will be typechecked
-- against their types in the signature.
--
-- If no signature is specified, then the module will have an anonymous signature
-- based on the inferred (or specified) types for all of its public definitions.
--
-- > Λ a → mod : Monoid (List a) {
-- > ∅ = [] ;
-- > _<>_ = /* don't feel like writing this code right now */ ;
-- >
-- > monoid_id_left = ... ;
-- > ...
-- > }
--
-- Like with most scopes, it is possible to open a module within another module;
-- public imports will be re-exported if specified by the module's signature.
--
-- A public definition which is not included in the module's signature
-- will issue a warning, but not fail to compile.
-- Because definitions are exported if and only if they are part of the signature,
-- @pub@ is always redundant in a module with a type signature.
--
-- This uses 'FlexibleSeparatorSyntax'.
| Mod [Either (NonEmpty Text, Kind) Text] (Maybe Type) [TopLevel]
-- | A field accessor for signatures and modules.
--
-- A signature @sig : Sig@ which defines @foo : bar@
-- and a module implementing that signature @mod : sig@, then:
--
-- > sig::foo : sig → bar
-- > mod::foo : bar
| Access Expr Text
---
--- Expressions pertaining to types
---
-- | Universal type quantification with optional kind annotations.
--
-- With unicode syntax:
--
-- > ∀ foo ( bar : baz ) quux → type
--
-- With ASCII syntax:
--
-- > forall foo ( bar : baz ) quux -> type
--
-- Forall is constructed by 'TypeLam' and eliminated by 'TypeApp';
-- however, occurrences of 'TypeLam' and 'TypeApp'
-- will usually be inferred and do not need to be written.
--
-- 'Forall' is used for abstracting over /computationally-irrelevant/ terms;
-- the terms are usually dependent and invisible, but /may/ not be.
--
-- 'Arrow' is used for abstracting over /computationally-relevant/ terms;
-- the terms are always visible and usually independent, but /may/ be.
--
-- The distinction between these quantifiers was first made known to me
-- by Sam Lindley and Conor McBride's Hasochism paper.
| Forall (NonEmpty (Either (NonEmpty Text, Kind) Text)) Type
-- | Function types with optional argument names;
-- multiple arguments may be bound with one type.
--
-- With unicode syntax:
--
-- > ( foo bar : baz ) → quux → fwee
--
-- With ASCII syntax:
--
-- > ( foo bar : baz ) -> quux -> fwee
--
-- Arguments @foo@ and @bar@ both have type @baz@;
-- the argument with type @quux@ does not have a name;
-- @fwee@ is the function's return type.
--
-- Note that an empty list of argument names
-- corresponds with @foo → bar@, /not/ @(: foo) → bar@,
-- which is syntactically invalid.
--
-- 'Arrow' is constructed by 'Lam' and eliminated by 'App'.
--
-- 'Arrow', as a relevant quantifier, contrasts 'Forall', an irrelevant quantifier;
-- more information in the documentation for 'Forall'.
| Arrow [Text] Type Type
-- | Type annotations (or kind annotations; they are the same thing).
--
-- > foo : bar
| Ann Expr Type
-- | A hole represents a type or value to be inferred.
-- If inference fails, then the inferred type or kind
-- and detailed information about the surrounding context will be printed as an error,
-- to be used as a tool for type-driven development.
--
-- > _
| Hole
---
--- Expressions pertaining to values
---
-- | The constructor for 'Forall',
-- which explicitly binds a forall-quantified type variable
-- as a value-level variable with an optional kind annotation.
--
-- With unicode syntax:
--
-- > Λ a → foo : ∀ a → bar
-- > Λ ( a : k ) → foo : ∀ ( a : k ) → bar
--
-- With ASCII syntax:
--
-- > ^ a -> foo : forall a -> bar
-- > ^ ( a : k ) -> foo : forall ( a : k ) -> bar
--
-- The name of the variable and the type and in the value
-- do not have to be the same, and in these specific examples,
-- there is no need whatsoever to annotate the kind in both
-- the type and value; if one is specified, the other will
-- /always/ be able to be inferred.
--
-- These variables still are type variables and do not contain data;
-- they will be completely erased at runtime so the computational behavior
-- of a function cannot depend on them.
--
-- Type lambdas are usually inferred and do not need to be written down.
-- The purpose of type lambdas is to bring type variables into scope
-- for use by type annotations and type applications.
--
-- In the presence of inferred type lambdas,
-- the explicit type lambda will always bind the /innermost/ forall.
-- There are a few reasons for this:
--
-- 1. Type quantifiers can only depend on types to their left,
-- meaning that the innermost quantifiers tend to be the most complex
-- and most difficult to infer.
--
-- 2. If the kind of a quantified type is inferred to be polymorphic,
-- a new kind variable will be introduced and quantified
-- on the /left/ for the reasons described in (1).
-- These implicitly-quantified variables /cannot/
-- be bound by type lambdas or applied using type applications,
-- but even if they could be, binding quantifiers inner-to-outer
-- would prevent this transformation from affecting
-- the meaning of the expression.
--
-- 3. Module parameters may be given specific values
-- /or/ be made polymorphic, depending on how the module is used.
-- These inserted foralls are inserted (obviously) on the /left/.
| TypeLam (NonEmpty (Either (NonEmpty Text, Kind) Text)) Expr
-- | Type application, the eliminator for 'Forall'.
--
-- Suppose you have an expression @foo : forall a → bar a@,
-- but the type @a@ is ambiguous and cannot be inferred.
-- You may explicitly instantiate @foo@ with @Nat@:
--
-- > foo @ Nat : bar Nat
--
-- Type application can usually be inferred without needing to be specified.
| TypeApp Expr Type
-- | An lambda abstraction, the constructor for 'Arrow'.
--
-- With unicode syntax, if @baz : quux@:
--
-- > λ foo → baz : bar → quux
-- > λ ( foo : bar ) → baz : bar → quux
--
-- With ASCII syntax:
--
-- > λ foo -> baz
-- > λ ( foo : bar ) -> baz
--
-- A lambda expression may pattern match against its arguments:
--
-- > λ _ → foo // the argument is irrelevant
-- > λ x y → foo // multiple arguments
-- > λ ( x , y ) → foo // pattern match against a *-pair (data)
-- > λ ( π₂ → y ) → foo // project the first argument of an &-pair (codata)
--
-- As a special case, a lambda which matches an impossible pattern
-- may have no body:
--
-- > λ ! // that's really the whole thing!
--
-- Even more generally, it may branch based on the pattern match,
-- after binding any number of arguments which it doesn't branch on,
-- which will be shared across every case branch.
--
-- > λ (x , y) {
-- > S n | Left m → f n m ;
-- > Z | Right z → x z ;
-- > _ | _ → y ;
-- > }
--
-- Pattern matching is a very complex syntactic construct
-- which can do even more than just this;
-- see 'CaseBranch' for the full detail of what it can do.
--
-- Although the 'Lam' constructor allows for either
-- the list of patterns (for non-branching arguments)
-- or the list of case branches (for branching arguments)
-- to be empty, /at least one/ must consume an argument;
-- both may not be empty.
| Lam [Pattern] [CaseBranch]
-- | A lambda with only one, trivial branch, e.g. @λ x y z -> e@.
--
-- See 'Lam'.
| LamBlock [Pattern] Expr
-- | Function application, the eliminator for 'Arrow'.
-- (See also 'AppI', mixfix function application.)
--
-- Syntactically, function application is represented by juxtiposition.
--
-- If @foo : baz -> quux@ and @bar : baz@:
--
-- > foo bar : quux
| App Expr (NonEmpty Expr)
-- | Mixfix function application (see also 'App').
--
-- A mixfix operator name is composed of text
-- plus underscores where the arguments go.
-- Here, we replace the underscores with a space
-- corresponding with each argument in the list,
-- and, in the case of a partially applied mixfix operator,
-- leave the underscores for each argument /not/ applied.
--
-- Here's a fully applied example for @if_then_else_@:
--
-- > if foo then bar else baz
--
-- This is a single infix application: @AppI "if then else " [foo, bar, baz]@.
--
-- On the other hand, if we only provide the @then@ case:
--
-- > if_then bar else_
--
-- This is represented as @AppI "if_then else_" [bar]@.
--
-- If a mixfix operator is left completely unapplied, @if_then_else_@,
-- then it should be parsed as a regular variable name
-- and applied using regular function application.
| AppI Text (NonEmpty Expr)
-- | A let expression, used to define variables in a local scope.
--
-- The definitions in a let expression can include both variable definitions
-- and /opening modules/ (and consequently, even local data types).
--
-- Defining two variables:
--
-- > let foo = bar ; baz : quuz = splee in fwap
--
-- Opening a module and defining a variable:
--
-- > let open Module ; foo = bar in baz
--
-- The definitions in a let expression may be recursive, interdependent,
-- or even mutually recursive, just like top-level declarations.
-- (However, the use of these features may interfere with type inference.)
-- Variables bound in a let expression may shadow variables,
-- including those defined by /other/ let expressions,
-- but a variable can only be defined at most once in a single let expression.
--
-- This uses 'FlexibleSeparatorSyntax'.
| Let [TopLevel] Expr
-- | A case expression, used to pattern match against some variables.
--
-- Multiple expressions to be matched against are separated by bars (@|@)
-- with 'FlexibleSeparatorSyntax'.
--
-- > case foo | bar {
-- > π₁ ( Left x | _ ) → x ;
-- > π₁ ( Right _ | z ) → z ;
-- > π₂ ( Right y | _ ) → y ;
-- > π₂ ( Left _ | z ) → z ;
-- > }
--
-- A case expression with no arguments may be used just for guards:
--
-- > case {
-- > ? cond1 → x ;
-- > ? cond2 → y ;
-- > ? else → z ;
-- > }
--
-- 'Lam' may also perform case branching; lambda case syntax
-- should generally be preferred over binding a variable with lambda
-- and then immediately 'Case'-ing it.
--
-- For more information about the (very complex) syntax of case branches,
-- see 'CaseBranch'.
| Case [Expr] [CaseBranch]
-- | A case with only one, trivial branch, e.g.
-- @case x | y of a , b | c , d → e@.
--
-- See 'Case'.
| CaseBlock [Expr] [Pattern] Expr
-- | Integer, string, character, and list literals.
| Lit (Lit Expr)
-- | Variables.
| Var Text
deriving Show
-- | A signature literal.
--
-- Signatures are the type of modules.
-- They're basically record types,
-- associating a bunch of names with a bunch of types.
--
-- Signatures can be used for many things, including defining data types.
-- See 'Data', 'Axiom', and 'Mod' for a few ways they can be used.
--
-- However, there are multiple syntaxes for writing them,
-- which is (obviously) what this type enumerates.
--
-- Do not confuse type functions which return a signature (@Λ a → <sig>@),
-- signature parameters (@sig a { ... }@),
-- and type indices (@sig { Foo : a → Type }@)!
-- These three features serve entirely different purposes.
--
-- Type functions which return a signature /are not signatures per se/
-- and /must/ be instantiated with a type argument to become a signature.
-- The created signature is /not/ polymorphic over the bound type.
--
-- For example, this signature describes for any type @m@ that @m@ is a semigroup:
--
-- > Λ m → sig {
-- > _<>_ : m → m → m ;
-- > <>IsAssoc : ( x y z : m ) → ( x <> y ) <> z ≡ x <> ( y <> z ) ;
-- >
--
-- Signature parameters are universally quantified
-- across all definitions in the signature,
-- so that the signature may reference its own definitions with any type parameter,
-- and critically, the /implementation/ must be polymorphic over the
-- signature parameter as well.
--
-- The way quantifiers work is that they're added as /explicit/ parameters to kinds
-- and as /implicit/ parameters to types for all declarations and definitions
-- in the signature.
--
-- For example, this signature describes a list data type
-- which works for any type @a@, plus a @join@ operation:
--
-- > sig a {
-- > List : Type ;
-- > [] : List a ;
-- > _∷_ : List a → List a → List a ;
-- > join : List (List a) → List a ;
-- > }
--
-- Note that the @join@ operation would be illegal for @Λ a → sig { ... }@
-- because the signature's implementation doesn't have to be polymorphic in @a@,
-- but @join@ instantiates @a@ with @List a@.
--
-- It is the difference between describing a polymorphic type
-- and a family of monomorphic types which isn't necessarily universally implemented.
--
-- Finally, type indices are just the arguments to a type constructor
-- which are not quantified by @sig@ (but rather by each individual definition)
-- and thus are not necessarily parametric, e.g.:
--
-- > sig {
-- > Fin : Nat → Type ;
-- >
-- > FZ : Fin Z ;
-- > FS : ∀ n → Fin n → Fin ( S n ) ;
-- > }
data Sig
-- | Every kind of signature literal allows
-- for type parameters with optional kind signatures,
-- so those have been factored out into this constructor.
= SigQs [Either (NonEmpty Text, Kind) Text] Sig'
deriving Show
-- | A signature literal variant, excluding type parameters.
--
-- ('SigQs' is the constructor with the type parameters.)
data Sig'
-- | An algebraic data type (ADT) signature. @False@ for data, @True@ for codata.
--
-- > adt Nat { Z + S Nat }
-- > adt Stream a { Head a & Tail ( Stream a ) }
--
-- Preceding @+@s and @&@s are also permitted, which looks like this:
--
-- The syntax @+@ and @&@ were chosen because the branches of ADTs correspond with
-- additive disjunction (written @⊕@ or @+@) and additive conjunction (written @&@)
-- from linear logic.
--
-- (The arguments correspond with multiplicative conjunction (@⊗@ or @*@),
-- and multiplicative disjunction (@⅋@ or @|@) respectively;
-- however, these arguments are written using mere juxtaposition
-- instead of using an explicit syntax because it is unambiguous.)
--
-- This uses 'FlexibleSeparatorSyntax', e.g.:
--
-- > adt List a {
-- > + []
-- > + _∷_ a ( List a )
-- > }
= ADT Bool Text (Maybe Kind) [(Text, [Type])]
-- | A generalized algebraic data type (GADT) signature.
--
-- GADTs are similar to ADTs. However, thanks to their extended syntax,
-- they also may be non-parametric in their type arguments
-- (non-parametric arguments, i.e. indices, are specified in the kind signature),
-- or be used to define higher-inductive types.
--
-- `Vec a n` has a type parameter `a` and a type index `n`:
--
-- > gadt Vec a : Nat → Type {
-- > [] : Vec a Z ;
-- > _∷_ : ∀n. a → Vec a n → Vec a ( S n ) ;
--
-- `Multiset a` is a higher-inductive type with a commutative `union` constructor.
--
-- > gadt Multiset a {
-- > empty : Set a ;
-- > singleton : a → Set a ;
-- > _union_ : Set a → Set a → Set a ;
-- >
-- > // An additional constructor for Multiset's *identity* type.
-- > union_commutative : ( xs ys : Set a ) → xs union ys ≡ ys union xs;
-- > }
--
-- This uses 'FlexibleSeparatorSyntax'.
| GADT Text (Maybe Kind) [(NonEmpty Text, Type)]
-- | The most general syntax for signatures.
--
-- General signatures are simply collections of names and their types.
-- They are not necessarily the template for a /data/ type;
-- they may also be used for defining typeclasses,
-- an abstract interface for multiple implementations of a data structure,
-- a set of definitions to be taken as axiomatic, etc.
--
-- However, general signatures still /can/ be used to define data types
-- which are potentially more general than even GADTs,
-- for example mutually recursive types or inductive-inductive types.
--
-- A signature which describes the Monoid typeclass:
--
-- > sig m {
-- > empty : m ;
-- > _<>_ : m -> m -> m ;
-- >
-- > monoid_id_left : ( y : m ) → empty <> y ≡ y ;
-- > monoid_id_right : ( x : m ) → x <> empty ≡ x ;
-- > monoid_assoc : ( x y z : m ) → x <> (y <> z) ≡ (x <> y) <> z ;
-- > }
--
-- A signature which describes an axiom, the law of the excluded middle:
--
-- > sig a { lem : ¬ a + a }
--
-- The simultaneous definition of two mutually-recursive data types:
--
-- > sig {
-- > Even , Odd : Nat → Type ;
-- >
-- > zeroIsEven : Even Z ;
-- > succOddIsEven : ∀n. Odd n → Even ( S n ) ;
-- > succEvenIsOdd : ∀n. Even n → Odd ( S n ) ;
-- > }
--
-- (Multiple comma-separated declarations use 'FlexibleSeparatorSyntax'.)
--
-- An inductive-recursive pair of types:
--
-- > // An inductive-recursive pair of types:
-- > open data sig ( a : Type ) ( _#_ : a → a → Type ) {
-- > // A list in which all elements are distinct from each other
-- > // by the distinctness relation `_#_`.
-- > Dlist : Type ;
-- > // A proof that the first argument is distinct
-- > // from all of the elements of the second argument.
-- > Fresh : a → Dlist a _#_ → Type ;
-- >
-- > nil : Dlist a _#_ ;
-- > cons : ( x : a ) → ( xs : Dlist a _#_ ) → Fresh a _#_ x xs → Dlist a _#_ ;
-- >
-- > freshNil : ( x : a ) → Fresh a _#_ x nil ;
-- > freshCons : ( x : a )
-- > // the original list
-- > → ( head : a ) → ( tail : Dlist a _#_ ) → ( p : Fresh a _#_ head tail )
-- > // `x` is distinct from both the head and tail of the original list
-- > → x # head -> Fresh a _#_ x tail
-- > // thus `x` is distinct from the /entire/ original list.
-- > → Fresh a _#_ x ( cons head tail p ) ;
-- > }
--
-- Adding yet more complexity, signatures may also contain a default implementation,
-- which is a definition, rather than a type declaration.
-- Even further, the type of the default implementation may be a supertype of
-- the declared type of the field, so that the default implementation
-- may only be used if the definitions of other fields
-- allow the default implementation's type to unify with the declared type.
--
-- If the default implementation's type is identical to the declared type,
-- then it is sufficient to only include the default implementation's definition;
-- that the field is part of the signature is implied by its definition.
--
-- For typechecking purposes, the equality of signatures
-- is based only on the declared types of fields
-- (including fields implicitly declared by a definition)
-- and the definitions themselves are completely ignored;
-- for the purposes of implementing a module,
-- the default definitions will be exported as the implementations
-- of their respective declared fields in the signature unless overridden
-- by the module implementation.
--
-- Examples of default implementations:
--
-- > sig {
-- > Fwip : Type ;
-- > foo : a ; // no default implementation
-- > bar : b = x ; // `bar` defaults to `x`
-- > baz : Thing ;
-- > // `baz` defaults to `cool` only if an instance of `Cool Thing` is in scope
-- > baz : Cool Thing ⇒ Thing = cool ;
-- > }
--
-- I don't feel like including examples of `open` in signatures
-- because I'm sick of writing docs, so I hope you get the idea
-- based on my wall of text.
--
-- Finally, this is how visibility works:
--
-- * All declarations are public; @pub@ modifiers are forbidden
-- because they would be strictly redundant.
--
-- * All definitions associated with a declaration are public;
-- @pub@ modifiers are allowed but redundant.
--
-- * Definitions without a declaration are public only if marked @pub@;
-- otherwise, there will be no "inferred declaration".
| Sig [Either (NonEmpty Text, Type) TopLevel]
deriving Show
-- | A pattern matching against a data type's constructors
-- or a codata type's eliminators.
--
-- A nested pattern is composed of some depth of codata eliminators
-- containing some depth of data constructors;
-- matching codata eliminators is used to construct codata types,
-- whereas matching data constructors is used to eliminate data types.
--
-- Patterns are used primarily in the context of 'CaseBranch'es.
data Pattern
-- | Bind the matched value to a variable, with an optional type annotation.
--
-- > foo : bar
-- > foo
--
-- A variable name may be bound multiple times in the same pattern
-- if and only if the type checker can prove that both
-- uses have identicial values.
= PatVar Text (Maybe Type)
-- | An irrelevant pattern completely ignores the matched value.
--
-- > _
| Irrelevant
-- | An impossible pattern matches a value of a type which
-- the typechecker can prove is not possibly inhabited.
--
-- > !
| Impossible
-- | Match against a data constructor or codata eliminator.
-- (See also 'PatAppI'.)
--
-- > Left x
-- > π₁ xy
-- > Pair x y
| PatApp Text [Pattern]
-- | Match against a mixfix data constructor or codata eliminator.
-- (See also 'PatApp'.)
--
-- The mixfix constructor must be completely applied to patterns.
--
-- > x ∷ xs // Pattern match against a list constructor.
| PatAppI Text (NonEmpty Pattern)
-- | Apply a function to the value and then pattern match against it.
-- This may be used with e.g. copattern eliminators.
--
-- If the matched value has type @a@, then @( f : a → b ) → ( x : b )@.
--
-- > π₁ → foo // Pattern match against the left value of an &-pair.
--
-- This syntax is based on Haskell's view patterns.
| View Expr Pattern
| PatLit (Lit Pattern)
deriving Show
-- | A case branch has a left-hand side which
-- consists of some number of patterns to be matched and guards,
-- and a right-hand side consisting of an expression.
-- In an expression with multiple cases branches,
-- each left-hand side has the same type,
-- and each right-hand side has the same type (plus copattern matches).
--
-- To give a bit of an example, in:
--
-- > {
-- > pat1 | pat2 → foo ;
-- > pat3 | pat4 → bar ;
-- > }
--
-- `pat1` and `pat3` both match a value of the same type,
-- `pat2` and `pat4` both match a value of the same different type,
-- and `foo` and `bar` both have yet another different matching type.
--
-- In the case of copattern matches, e.g.
--
-- > {
-- > π₁ → foo ;
-- > π₂ → bar ;
-- > }
--
-- then @foo : a@ and @bar : b@ have different types,
-- but the return type of the expression as a whole remains the same: @a & b@.
--
-- In every context, the case branch syntax uses 'FlexibleSeparatorSyntax'.
--
-- Additionally, if there is only one case branch,
-- all of the parentheses and semicolons can usually be elided
-- in a context where block syntax is permitted:
--
-- > <pat> → <expr>
--
-- The behavior of case branches is yet more complex: see 'Refinement' and 'Guards'
-- for the additional things that case branches can do.
data CaseBranch
-- | A branch may introduce additional values to match against using __plus clauses__.
-- This allows /refining/ previous branches, for example by introducing a proof
-- so that two variables may be /unified/ by matching against the identity type,
-- or by proving that a type is uninhabited so that an impossible pattern may be used.
--
-- For example, when matching against two values @x : Nat@, @y : Nat@
-- such that @x ~ z × 2@ and @y ~ z + z@,
-- and given a function @p : (z : Nat) → z × 2 ≡ z + z@:
--
-- > {
-- > // additionally pattern match against the value `p z`
-- > x y + p z
-- > // now that a proof of equality is in-scope, we can refine our match
-- > x x | Refl → ...
-- > }
--
-- The additional patterns may be matched without modifying the original pattern;
-- in this case, the patterns list will only
-- include the patterns for newly-introduced matches,
-- and the rest of the pattern match can be assumed to be the same.
--
-- > {
-- > some complex patterns | foo ;
-- > // `...` is identical to writing `some complex patterns`;
-- > // all of the variables that it bound are still in scope.
-- > ... | pat1 → asdasd
-- > ... | pat2 → iouqwe
-- > }
--
-- This syntax is based on Agda's with clauses.
= Refinement [Pattern] [Expr] (NonEmpty CaseBranch)
-- | In addition to pattern matches, branches may be subdivided by 'Guard' clauses,
-- which are conditional expressions.
-- If no guard clause is true, then further case patterns will be tried.
-- (This is different from the behavior of Haskell, which can only match one pattern,
-- and will fail due to non-exhaustive patterns if none of the guards match.)
--
-- > {
-- > One x ? isGood x → foo ; // if `x` is good
-- > Two y ? isBad y → bar // if `y` is bad
-- > ? else → baz ; // if `y` is /not/ bad
-- > Three z ? isFun z → quux ; // if `z` is fun
-- > _ → floo ; // if `x` is not good /or/ `z` is not fun
-- > }
--
-- With regards to view patterns and guard fall-through,
-- patterns and guard clauses will always be evaluated top-to-bottom,
-- to the extent that the behavior of the program would be affected
-- by the order of evaluation for guard clauses.
--
-- This syntax is based on Haskell's guard clauses.
| Guards [Pattern] (NonEmpty (Guard, Expr))
-- | A case branch without refinement or guards.
| PlainCase [Pattern] Expr
deriving Show
-- | A guard is a kind of syntax for conditionals used as part of a 'CaseBranch'.
--
-- A guard is a sequence of plain pattern matches (/no/ plus or guard clauses)
-- against expressions /or/ expressions with a truth value (e.g. booleans)¹.
-- A guard returns true only if all patterns match and all expressions are truthy.
--
-- For example:
--
-- > ? x ← foo | y ← bar | isNice x && isNaughty y
--
-- Or as a simpler example:
--
-- > ? isCool foo
--
-- ¹: I might add a typeclass for 'truthiness' to allow using guard clause syntax.
-- Or it'll just be hardcoded for use by booleans; I'm not sure.
type Guard = [Either (Pattern, Expr) Expr]
-- | Special syntactic support for a few very important types.
data Lit r
-- | @12310@, @+1338848@, @-131@
= LitInt Integer
-- | @'a@, @'#@, @'\n@,
| LitChar Char
-- | @"And so I said, \"this language SUCKS!\""@
| LitStr Text
-- | @[ x ; y ; z ]@, @[ ]@
--
-- Note that to minimize the number of reserved characters,
-- (so that e.g. @[]@ can be a constructor name and not just syntax)
-- square brackets are /keywords/ and not /tokens/,
-- so e.g. in @[x ; y]@, @[x@ and @y]@ are /identifiers/.
-- You /must/ leave a space: @[ x ; y ]@.
--
-- This uses 'FlexibleSeparatorSyntax'.
| LitList [r]
deriving Show
-- | When the languages has things within brackets separated by a divider,
-- that divider will usually be allowed redundantly both preceding and trailing.
--
-- This allows for more flexible layouts, for example with 'CaseBranch':
--
-- > {
-- > <pat> → <expr> ;
-- > <pat> → <expr> ;
-- > }
-- >
-- > {
-- > ; <pat> → <expr>
-- > ; <pat> → <expr>
-- > }
-- >
-- > { <pat> → <expr>
-- > ; <pat> → <expr>
-- > }
--
-- The benefit of trailing or preceding dividers for multi-line things like this
-- is that it allows for uniformity, which looks good, and additionally,
-- it means that re-ordering elements is as simple as copy-pasting the
-- entire line, without having to add or remove dividers anywhere.
-- (Most programmers probably already know about this trick.)
--
-- This sort of syntax occurs in many places, e.g.:
-- 'ADT', 'GADT', 'Sig', 'Mod', 'CaseBranch', 'Use', and 'Let'.
--
-- This data type isn't actually used for anything;
-- it's just so that I can link to this explanation in Haddock.
data FlexibleSeparatorSyntax