Safe Haskell | None |
---|---|
Language | Haskell98 |
This module contains the core calculus for the Morte language. This language is a minimalist implementation of the calculus of constructions, which is in turn a specific kind of pure type system. If you are new to pure type systems you may wish to read "Henk: a typed intermediate language".
http://research.microsoft.com/en-us/um/people/simonpj/papers/henk.ps.gz
Morte is a strongly normalizing language, meaning that:
- Every expression has a unique normal form computed by
normalize
- You test expressions for equality of their normal forms using
==
- Equational reasoning preserves normal forms
Strong normalization comes at a price: Morte forbids recursion. Instead, you must translate all recursion to F-algebras and translate all corecursion to F-coalgebras. If you are new to F-(co)algebras then you may wish to read Morte.Tutorial or read "Recursive types for free!":
http://homepages.inf.ed.ac.uk/wadler/papers/free-rectypes/free-rectypes.txt
Morte is designed to be a super-optimizing intermediate language with a simple optimization scheme. You optimize a Morte expression by just normalizing the expression. If you normalize a long-lived program encoded as an F-coalgebra you typically get a state machine, and if you normalize a long-lived program encoded as an F-algebra you typically get an unrolled loop.
Strong normalization guarantees that all abstractions encodable in Morte are "free", meaning that they may increase your program's compile times but they will never increase your program's run time because they will normalize to the same code.
- data Var = V Text Int
- data Const
- data Path
- newtype X = X {
- absurd :: forall a. a
- data Expr a
- data Context a
- typeWith :: Context (Expr X) -> Expr X -> Either TypeError (Expr X)
- typeOf :: Expr X -> Either TypeError (Expr X)
- normalize :: Expr a -> Expr a
- shift :: Int -> Text -> Expr a -> Expr a
- subst :: Text -> Int -> Expr a -> Expr a -> Expr a
- pretty :: Buildable a => a -> Text
- buildExpr :: Buildable a => Expr a -> Builder
- buildExprASCII :: Buildable a => Expr a -> Builder
- data TypeError = TypeError {
- context :: Context (Expr X)
- current :: Expr X
- typeMessage :: TypeMessage
- data TypeMessage
- = UnboundVariable
- | InvalidInputType (Expr X)
- | InvalidOutputType (Expr X)
- | NotAFunction
- | TypeMismatch (Expr X) (Expr X)
- | Untyped Const
Syntax
Label for a bound variable
The Text
field is the variable's name (i.e. "x
").
The Int
field disambiguates variables with the same name if there are
multiple bound variables of the same name in scope. Zero refers to the
nearest bound variable and the index increases by one for each bound variable
of the same name going outward. The following diagram may help:
+-refers to-+ | | v | \(x : *) -> \(y : *) -> \(x : *) -> x@0 +-------------refers to-------------+ | | v | \(x : *) -> \(y : *) -> \(x : *) -> x@1
This Int
behaves like a De Bruijn index in the special case where all
variables have the same name.
You can optionally omit the index if it is 0
:
+refers to+ | | v | \(x : *) -> \(y : *) -> \(x : *) -> x
Zero indices are omitted when pretty-printing Var
s and non-zero indices
appear as a numeric suffix.
Constants for the calculus of constructions
The only axiom is:
⊦ * : □
... and all four rule pairs are valid:
⊦ * ↝ * : * ⊦ □ ↝ * : * ⊦ * ↝ □ : □ ⊦ □ ↝ □ : □
Path to an external resource
Syntax tree for expressions
Const Const | Const c ~ c |
Var Var | Var (V x 0) ~ x Var (V x n) ~ x@n |
Lam Text (Expr a) (Expr a) | Lam x A b ~ λ(x : A) → b |
Pi Text (Expr a) (Expr a) | Pi x A B ~ ∀(x : A) → B Pi unused A B ~ A → B |
App (Expr a) (Expr a) | App f a ~ f a |
Embed a | Embed path ~ #path |
Monad Expr Source # | |
Functor Expr Source # | |
Applicative Expr Source # | |
Foldable Expr Source # | |
Traversable Expr Source # | |
Eq a => Eq (Expr a) Source # | |
Show a => Show (Expr a) Source # | |
IsString (Expr a) Source # | |
Binary a => Binary (Expr a) Source # | |
NFData a => NFData (Expr a) Source # | |
Buildable a => Buildable (Expr a) Source # | Generates a syntactically valid Morte program |
Core functions
typeWith :: Context (Expr X) -> Expr X -> Either TypeError (Expr X) Source #
Type-check an expression and return the expression's type if type-checking suceeds or an error if type-checking fails
typeWith
does not necessarily normalize the type since full normalization
is not necessary for just type-checking. If you actually care about the
returned type then you may want to normalize
it afterwards.
normalize :: Expr a -> Expr a Source #
Reduce an expression to its normal form, performing both beta reduction and eta reduction
normalize
does not type-check the expression. You may want to type-check
expressions before normalizing them since normalization can convert an
ill-typed expression into a well-typed expression.
Utilities
shift :: Int -> Text -> Expr a -> Expr a Source #
shift n x
adds n
to the index of all free variables named x
within an
Expr
subst :: Text -> Int -> Expr a -> Expr a -> Expr a Source #
Substitute all occurrences of a variable with an expression
subst x n C B ~ B[x@n := C]
buildExpr :: Buildable a => Expr a -> Builder Source #
Pretty-print an expression as a Builder
, using Unicode symbols
buildExprASCII :: Buildable a => Expr a -> Builder Source #
Pretty-print an expression as a Builder
, using ASCII symbols
Errors
A structured type error that includes context
TypeError | |
|
data TypeMessage Source #
The specific type error