Welcome to the GEB project.
Here is the official repository
and HTML documentation for the latest version.
Maintainers: please read the maintainers guide
For test coverage it can be found at the following links:
CCL test coverage: current under maintenance
Note that due to #34 CCL tests are not currently displaying
I recommend reading the CCL code coverage version, as it has proper tags.
Currently they are manually generated, and thus for a more accurate assessment see GEB-TEST:CODE-COVERAGE
Welcome to the GEB Project!
This project uses common lisp, so a few dependencies are needed to get around the code-base and start hacking. Namely:
Emacs along with one of the following:
Now that we have an environment setup, we can load the project, this can be done in a few steps.
Open the REPL
(sbcl (terminal), M-x
sly, M-x
swank)
For the terminal, this is just calling the common lisp implementation from the terminal.
user@system:geb-directory % sbcl
.
For Emacs, this is simply calling either M-x sly
or M-x slime
if you are using either sly or slime
From Emacs: open geb.asd
and press C-ck
(sly-compile-and-load-file
, or
swank-compile-and-load-file
if you are using swank).
Now that we have the file open, we can now load the system by writing:
;; only necessary for the first time!
(ql:quickload :geb/documentation)
;; if you want to load it in the future
(asdf:load-system :geb/documentation)
;; if you want to load the codbase and run tests at the same time
(asdf:test-system :geb/documentation)
;; if you want to run the tests once the system is loaded!
(geb-test:run-tests)
The standard way to use geb currently is by loading the code into one's lisp environment
(ql:quickload :geb)
However, one may be interested in running geb in some sort of compilation process, that is why we also give out a binary for people to use
An example use of this binary is as follows
mariari@Gensokyo % ./geb.image -i "foo.lisp" -e "geb.lambda.main::*entry*" -l -p -o "foo.pir"
mariari@Gensokyo % cat foo.pir
def entry x1 = {
(x1)
};%
mariari@Gensokyo % ./geb.image -i "foo.lisp" -e "geb.lambda.main::*entry*" -l -p
def *entry* x {
0
}
mariari@Gensokyo % ./geb.image -h
-i --input string Input geb file location
-e --entry-point string The function to run, should be fully qualified I.E. geb::my-main
-l --stlc boolean Use the simply typed lambda calculus frontend
-o --output string Save the output to a file rather than printing
-v --version boolean Prints the current version of the compiler
-p --vampir string Return a vamp-ir expression
-s --library boolean Print standard library
-h -? --help boolean The current help message
mariari@Gensokyo % ./geb.image -v
0.3.2
starting from a file foo.lisp that has
any valid lambda form. Good examples can be found at the following section:
The Simply Typed Lambda Calculus model
with the term bound to some global variable
(in-package :geb.lambda.main)
(defparameter *entry*
(lamb (list (coprod so1 so1))
(index 0)))
inside of it.
The command needs an entry-point (-e or --entry-point), as we are
simply call LOAD
on the given file, and need to know what to
translate.
from STLC
, we expect the form to be wrapped in the
GEB.LAMBDA.SPEC.TYPED which takes both the type and the value to
properly have enough context to evaluate.
It is advised to bind this to a parameter like in our example as -e expects a symbol.
the -l flag means that we are not expecting a geb term, but rather a lambda frontend term, this is to simply notify us to compile it as a lambda term rather than a geb term. In time this will go away
The flag -s prints the standard library the compiler uses. If -p is used alongside it, the standard library gets printed before the compiled circuit.
The flag -t after -p signals that the user wants to make an automatically generated test equality. Given a compiled VampIR function with name foo and arguments x1...xn prints an equality as foo x1 ... xn = y
[glossary-term] closed type
A closed type is a type that can not be extended dynamically. A good example of this kind of term is an ML ADT.
data Tree = Empty
| Leaf Int
| Node Tree Tree
In our lisp code we have a very similar convention:
(in-package :geb.spec)
(deftype substmorph ()
`(or substobj
alias
comp init terminal case pair distribute
inject-left inject-right
project-left project-right))
This type is closed, as only one of GEB:SUBSTOBJ
, GEB:INJECT-LEFT
,
GEB:INJECT-RIGHT
etc can form the GEB:SUBSTMORPH
type.
The main benefit of this form is that we can be exhaustive over what
can be found in GEB:SUBSTMORPH
.
(defun so-hom-obj (x z)
(match-of substobj x
(so0 so1)
(so1 z)
(alias (so-hom-obj (obj x) z))
((coprod x y) (prod (so-hom-obj x z)
(so-hom-obj y z)))
((prod x y) (so-hom-obj x (so-hom-obj y z)))))
If we forget a case, like GEB:COPROD
it wanrs us with an non exhaustion warning.
Meaning that if we update definitions this works well.
The main downside is that we can not extend the type after the fact, meaning that all interfaces on SO-HOM-OBJ must take the unaltered type. This is in stark contrast to open types. To find out more about the trade offs and usage in the code-base read the section Open Types versus Closed Types.
[glossary-term] open type
An open type is a type that can be extended by user code down the line. A good example of this in ML is the type class system found in Haskell.
In our code base, it is simple as creating a Common Lisp Object System (CLOS) term
(defclass <substobj> (direct-pointwise-mixin) ())
and to create a child of it all we need to do is.
(defclass so0 (<substobj>) ())
Now any methods on GEB:<SUBSTOBJ>
will cover GEB:SO0
(0
1
).
The main disadvantage of these is that exhaustion can not be checked, and thus the user has to know what methods to fill out. In a system with a bit more checks this is not a problem in practice. To find out more about the trade offs and usage in the code-base read the section Open Types versus Closed Types.
[glossary-term] Common Lisp Object System (CLOS)
The object system found in CL. Has great features like a Meta Object Protocol that helps it facilitate extensions.
Originally GEB started off as an Idris codebase written by the designer and creator of GEB, Terence Rokop, However further efforts spawned for even further formal verification by Artem Gureev. Due to this, we have plenty of code not in Common Lisp that ought to be a good read.
The Idris folder can be found in the geb-idris folder provided in the codebase
At the time of this document, there is over 16k lines of Idris code written. This serves as the bulk of the POC that is GEB and is a treasure trove of interesting information surrounding category theory.
The Agda folder can be found in the geb-agda folder provided in the codebase
The Agda codebase serves as a great place to view formally verified properties about the GEB project. Although Geb's Idris Code is written in a dependently typed language, it serves as reference example of GEB, while Geb's Agda Code serves as the mathematical formalism proving various conjectures about GEB
Geb is organizing programming language concepts (and entities!) using category theory, originally developed by mathematicians, but very much alive in programming language theory. Let us look at a simple well-known example: the category of sets and functions. It is the bread and butter example: sets $A,B,C,…$ play the role of objects, functions are arrows between objects $A—f→B$, and the latter compose as functions do, such that every path of matching functions $$A—f→B—g→C—h→D$$ composes to a corresponding composite function $$A—f;g;h→D$$ (or $h∘g∘f$ if you prefer) and we enjoy the luxury of not having to worry about the order in which we compose; for the sake of completeness, there are identify functions $A —\mathrm{id}_A→ A$ on each set $A$, serving as identities (which correspond to the composite of the empty path on an object). Sets and functions together form a category—based on function composition; thus, let's call this category sets-'n'-functions. This example, even “restricted” to finite sets-'n'-functions, will permeate through Geb.
One of the first lessons (in any introduction to category theory) about sets-'n'-functions is the characterization of products and disjoint sums of sets in terms of functions alone, i.e., without ever talking about elements of sets. Products and co-products are the simplest examples of universal constructions. One of the first surprises follows suit when we generalize functions to partial functions, relations, or even multi-relations: we obtain very different categories! For example, in the category sets-'n'-relations, the disjoint union of sets features as both a product and a co-product, as a categorical construction.
Do not fear! The usual definition of products in terms of elements of sets are absolutely compatible with the universal construction in sets-'n'-functions. However we gain the possibility to compare the “result” of the universal constructions in sets-'n'-functions with the one in sets-'n'-relations (as both actually do have products).
for the purposes of Geb, many things can be expressed in analogy to the category of sets-'n'-functions; thus a solid understanding of the latter will be quite useful. In particular, we shall rely on the following universal constructions:
The construction of binary products $A × B$ of sets $A,B$, and the empty product $mathsf{1}$.
The construction of “function spaces” $B^A$ of sets $A,B$, called exponentials, i.e., collections of functions between pairs of sets.
The so-called currying of functions, $C^{(B^A)} cong C^{(A × B)}$, such that providing several arguments to a function can done either simultaneously, or in sequence.
The construction of sums (a.k.a. co-products) $A + B$ of sets $A,B$, corresponding to forming disjoint unions of sets; the empty sum is $varnothing$.
Product, sums and exponentials are the (almost) complete tool chest for writing polynomial expressions, e.g., $$Ax^{sf 2} +x^{sf 1} - Dx^{sf 0}.$$ (We need these later to define “algebraic data types”.) In the above expression, we have sets instead of numbers/constants where $ mathsf{2} = lbrace 1, 2 rbrace$, $ mathsf{1} = lbrace 1 rbrace$, $ mathsf{0} = lbrace rbrace = varnothing$, and $A$ and $B$ are arbitrary (finite) sets. We are only missing a counterpart for the variable! Raising an arbitrary set to “the power” of a constant set happens to have a very natural counterpart: the central actor of the most-well known fundamental result about categories, which generalizes Cayley's Theorem, i.e., the Yoneda embedding.
If you are familiar with the latter, buckle up and jump to Poly in Sets. Have a look at our streamlined account of The Yoneda Lemma if you are familiar with Cartesian closed categories, or take it slow and read up on the background in one of the classic or popular textbooks. Tastes tend to vary. However, Benjamin Pierce's Basic Category Theory for Computer Scientists deserves being pointed out as it is very amenable and covers the background we need in 60 short pages.
The Geb Project is written in Common Lisp, which means the authors have a great choice in freedom in how the project is laid out and operates. In particular the style of Common Lisp here is a functional style with some OO idioms in the style of Smalltalk.
The subsections will outline many idioms that can be found throughout the codebase.
The codebase is split between many files. Each folder can be seen as
a different idea within geb itself! Thus the poly
has packages
revolving around polynomials, the geb
folder has packages regarding
the main types of geb Subst Obj and
Subst Morph, etc etc.
The general layout quirk of the codebase is that packages like
geb.package.spec
defines the specification for the base types for
any category we wish to model, and these reside in the specs
folder
not in the folder that talks about the packages of those types. This
is due to loading order issues, we thus load the specs
packages
before each of their surrounding packages, so that each package can
built off the last. Further, packages like geb.package.main
define
out most of the functionality of the package to be used by other
packages in geb.package
, then all of these are reexported out in the
geb.package
package
Further to make working with each package of an idea is easy, we have the main package of the folder (typically named the same as the folder name) reexport most important components so if one wants to work with the fully fledged versions of the package they can simply without having to import too many packages at once.
For example, the geb.poly.spec
defines out the types and data
structures of the Polynomial Types, this is then rexported
in geb.poly
, giving the module geb.poly
a convenient interface for
all functions that operate on geb.poly
.
closed type's and open type's both have their perspective tradeoff of openness versus exhaustiveness (see the linked articles for more on that). Due to this, they both have their own favorable applications. I would argue that a closed ADT type is great tool for looking at a function mathematically and treating the object as a whole rather than piecemeal. Whereas a more open extension is great for thinking about how a particular object/case behaves. They are different mindsets for different styles of code.
In the geb project, we have chosen to accept both styles, and allow both to coexist in the same setting. We have done this with a two part idiom.
(deftype substobj ()
`(or alias prod coprod so0 so1))
(defclass <substobj> (direct-pointwise-mixin) ())
(defclass so0 (<substobj>) ...)
(defclass prod (<substobj>) ...)
The closed type is GEB:SUBSTOBJ
, filling and defining every structure
it knows about. This is a fixed idea that a programmer may statically
update and get exhaustive warnings about. Whereas GEB:<SUBSTOBJ>
is
the open interface for the type. Thus we can view [GEB:<SUBSTOBJ>
] as
the general idea of a [GEB:SUBSTOBJ
]. Before delving into how we combine
these methods, let us look at two other benefits given by [GEB:<SUBSTOBJ>
]
We can put all the Mixins into the superclass to enforce that any type that extends it has the extended behaviors we wish. This is a great way to generically enhance the capabilities of the type without operating on it directly.
We can dispatch on GEB:<SUBSTOBJ>
since DEFMETHOD
only works on
Common Lisp Object System (CLOS) types and not generic types in CL.
With these pieces in play let us explore how we write a method in a way that is conducive to open and closed code.
(in-package :geb)
(defgeneric to-poly (morphism))
(defmethod to-poly ((obj <substmorph>))
(typecase-of substmorph obj
(alias ...)
(substobj (error "Impossible")
(init 0)
(terminal 0)
(inject-left poly:ident)
(inject-right ...)
(comp ...)
(case ...)
(pair ...)
(project-right ...)
(project-left ...)
(distribute ...)
(otherwise (subclass-responsibility obj))))
(defmethod to-poly ((obj <substobj>))
(declare (ignore obj))
poly:ident)
In this piece of code we can notice a few things:
We case on [GEB:SUBSTMORPH
] exhaustively
We cannot hit the [GEB:<SUBSTOBJ>
] case due to method dispatch
We have this [GEB.UTILS:SUBCLASS-RESPONSIBILITY
] function getting called.
We can write further methods extending the function to other subtypes.
Thus the [GEB.COMMON:TO-POLY
] function is written in such a way that it
supports a closed definition and open extensions, with
[GEB.UTILS:SUBCLASS-RESPONSIBILITY
] serving to be called if an
extension a user wrote has no handling of this method.
Code can also be naturally written in a more open way as well, by simply running methods on each class instead.
One nasty drawback is that we can't guarantee the method exists. In java this can easily be done with interfaces and then enforcing they are fulfilled. Sadly CL has no such equivalent. However, this is all easily implementable. If this ever becomes a major problem, it is trivial to implement this by registering the subclasses, and the perspective methods, and scouring the image for instance methods, and computing if any parent class that isn't the one calling responsibility fulfills it. Thus, in practice, you should be able to ask the system if any particular extension fulfills what extension sets that the base object has and give CI errors if they are not fulfilled, thus enforcing closed behavior when warranted.
These refer to the open type variant to a closed type. Thus when
one sees a type like GEB:GEB:SUBSTOBJ
. Read Open Types versus Closed Types for information on how to use them.
Everything here relates directly to the underlying machinery of GEB, or to abstractions that help extend it.
This covers the main Categorical interface required to be used and contained in various data structures
[class] CAT-OBJ
I offer the service of being a base category objects with no extensions
[class] CAT-MORPH
I offer the service of being a base categorical morphism with no extensions
[generic-function] DOM X
Grabs the domain of the morphism. Returns a CAT-OBJ
[generic-function] CODOM X
Grabs the codomain of the morphism. Returns a CAT-OBJ
[generic-function] CURRY-PROD CAT-MORPH CAT-LEFT CAT-RIGHT
Curries the given product type given the
product. This returns a CAT-MORPH
.
This interface version takes the left and right product type to
properly dispatch on. Instances should specialize on the CAT-RIGHT
argument
Use GEB.MAIN:CURRY
instead.
These functions represent the generic functions that can be run on many parts of the compiler, they are typically rexported on any package that implements the given generic function.
You can view their documentation in their respective API sections.
The main documentation for the functionality is given here, with examples often given in the specific methods
[generic-function] GAPPLY MORPHISM OBJECT
Applies a given Moprhism to a given object.
This is practically a naive interpreter for any category found throughout the codebase.
Some example usages of GAPPLY
are.
GEB> (gapply (comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
(left so1))
(right s-1)
GEB> (gapply (comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
(right so1))
(left s-1)
[generic-function] WELL-DEFP-CAT MORPHISM
Given a moprhism of a category, checks that it is well-defined. E.g. that composition of morphism is well-defined by checking that the domain of MCAR corresponds to the codomain of MCADR
[generic-function] MAYBE OBJECT
Wraps the given OBJECT
into a Maybe monad The Maybe monad in this
case is simply wrapping the term in a coprod
of so1(0
1
)
[generic-function] SO-HOM-OBJ OBJECT1 OBJECT2
Takes in X and Y Geb objects and provides an internal hom-object (so-hom-obj X Y) representing a set of functions from X to Y
[generic-function] SO-EVAL OBJECT1 OBJECT2
Takes in X and Y Geb objects and provides an evaluation morphism (prod (so-hom-obj X Y) X) -> Y
[generic-function] WIDTH OBJECT
Given an OBJECT
of Geb presents it as a SeqN object. That is,
width corresponds the object part of the to-seqn functor.
[generic-function] TO-CIRCUIT MORPHISM NAME
Turns a MORPHISM
into a Vampir circuit. the NAME
is the given name of
the output circuit.
[generic-function] TO-BITC MORPHISM
Turns a given MORPHISM
into a [GEB.BITC.SPEC:BITC
]
[generic-function] TO-SEQN MORPHISM
Turns a given MORPHISM
into a [GEB.SEQN.SPEC:SEQN
]
[generic-function] TO-POLY MORPHISM
Turns a given MORPHISM
into a [GEB.POLY.SPEC:POLY
]
[generic-function] TO-CAT CONTEXT TERM
Turns a MORPHISM
with a context into Geb's Core category
[generic-function] TO-VAMPIR MORPHISM VALUES CONSTRAINTS
Turns a MORPHISM
into a Vampir circuit, with concrete values.
The more natural interface is [TO-CIRCUIT
], however this is a more low
level interface into what the polynomial categories actually
implement, and thus can be extended or changed.
The VALUES
are likely vampir values in a list.
The CONSTRAINTS
represent constraints that get creating
The underlying category of GEB. With Subst Obj covering the shapes and forms (GEB-DOCS/DOCS:@OBJECTS) of data while Subst Morph deals with concrete GEB-DOCS/DOCS:@MORPHISMS within the category.
From this category, most abstractions will be made, with
SUBSTOBJ
serving as a concrete type layout, with
SUBSTMORPH
serving as the morphisms between different
SUBSTOBJ
types. This category is equivalent to
finset.
A good example of this category at work can be found within the Booleans section.
This section covers the objects of the SUBSTMORPH
category. Note that SUBSTOBJ
refers to the
[closed type], whereas <SUBSTOBJ>
refers
to the [open type] that allows for user extension.
[class] \
the class corresponding to SUBSTOBJ
. See GEB-DOCS/DOCS:@OPEN-CLOSED
SUBSTOBJ
type is not a constructor itself, instead it's
best viewed as the sum type, with the types below forming the
constructors for the term. In ML we would write it similarly to:
type substobj = so0
| so1
| prod
| coprod
[class] PROD \
The PRODUCT object. Takes two CAT-OBJ
values that
get put into a pair.
The formal grammar of PRODUCT is
(prod mcar mcadr)
where PROD
is the constructor, [MCAR
] is the left value of the
product, and [MCADR
] is the right value of the product.
Example:
(geb-gui::visualize (prod geb-bool:bool geb-bool:bool))
Here we create a product of two [GEB-BOOL:BOOL
] types.
[class] COPROD \
the CO-PRODUCT object. Takes CAT-OBJ
values that
get put into a choice of either value.
The formal grammar of PRODUCT is
(coprod mcar mcadr)
Where CORPOD is the constructor, [MCAR
] is the left choice of
the sum, and [MCADR
] is the right choice of the sum.
Example:
(geb-gui::visualize (coprod so1 so1))
Here we create the boolean type, having a choice between two unit values.
[class] SO0 \
The Initial Object. This is sometimes known as the VOID type.
the formal grammar of SO0
is
so0
where SO0
is THE
initial object.
Example
lisp
[class] SO1 \
The Terminal Object. This is sometimes referred to as the Unit type.
the formal grammar or SO1
is
so1
where SO1
is THE
terminal object
Example
(coprod so1 so1)
Here we construct [GEB-BOOL:BOOL
] by simply stating that we have the
terminal object on either side, giving us two possible ways to fill
the type.
(->left so1 so1)
(->right so1 so1)
where applying [->LEFT
] gives us the left unit, while [->RIGHT
] gives
us the right unit.
The Accessors specific to Subst Obj
The overarching types that categorizes the SUBSTMORPH
category. Note that SUBSTMORPH
refers to the
[closed type], whereas <SUBSTMORPH>
refers
to the [open type] that allows for user extension.
[type] SUBSTMORPH
The morphisms of the SUBSTMORPH
category
[class] \
the class type corresponding to SUBSTMORPH
. See GEB-DOCS/DOCS:@OPEN-CLOSED
SUBSTMORPH
type is not a constructor itself, instead it's
best viewed as the sum type, with the types below forming the
constructors for the term. In ML we would write it similarly to:
type substmorph = comp
| substobj
| case
| init
| terminal
| pair
| distribute
| inject-left
| inject-right
| project-left
| project-right
Note that an instance of SUBSTOBJ
, acts like the identity
morphism to the layout specified by the given SUBSTOBJ
. Thus
we can view this as automatically lifting a SUBSTOBJ
into a
SUBSTMORPH
[class] COMP \
The composition morphism. Takes two CAT-MORPH
values that get
applied in standard composition order.
The formal grammar of COMP
is
(comp mcar mcadr)
which may be more familiar as
g 。f
Where COMP
( 。) is the constructor, [MCAR
](g) is the second morphism
that gets applied, and [MCADR
](f) is the first morphism that gets
applied.
Example:
(geb-gui::visualize
(comp
(<-right so1 geb-bool:bool)
(pair (<-left so1 geb-bool:bool)
(<-right so1 geb-bool:bool))))
In this example we are composing two morphisms. the first morphism
that gets applied ([PAIR
] ...) is the identity function on the
type (PROD
SO1
[GEB-BOOL:BOOL
]), where we pair the
left projection and the right
projection, followed by taking the right
projection of the type.
Since we know (COMP
f id) is just f per the laws of category
theory, this expression just reduces to
(<-right so1 geb-bool:bool)
[class] CASE \
Eliminates coproducts. Namely Takes two CAT-MORPH
values, one
gets applied on the left coproduct while the other gets applied on the
right coproduct. The result of each CAT-MORPH
values must be
the same.
The formal grammar of CASE
is:
(mcase mcar mcadr)
Where [MCASE
] is the constructor, [MCAR
] is the morphism that gets
applied to the left coproduct, and [MCADR
] is the morphism that gets
applied to the right coproduct.
Example:
(comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
In the second example, we inject a term with the shape [GEB-BOOL:BOOL
]
into a pair with the shape (SO1
× [GEB-BOOL:BOOL
]), then we use
[MCASE
] to denote a morphism saying. IF
the input is of the shape [SO1
],
then give us True, otherwise flip the value of the boolean coming in.
[class] INIT \
The INITIAL Morphism, takes any [CAT-OBJ
] and
creates a moprhism from SO0
(also known as void) to the object given.
The formal grammar of INITIAL is
(init obj)
where INIT
is the constructor. [OBJ
] is the type of object
that will be conjured up from SO0
, when the morphism is
applied onto an object.
Example:
(init so1)
In this example we are creating a unit value out of void.
[class] TERMINAL \
The TERMINAL
morphism, Takes any [CAT-OBJ
] and creates a
morphism from that object to SO1
(also known as unit).
The formal grammar of TERMINAL
is
(terminal obj)
where TERMINAL
is the constructor. [OBJ
] is the type of object that
will be mapped to SO1
, when the morphism is applied onto an
object.
Example:
(terminal (coprod so1 so1))
(geb-gui::visualize (terminal (coprod so1 so1)))
(comp value (terminal (codomain value)))
(comp true (terminal bool))
In the first example, we make a morphism from the corpoduct of
SO1
and SO1
(essentially [GEB-BOOL:BOOL
]) to
SO1
.
In the third example we can proclaim a constant function by ignoring the input value and returning a morphism from unit to the desired type.
The fourth example is taking a [GEB-BOOL:BOOL
] and returning [GEB-BOOL:TRUE
].
[class] PAIR \
Introduces products. Namely Takes two CAT-MORPH
values. When
the PAIR
morphism is applied on data, these two [CAT-MORPH
]'s are
applied to the object, returning a pair of the results
The formal grammar of constructing an instance of pair is:
(pair mcar mcdr)
where PAIR
is the constructor, MCAR
is the left morphism, and MCDR
is
the right morphism
Example:
(pair (<-left so1 geb-bool:bool)
(<-right so1 geb-bool:bool))
(geb-gui::visualize (pair (<-left so1 geb-bool:bool)
(<-right so1 geb-bool:bool)))
Here this pair morphism takes the pair SO1
(0
1
) × GEB-BOOL:BOOL
, and
projects back the left field SO1
as the first value of the pair and
projects back the GEB-BOOL:BOOL
field as the second values.
[class] DISTRIBUTE \
The distributive law
[class] INJECT-LEFT \
The left injection morphism. Takes two CAT-OBJ
values. It is
the dual of INJECT-RIGHT
The formal grammar is
(->left mcar mcadr)
Where [->LEFT
] is the constructor, [MCAR
] is the value being injected into
the coproduct of [MCAR
] + [MCADR
], and the [MCADR
] is just the type for
the unused right constructor.
Example:
(geb-gui::visualize (->left so1 geb-bool:bool))
(comp
(mcase geb-bool:true
geb-bool:not)
(->left so1 geb-bool:bool))
In the second example, we inject a term with the shape SO1
(0
1
) into a pair
with the shape (SO1
× [GEB-BOOL:BOOL
]), then we use MCASE
to denote a
morphism saying. IF
the input is of the shape [SO1
], then give us True,
otherwise flip the value of the boolean coming in.
[class] INJECT-RIGHT \
The right injection morphism. Takes two CAT-OBJ
values. It is
the dual of INJECT-LEFT
The formal grammar is
(->right mcar mcadr)
Where ->RIGHT
is the constructor, [MCADR
] is the value being injected into
the coproduct of [MCAR
] + [MCADR
], and the [MCAR
] is just the type for
the unused left constructor.
Example:
(geb-gui::visualize (->right so1 geb-bool:bool))
(comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
In the second example, we inject a term with the shape [GEB-BOOL:BOOL
]
into a pair with the shape (SO1
× [GEB-BOOL:BOOL
]), then we use
[MCASE
] to denote a morphism saying. IF
the input is of the shape [SO1
],
then give us True, otherwise flip the value of the boolean coming in.
[class] PROJECT-LEFT \
The LEFT
PROJECTION. Takes two
[CAT-MORPH
] values. When the LEFT
PROJECTION morphism is then applied, it grabs the left value of a product,
with the type of the product being determined by the two
[CAT-MORPH
] values given.
the formal grammar of a PROJECT-LEFT
is:
(<-left mcar mcadr)
Where [<-LEFT
] is the constructor, [MCAR
] is the left type of the
PRODUCT and [MCADR
] is the right type of the PRODUCT.
Example:
(geb-gui::visualize
(<-left geb-bool:bool (prod so1 geb-bool:bool)))
In this example, we are getting the left [GEB-BOOL:BOOL
] from a
product with the shape
(GEB-BOOL:BOOL
× SO1
× [GEB-BOOL:BOOL
])
[class] PROJECT-RIGHT \
The RIGHT
PROJECTION. Takes two
[CAT-MORPH
] values. When the RIGHT
PROJECTION morphism is then applied, it grabs the right value of a product,
with the type of the product being determined by the two
[CAT-MORPH
] values given.
the formal grammar of a PROJECT-RIGHT
is:
(<-right mcar mcadr)
Where [<-RIGHT
] is the constructor, [MCAR
] is the right type of the
PRODUCT and [MCADR
] is the right type of the PRODUCT.
Example:
(geb-gui::visualize
(comp (<-right so1 geb-bool:bool)
(<-right geb-bool:bool (prod so1 geb-bool:bool))))
In this example, we are getting the right [GEB-BOOL:BOOL
] from a
product with the shape
(GEB-BOOL:BOOL
× SO1
× [GEB-BOOL:BOOL
])
The Accessors specific to Subst Morph
[accessor] MCAR COMP (:MCAR)
The first composed morphism
[accessor] MCADR COMP (:MCADR)
the second morphism
[accessor] MCAR CASE (:MCAR)
The morphism that gets applied on the left coproduct
[accessor] MCADR CASE (:MCADR)
The morphism that gets applied on the right coproduct
[accessor] MCAR PAIR (:MCAR)
The left morphism
[accessor] MCDR PAIR (:MCDR)
The right morphism
[accessor] MCADR PROJECT-RIGHT (:MCADR)
Right projection (product elimination)
This section covers the REALIZED-OBJECT
type. This
represents a realized SUBSTOBJ
term.
The REALIZED-OBJECT
is not a real constructor but rather a sum
type for the following type
(deftype realized-object () `(or left right list so1 so0))
In ML we would have written something like
type realized-object = so0
| so1
| list
| left
| right
[type] REALIZED-OBJECT
A realized object that can be sent into.
Lists represent PROD
in the <SUBSTOBJ>
category
These functions are generic lenses of the GEB codebase. If a class is defined, where the names are not known, then these accessors are likely to be used. They may even augment existing classes.
[generic-function] MCDR X
Similar to MCAR
, however acts like a CDR
for
[classes] that we wish to view as a SEQUENCE
[generic-function] OBJ X
Grabs the underlying object
[generic-function] NAME X
the name of the given object
[generic-function] FUNC X
the function of the object
[generic-function] PREDICATE X
the PREDICATE
of the
object
[generic-function] THEN X
the then branch of the object
[generic-function] ELSE X
the then branch of the object
[generic-function] CODE X
the code of the object
The API for creating GEB terms. All the functions and variables here relate to instantiating a term
[variable] *SO0* s-0
The Initial Object
[variable] *SO1* s-1
The Terminal Object
More Ergonomic API variants for *SO0*
and *SO1*
[function] \<-LEFT MCAR MCADR
projects left constructor
[function] \<-RIGHT MCAR MCADR
projects right constructor
[function] ->LEFT MCAR MCADR
injects left constructor
[function] ->RIGHT MCAR MCADR
injects right constructor
Various forms and structures built on-top of Core Category
[method] GAPPLY (MORPH \
My main documentation can be found on GAPPLY
I am the GAPPLY
for \REALIZED-OBJECT
. See the
documentation for REALIZED-OBJECT
for the forms it can take.
Some examples of me are
GEB> (gapply (comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
(left so1))
(right s-1)
GEB> (gapply (comp
(mcase geb-bool:true
geb-bool:not)
(->right so1 geb-bool:bool))
(right so1))
(left s-1)
GEB> (gapply geb-bool:and
(list (right so1)
(right so1)))
(right s-1)
GEB> (gapply geb-bool:and
(list (left so1)
(right so1)))
(left s-1)
GEB> (gapply geb-bool:and
(list (right so1)
(left so1)))
(left s-1)
GEB> (gapply geb-bool:and
(list (left so1)
(left so1)))
(left s-1)
[method] GAPPLY (MORPH OPAQUE-MORPH) OBJECT
My main documentation can be found on GAPPLY
I am the GAPPLY
for a generic OPAQUE-MOPRH
I simply dispatch GAPPLY
on my interior code
lisp GEB> (gapply (comp geb-list:*car* geb-list:*cons*) (list (right geb-bool:true-obj) (left geb-list:*nil*))) (right GEB-BOOL:TRUE)
[method] GAPPLY (MORPH OPAQUE) OBJECT
My main documentation can be found on GAPPLY
I am the GAPPLY
for a generic OPAQUE
I
simply dispatch GAPPLY
on my interior code, which
is likely just an object
Here we define out the idea of a boolean. It comes naturally from the concept of coproducts. In ML they often define a boolean like
data Bool = False | True
We likewise define it with coproducts
(def bool (coprod so1 so1))
(def true (->right so1 so1))
(def false (->left so1 so1))
The functions given work on this.
Here we define out the idea of a List. It comes naturally from the
concept of coproducts. Since we lack polymorphism this list is
concrete over GEB-BOOL:@GEB-BOOL
In ML syntax it looks like
data List = Nil | Cons Bool List
We likewise define it with coproducts, with the recursive type being opaque
(defparameter *nil* (so1))
(defparameter *cons-type* (reference 'cons))
(defparameter *canonical-cons-type*
(opaque 'cons
(prod geb-bool:bool *cons-type*)))
(defparameter *list*
(coprod *nil* *cons-type*))
The functions given work on this.
These cover various conversions from Subst Morph and Subst Obj into other categorical data structures.
[method] TO-CIRCUIT (OBJ \
Turns a Subst Morph to a Vamp-IR Term
[method] TO-SEQN (OBJ \
Preserves identity morphims
[method] TO-SEQN (OBJ COMP)
Preserves composition
[method] TO-SEQN (OBJ INIT)
Produces a list of zeroes Currently not aligning with semantics of drop-width as domain and codomain can be of differing lengths
[method] TO-SEQN (OBJ TERMINAL)
Drops everything to the terminal object, i.e. to nothing
[method] TO-SEQN (OBJ INJECT-LEFT)
Injects an x by marking its entries with 0 and then inserting as padded bits if necessary
[method] TO-SEQN (OBJ INJECT-RIGHT)
Injects an x by marking its entries with 1 and then inserting as padded bits if necessary
[method] TO-SEQN (OBJ CASE)
Cases by forgetting the padding and if necessary dropping the extra entries if one of the inputs had more of them to start with
[method] TO-SEQN (OBJ PROJECT-LEFT)
Takes a list of an even length and cuts the right half
[method] TO-SEQN (OBJ PROJECT-RIGHT)
Takes a list of an even length and cuts the left half
[method] TO-SEQN (OBJ PAIR)
Forks entries and then applies both morphism on corresponding entries
[method] TO-SEQN (OBJ DISTRIBUTE)
Given A x (B + C) simply moves the 1-bit entry to the front and keep the same padding relations to get ((A x B) + (A x C)) as times appends sequences
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-DIV)
Division is interpreted as divition
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-CONST)
A choice of a natural number is the same exact choice in SeqN
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-INJ)
Injection of bit-sizes is interpreted in the same maner in SeqN
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:ONE-BIT-TO-BOOL)
Iso interpreted in an evident manner
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-DECOMPOSE)
Decomposition is interpreted on the nose
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-EQ)
Equality predicate is interpreted on the nose
[method] TO-SEQN (OBJ GEB.EXTENSION.SPEC:NAT-LT)
Equality predicate is interpreted on the nose
Various utility functions on top of Core Category
[function] PAIR-TO-LIST PAIR &OPTIONAL ACC
converts excess pairs to a list format
[function] SAME-TYPE-TO-LIST PAIR TYPE &OPTIONAL ACC
converts the given type to a list format
[function] CLEAVE V1 &REST VALUES
Applies each morphism to the object in turn.
[function] CONST F X
The constant morphism.
Takes a morphism from SO1
to a desired value of type $B$,
along with a [<SUBSTOBJ>
] that represents the input type say of
type $A$, giving us a morphism from $A$ to $B$.
Thus if:
F
: SO1
→ a,
X
: b
then: (const f x) : a → b
Γ, f : so1 → b, x : a
----------------------
(const f x) : a → b
Further, If the input F
has an ALIAS
, then we wrap the output
in a new alias to denote it's a constant version of that value.
Example:
(const true bool) ; bool -> bool
[function] COMMUTES-LEFT MORPH
swap the input domain of the given [cat-morph]
In order to swap the domain we expect the [cat-morph] to
be a PROD
Thus if: (dom morph) ≡ (prod x y)
, for any x
, y
[CAT-OBJ
]
(commutes-left (dom morph)) ≡ (prod y x)
u
`
Γ, f : x × y → a(commutes-left f) : y × x → a `
[generic-function] SO-CARD-ALG OBJ
Gets the cardinality of the given object, returns a FIXNUM
[function] CURRY F
Curries the given object, returns a [cat-morph]
The [cat-morph] given must have its DOM
be of a PROD
type, as CURRY
invokes the idea of
if f : (PROD
a b) → c
for all a
, b
, and c
being an element of [cat-morph]
then: (curry f): a → c^b
where c^b means c to the exponent of b (EXPT
c b)
Γ, f : a × b → c,
--------------------
(curry f) : a → c^b
In category terms, a → c^b
is isomorphic to a → b → c
[function] COPROD-MOR F G
Given f : A → B and g : C → D gives appropriate morphism between
COPROD
objects f x g : A + B → C + D via the universal property.
That is, the morphism part of the coproduct functor Geb x Geb → Geb
[function] PROD-MOR F G
Given f : A → B and g : C → D gives appropriate morphism between
PROD
objects f x g : A x B → C x D via the universal property.
This is the morphism part of the product functor Geb x Geb → Geb
[function] UNCURRY Y Z F
Given a morphism f : x → z^y and explicitly given y and z variables produces an uncurried version f' : x × y → z of said morphism
[generic-function] TEXT-NAME MORPH
Gets the name of the moprhism
These utilities are on top of [CAT-OBJ
]
[method] MAYBE (OBJ \
I recursively add maybe terms to all \
turning products of A x B into Maybe (Maybe A x Maybe B),
turning coproducts of A | B into Maybe (Maybe A | Maybe B),
PLACEHOLDER: TO SHOW OTHERS HOW EXAMPLE
s WORK
Let's see the transcript of a real session of someone working with GEB:
(values (princ :hello) (list 1 2))
.. HELLO
=> :HELLO
=> (1 2)
(+ 1 2 3 4)
=> 10
This package contains many extensions one may see over the codebase.
Each extension adds an unique feature to the categories they are extending. To learn more, read about the individual extension you are interested in.
Common Sub expressions represent repeat logic that can be found throughout any piece of code
[class] COMMON-SUB-EXPRESSION DIRECT-POINTWISE-MIXIN META-MIXIN CAT-MORPH
I represent common sub-expressions found throughout the code.
I implement a few categorical extensions. I am a valid
CAT-MORPH
along with fulling the interface for the
[GEB.POLY.SPEC:
The name should come from an algorithm that automatically fines common sub-expressions and places the appropriate names.
The Opaque extension lets users write categorical objects and morphisms where their implementation hide the specifics of what types they are operating over
[class] OPAQUE CAT-OBJ META-MIXIN
I represent an object where we want to hide the implementation
details of what kind of GEB:SUBSTOBJ
I am.
[class] REFERENCE CAT-OBJ CAT-MORPH DIRECT-POINTWISE-MIXIN META-MIXIN
I represent a reference to an OPAQUE
identifier.
[class] OPAQUE-MORPH CAT-MORPH META-MIXIN
This represents a morphsim where we want to deal with an
OPAQUE
that we know intimate details of
[accessor] CODE OPAQUE-MORPH (:CODE)
the code that represents the underlying morphsism
[accessor] DOM OPAQUE-MORPH (:DOM)
The dom of the opaque morph
[accessor] CODOM OPAQUE-MORPH (:CODOM)
The codom of the opaque morph
The Natural Object/Morphism extension allows to expand the core Geb category with additional constructors standing in for bt-sequence representation of natural numbers along with basic operation relating to those.
[class] \
the class corresponding to [NATOBJ
], the extension of
SUBSTOBJ adding to Geb bit representation of natural numbers.
[class] NAT-WIDTH \
the NAT-WIDTH
object. Takes a non-zero natural
number [NUM
] and produces an object standing for cardinality
2^([NUM
]) corresponding to [NUM
]-wide bit number.
The formal grammar of NAT-WIDTH
is
(nat-width num)
where NAT-WIDTH
is the constructor, [NUM
] the choice
of a natural number we want to be the width of the bits we
are to consder.
[class] \
the class corresponding to [NATMORPH
], the extension of
SUBSTMORPH adding to Geb basic operations on bit representations
of natural numbers
[class] NAT-ADD \
Given a natural number [NUM
] greater than 0, gives a morphsm
(nat-add num) : (nat-mod num) x (nat-mod num) -> (nat-mod num) representing
floored addition of two bits of length n.
The formal grammar of NAT-ADD
is
lisp (nat-add num)
[class] NAT-MULT \
Given a natural number [NUM
] greater than 0, gives a morphsm
(nat-mult num) : (nat-mod num) x (nat-mod num) -> (nat-mod n) representing floored
multiplication in natural numbers modulo n.
The formal grammar of NAT-MULT
is
lisp (nat-mult num)
[class] NAT-SUB \
Given a natural number [NUM
] greater than 0, gives a morphsm
(nat-sub sum) : (nat-mod num) x (nat-mod num) -> (nat-mod num) representing
floored subtraction of two bits of length n.
The formal grammar of NAT-SUB
is
lisp (nat-sub num)
[class] NAT-DIV \
Given a natural number [NUM
] greater than 0, gives a morphsm
(nat-div num) : (nat-mod num) x (nat-mod num) -> (nat-mod num) representing
floored division in natural numbers modulo n.
The formal grammar of NAT-DIV
is
lisp (nat-div num)
[class] NAT-CONST \
Given a NUM
natural number, gives a morphsm
(nat-const num pos) : so1 -> (nat-width num).
That is, chooses the POS
natural number as a NUM
-wide bit number
The formal grammar of NAT-ADD
is
lisp (nat-const num pos)
[class] NAT-INJ \
Given a nutural number [NUM
] presents a [NUM
]-wide bit number
as a ([NUM
] + 1)-wide bit number via injecting.
The formal grammar of NAT-INJ
is
(nat-inj num)
In Geb, the injection presents itself as a morphism (nat-width num) -> (nat-width (1 + num))
[class] NAT-CONCAT \
Given two natural numbers of bit length n and m, concatenates them in that order giving a bit of length n + m.
The formal grammar of NAT-CONCAT
is
(nat-concat num-left num-right)
In Geb this corresponds to a morphism (nat-width num-left) x (nat-width num-right) -> (nat-width (n + m))
For a translation to SeqN simply take x of n width and y of m with and take x^m + y
[class] ONE-BIT-TO-BOOL \
A map nat-width 1 -> bool sending #0 to false and #1 to true
[class] NAT-DECOMPOSE \
Morphism nat-width n -> (nat-width 1) x (nat-width (1- n)) splitting a natural number into the last and all but last collection of bits
[class] NAT-EQ \
Morphism nat-width n x nat-width n -> bool which evaluated to true iff both inputs are the same
[class] NAT-LT \
Morphism nat-width n x nat-width n -> bool which evaluated to true iff the first input is less than the second
[class] NAT-MOD \
Morphism nat-width n x nat-width n -> nat-width n which takes a modulo of the left projection of a pair by the second projection of a pari
This section covers the suite of tools that help visualize geb objects and make the system nice to work with
The GEB visualizer deals with visualizing any objects found in the Core Category
if the visualizer gets a Subst Morph, then it will show how
the GEB:SUBSTMORPH
changes any incoming term.
if the visualizer gets a Subst Obj, then it shows the data layout of the term, showing what kind of data
[function] VISUALIZE OBJECT &OPTIONAL (ASYNC T)
Visualizes both Subst Obj and Subst Morph objects
[function] KILL-RUNNING
Kills all threads and open gui objects created by VISUALIZE
One can aid the visualization process a bit, this can be done by
simply placing ALIAS
around the object, this will place it
in a box with a name to better identify it in the graphing procedure.
This works like the normal visualizer except it exports it to a file to be used by other projects or perhaps in papers
[function] SVG OBJECT PATH &KEY (DEFAULT-VIEW (MAKE-INSTANCE 'SHOW-VIEW))
Runs the visualizer, outputting a static SVG
image at the directory of choice.
You can customize the view. By default it uses the show-view, which is the default of the visualizer.
A good example usage is
GEB-TEST> (geb-gui:svg (shallow-copy-object geb-bool:and) "/tmp/foo.svg")
This section covers the GEB Graph representation
This section covers the graphing procedure in order to turn a GEB object into a format for a graphing backend.
The core types that facilittate the functionality
[type] NOTE
A note is a note about a new node in the graph or a note about a
NODE
which should be merged into an upcoming NODE
.
An example of a NODE-NOTE
would be in the case of pair
(pair g f)
Π₁
--f--> Y------
X----| |-----> [Y × Z]
--g--> Z-----
Π₂
An example of a MERGE-NOTE
(Case f g)
(COMP g f)
χ₁
-------> X --f---
[X + Y]--| ---> A
-------> Y --g---/
χ₂
X -f-> Y --> Y -g-> Z
Notice that in the pair case, we have a note and a shared node to place down, where as in both of the MERGE-NOTE examples, the Note at the end is not prepended by any special information
[class] NODE META-MIXIN
I represent a graphical node structure. I contain my children and a value to display, along with the representation for which the node really stands for.
Further, we derive the meta-mixin, as it's important for arrow drawing to know if we are the left or the right or the nth child of a particular node. This information is tracked, by storing the object that goes to it in the meta table and recovering the note.
[class] SQUASH-NOTE
This note should be squashed into another note and or node.
[generic-function] GRAPHIZE MORPH NOTES
Turns a morphism into a node graph.
The NOTES
serve as a way of sharing and continuing computation.
If the NOTE
is a :SHARED
NOTE
then it represents a NODE
without children, along with saying where it came from. This is to be
stored in parent of the NOTE
If the NOTE
is a :CONTINUE NOTE
, then the computation is continued at
the spot.
The parent field is to set the note on the parent if the NOTE
is going
to be merged
[function] CONS-NOTE NOTE NOTES
Adds a note to the notes list.
[function] APPLY-NOTE NOTE-TO-BE-ON NOTE
Here we apply the NOTE
to the NODE
.
In the case of a new node, we record down the information in the note,
and set the note as the child of the current NODE
. The NODE
is
returned.
In the case of a squash-note, we instead just return the squash-note
as that is the proper NODE
to continue from
[function] DETERMINE-TEXT-AND-OBJECT-FROM-NODE FROM TO
Helps lookup the text from the node
[function] NOTERIZE-CHILDREN NODE FUNC
Applies a specified note to the CHILDREN
of the NODE
.
It does this by applying FUNC
on all the CHILDREN
and the index of the
child in the list
[function] NOTORIZE-CHILDREN-WITH-INDEX-SCHEMA PREFIX NODE
Notorizes the node with a prefix appended with the subscripted number
This changes how the graph is visualized, simplifying the graph in ways that are intuitive to the user
[function] PASSES NODE
Runs all the passes that simplify viewing the graph. These simplifications should not change the semantics of the graph, only display it in a more bearable way
This covers a GEB view of multibit sequences. In particular this type will be used in translating GEB's view of multibit sequences into Vampir
[class] \
Seqn is a category whose objects are finite sequences of natural numbers. The n-th natural number in the sequence represents the bitwidth of the n-th entry of the corresponding polynomial circuit
Entries are to be read as (x_1,..., x_n) and x_i is the ith entry So car of a such a list will be the first entry, this is the dissimilarity with the bit notation where newer entries come first in the list
We interpret pairs as actual pairs if entry is of width above 0 and drop it otherwise in the Vamp-Ir setup so that ident bool x bool goes to name x1 = x1 as (seqwidth bool) = (1, max(0, 0))
[class] COMPOSITION \
composes the MCAR
and MCADR
morphisms
f : (a1,...,an) -> (b1,..., bm), g : (b1,...,bm) -> (c1, ..., ck)
compose g f : (a1,...,an) -> (c1,...,cn)
[class] ID \
For (x1,...,xn), id (x1,...,xn) : (x1,....,xn) -> (x1,...,xn)
[class] PARALLEL-SEQ \
For f : (a1,..., an) -> (x1,...,xk), g : (b1,..., bm) -> (y1,...,yl) parallel f g : (a1,...,an, b1,...bm) -> (x1,...,xk,y1,...,yl)
[class] FORK-SEQ \
Copies the MCAR
of length n onto length 2*n by copying its
inputs (MCAR
). fork (a1, ...., an) -> (a1,...,an, a1,..., an)
[class] DROP-NIL \
Drops everything onto a 0 vector, the terminal object drop-nil (x1, ..., xn) : (x1,...,xn) -> (0)
[class] REMOVE-RIGHT \
Removes an extra 0 entry from MCAR
on the right
remove (x1,...,xn) : (x1,...,xn, 0) -> (x1,...,xn)
[class] REMOVE-LEFT \
Removes an extra 0 entry from MCAR
on the left
remove (x1,...,xn) : (0, x1,...,xn) -> (x1,...,xn)
[class] DROP-WIDTH \
Given two vectors of same length but with the first ones of padded width, simply project the core bits without worrying about extra zeroes at the end if they are not doing any work drop-width (x1,...,xn) (y1,...,yn) : (x1,...,xn) -> (y1,...,yn) where xi > yi for each i and entries need to be in the image of the evident injection map
In other words the constraints here are that on the enput ei corresponding to xi bit entry the constraint is that range yi ei is true alongside the usual (isrange xi ei) constraint
Another implementation goes through manual removal of extra bits (specifically xi-yi bits) to the left after the decomposition by range xi
[class] INJ-LENGTH-LEFT \
Taken an MCAR
vector appends to it
MCADR
list of vectors with arbitrary bit length by doing
nothing on the extra entries, i.e. just putting 0es there.
inj-lengh-left (x1,...,xn) (y1,...,ym): (x1,...,xn) -> (x1,...,xn, y1,...,yn)
Where 0es go in the place of ys or nothing if the injection is into
0-bits
So what gets injected is the left part
[class] INJ-LENGTH-RIGHT \
Taken an MCADR
vector appends to it
MCAR
list of vectors with arbitrary bit length by doing
nothing on the extra entries, i.e. just putting 0es there.
inj-lengh-right (x1,...,xn) (y1,...,ym): (y1,...,ym) -> (x1,...,xn, y1,...,yn)
Where 0es go in the place of xs. If any of yi's are 0-bit vectors, the injection
goes to nil entry on those parts
What gets injected is the right part
[class] INJ-SIZE \
Taken an MCAR
1-long and injects it to
MCADR
-wide slot wider than the original one by padding it with
0es on the left.
inj-size x1 m: (x1) -> (m)
Just a special case of drop-width
[class] BRANCH-SEQ \
Takes an f: (x1,...,xn) -> c, g : (x1,...,xn) ->c and produces branch-seq f g (1, x1,...,xn) -> c acting as f on 0 entry and g on 1
[class] SHIFT-FRONT \
Takes an MCAR
sequence of length at least
MCADR
and shifts the MCADR
-entry to the front
shift-front (x1,...,xn) k : (x1,...,xk,...,xn) -> (xk, x1,..., x_k-1, x_k+1,...,xn)
[class] ZERO-BIT \
Zero choice of a bit zero: (0) -> (1)
[class] ONE-BIT \
1 choice of a bit one: (0) -> (1)
[class] SEQN-ADD \
Compiles to range-checked addition of natural numbers
of MCAR
width. seqn-add n : (n, n) -> (n)
[class] SEQN-SUBTRACT \
Compiles to negative-checked subtraction of natural numbers
of MCAR
width. seqn-subtract n : (n, n) -> (n)
[class] SEQN-MULTIPLY \
Compiles to range-checked multiplication of natural numbers
of MCAR
width. seqn-multiply n : (n, n) -> (n)
[class] SEQN-DIVIDE \
Compiles to usual Vamp-IR floored multiplication of natural
numbers of MCAR
width. seqn-divide n : (n, n) -> (n)
[class] SEQN-NAT \
Produces a MCAR
-wide representation of MCADR
natural number
seqn-nat n m = (0) -> (n)
[class] SEQN-CONCAT \
Takes a number of MCAR
and MCADR
width and produces a number
of MCAR
+ MCADR
width by concatenating the bit representations. Using field
elements, this translates to multiplying the first input by 2 to the power of
MCADR
and summing with the second entry
seqn-concat n m = (n, m) -> (n+m)
[class] SEQN-DECOMPOSE \
The type signature of the morphism is seqn-docompose n : (n) -> (1, (n - 1)) with the intended semantics being that the morphism takes an n-bit integer and splits it, taking the leftmost bit to the left part of the codomain and the rest of the bits to the right
[class] SEQN-EQ \
The type signature of the morphism is seqn-eq n : (n, n) -> (1, 0) with the intended semantics being that the morphism takes two n-bit integers and produces 1 iff they are equal
[class] SEQN-LT \
The type signature of the morphism is seqn-eq n : (n, n) -> (1, 0) with the intended semantics being that the morphism takes two n-bit integers and produces 1 iff the first one is less than the second
[class] SEQN-MOD \
The type signature of the morphism is seqn-mod n : (n, n) -> (n) with the intended semantics being that the morphism takes two n-bit integers and produces the modulo of the first integer by the second
Every accessor for each of the classes making up seqn
this covers the seqn api
[function] FILL-IN NUM SEQ
Fills in extra inputs on the right with 0-oes
[function] PROD-LIST L1 L2
Takes two lists of same length and gives pointwise product where first element come from first list and second from second
SEQN> (prod-list (list 1 2) (list 3 4))
((1 3) (2 4))
[function] SEQ-MAX-FILL SEQ1 SEQ2
Takes two lists, makes them same length by adding 0es on the right where necessary and takes their pointwise product
[function] INJ-COPROD-PARALLEL OBJ COPR
takes an width(A) or width(B) already transformed with a width(A+B) and gives an appropriate injection of (a1,...,an) into (max (a1, b1), ...., max(an, bn),...) i.e. where the maxes are being taken during the width operation without filling in of the smaller object
[method] DOM (X \
Gives the domain of a morphism in SeqN. For a less formal description consult the specs file
[method] COD (X \
Gives the codomain of a morphism in SeqN. For a less formal description consult the specs file
[method] GAPPLY (MORPHISM \
Takes a list of vectors of natural numbers and gives out their evaluations. Currently does not correspond directly to the intended semantics but is capable of successfully evaluating all compiled terms
This covers transformation functions from
[method] TO-CIRCUIT (MORPHISM \
Turns a SeqN term into a Vamp-IR Gate with the given name Note that what is happening is that we look at the domain of the morphism and skip 0es, making non-zero entries into wires
[function] TEST-CALL CIRCUIT
Given a compiled VampIR function with name foo and arguments x1...xn prints an equality as foo in1 ... in2 = out
[method] TO-VAMPIR (OBJ ID) INPUTS CONSTRAINTS
Given a tuple (x1,...,xn) does nothing with it
[method] TO-VAMPIR (OBJ COMPOSITION) INPUTS CONSTRAINTS
Compile the MCADR
after feeding in appropriate
inputs and then feed them as entries to compiled MCAR
[method] TO-VAMPIR (OBJ PARALLEL-SEQ) INPUTS CONSTRAINTS
[method] TO-VAMPIR (OBJ FORK-SEQ) INPUTS CONSTRAINTS
Given a tuple (x1,...,xn) copies it twice
[method] TO-VAMPIR (OBJ DROP-NIL) INPUTS CONSTRAINTS
Drops everything by producing nothing
[method] TO-VAMPIR (OBJ REMOVE-RIGHT) INPUTS CONSTRAINTS
We do not have nul inputs so does nothing
[method] TO-VAMPIR (OBJ REMOVE-LEFT) INPUTS CONSTRAINTS
We do not have nul inputs so does nothing
[method] TO-VAMPIR (OBJ DROP-WIDTH) INPUTS CONSTRAINTS
The compilation does not produce dropping with domain inputs wider than codomain ones appropriately. Hence we do not require range checks here and simply project
[method] TO-VAMPIR (OBJ INJ-LENGTH-LEFT) INPUTS CONSTRAINTS
Look at the MCAR
. Look at non-null wide entries and place
0-es in the outputs otherwise ignore
[method] TO-VAMPIR (OBJ INJ-LENGTH-RIGHT) INPUTS CONSTRAINTS
Look at the MCADR
. Look at non-null wide entries and place
0-es in the outputs
[method] TO-VAMPIR (OBJ INJ-SIZE) INPUTS CONSTRAINTS
During th ecompilation procedure the domain will not have larger width than the codomain so we simply project
[method] TO-VAMPIR (OBJ BRANCH-SEQ) INPUTS CONSTRAINTS
With the leftmost input being 1 or 0, pointwise do usual bit
branching. If 0 run the MCAR
, if 1 run the MCADR
[method] TO-VAMPIR (OBJ SHIFT-FRONT) INPUTS CONSTRAINTS
Takes the MCADR
entry and moves it upward leaving everything
else fixed. Note that we have to be careful as inputs will have 0es
removed already and hence we cannot count as usual
This covers a GEB view of Boolean Circuits. In particular this type will be used in translating GEB's view of Boolean Circuits into Vampir
This section covers the types of things one can find in the [BIT
(0
1
)s]
constructors
[class] PARALLEL \
(parallel x y)
constructs a PARALLEL
term where the [MCAR
] is x
and the
[MCADR
] is y
,
where if
x : a → b, y : c → d
-------------------------------
(parallel x y) : a + c → b + d
then the PARALLEL
will return a function from a and c to b
and d where the [MCAR
] and [MCADR
] run on subvectors of the input.
[class] SWAP \
(swap n m)
binds the [MCAR
] to n and [MCADR
] to m, where if the input
vector is of length n + m
, then it swaps the bits, algebraically we
view it as
(swap n m) : #*b₁...bₙbₙ₊₁...bₙ₊ₘ → #*bₙ₊₁...bₘ₊ₙb₁...bₙ
[class] ZERO \
[ZERO
] map from 0 onto 1 producing a vector with only 0 in
it.
[class] IDENT \
[IDENT
] represents the identity
[class] DROP \
[DROP
] represents the unique morphism from n to 0.
[class] BRANCH \
(branch x y)
constructs a BRANCH
term where the [MCAR
] is x
and the
[MCADR
] is y
,
where if
x : a → b, y : a → b
-------------------------------
(branch x y) : 1+a → b
then the [BRANCH
] will return a function on the type 1 + a
, where the
1 represents a bit to branch on. If the first bit is 0
, then the
[MCAR
] is ran, however if the bit is 1
, then the [MCADR
] is ran.
Every accessor for each of the CLASS
's found here are from Accessors
[function] COMPOSE MCAR MCADR &REST ARGS
Creates a multiway constructor for [COMPOSE
]
[function] FORK MCAR
FORK
ARG1
[function] PARALLEL MCAR MCADR &REST ARGS
Creates a multiway constructor for [PARALLEL
]
[function] SWAP MCAR MCADR
swap ARG1 and ARG2
[function] IDENT MCAR
ident ARG1
[function] DROP MCAR
drop ARG1
[function] BRANCH MCAR MCADR
branch with ARG1 or ARG2
This covers the Bits (Boolean Circuit) API
[method] GAPPLY (MORPHISM \
My My main documentation can be found on GAPPLY
I am the GAPPLY
for <BITC>
, the
OBJECT
that I expect is of type NUMBER
. GAPPLY
reduces down to ordinary common lisp expressions rather straight
forwardly
;; figure out the number of bits the function takes
GEB-TEST> (dom (to-bitc geb-bool:and))
2 (2 bits, #x2, #o2, #b10)
GEB-TEST> (gapply (to-bitc geb-bool:and) #*11)
#*1
GEB-TEST> (gapply (to-bitc geb-bool:and) #*10)
#*0
GEB-TEST> (gapply (to-bitc geb-bool:and) #*01)
#*0
GEB-TEST> (gapply (to-bitc geb-bool:and) #*00)
#*0
[method] GAPPLY (MORPHISM \
I am a helper gapply function, where the second argument for
[<BITC>
] is a list. See the docs for the BIT-VECTOR
version for
the proper one. We do allow sending in a list like so
;; figure out the number of bits the function takes
GEB-TEST> (dom (to-bitc geb-bool:and))
2 (2 bits, #x2, #o2, #b10)
GEB-TEST> (gapply (to-bitc geb-bool:and) (list 1 1))
#*1
GEB-TEST> (gapply (to-bitc geb-bool:and) (list 1 0))
#*0
GEB-TEST> (gapply (to-bitc geb-bool:and) (list 0 1))
#*0
GEB-TEST> (gapply (to-bitc geb-bool:and) (list 0 0))
#*0
[method] DOM (X \
Gives the length of the bit vector the [<BITC>
] moprhism takes
[method] CODOM (X \
Gives the length of the bit vector the [<BITC>
] morphism returns
This covers transformation functions from
[method] TO-CIRCUIT (MORPHISM \
Turns a BITC
term into a Vamp-IR Gate with the given name
[method] TO-VAMPIR (OBJ FORK) VALUES CONSTRAINTS
Copy input n input bits into 2*n output bits
[method] TO-VAMPIR (OBJ PARALLEL) VALUES CONSTRAINTS
Take n + m bits, execute car the n bits and cadr on the m bits and concat the results from car and cadr
[method] TO-VAMPIR (OBJ SWAP) VALUES CONSTRAINTS
Turn n + m bits into m + n bits by swapping
[method] TO-VAMPIR (OBJ ONE) VALUES CONSTRAINTS
Produce a bitvector of length 1 containing 1
[method] TO-VAMPIR (OBJ ZERO) VALUES CONSTRAINTS
Produce a bitvector of length 1 containing 0
[method] TO-VAMPIR (OBJ IDENT) VALUES CONSTRAINTS
turn n bits into n bits by doing nothing
[method] TO-VAMPIR (OBJ DROP) VALUES CONSTRAINTS
turn n bits into an empty bitvector
[method] TO-VAMPIR (OBJ BRANCH) VALUES CONSTRAINTS
Look at the first bit.
If its 0, run f on the remaining bits.
If its 1, run g on the remaining bits.
This covers a GEB view of Polynomials. In particular this type will be used in translating GEB's view of Polynomials into Vampir
This section covers the types of things one can find in the [POLY
]
constructors
[class] IDENT \
The Identity Element
[class] IF-ZERO \
compare with zero: equal takes first branch; not-equal takes second branch
[class] IF-LT \
If the [MCAR
] argument is strictly less than the [MCADR
] then the
[THEN
] branch is taken, otherwise the [ELSE
] branch is taken.
This covers the polynomial API
[method] GAPPLY (MORPHISM POLY:\
My main documentation can be found on GAPPLY
I am the GAPPLY
for POLY:<POLY>
, the
OBJECT
that I expect is of type NUMBER
. GAPPLY
reduces down to
ordinary common lisp expressions rather straight forwardly
Some examples of me are
(in-package :geb.poly)
POLY> (gapply (if-zero (- ident ident 1) 10 ident) 5)
5 (3 bits, #x5, #o5, #b101)
POLY> (gapply (if-zero (- ident ident) 10 ident) 5)
10 (4 bits, #xA, #o12, #b1010)
POLY> (gapply (- (* 2 ident ident) (* ident ident)) 5)
25 (5 bits, #x19, #o31, #b11001)
[method] GAPPLY (MORPHISM INTEGER) OBJECT
My main documentation can be found on GAPPLY
I am the GAPPLY
for INTEGER
s, the
OBJECT
that I expect is of type NUMBER
. I simply return myself.
Some examples of me are
(in-package :geb.poly)
POLY> (gapply 10 5)
10 (4 bits, #xA, #o12, #b1010)
Every accessor for each of the CLASS
's found here are from Accessors
[function] + MCAR MCADR &REST ARGS
Creates a multiway constructor for [+]
[function] * MCAR MCADR &REST ARGS
Creates a multiway constructor for [*]
[function] / MCAR MCADR &REST ARGS
Creates a multiway constructor for [/]
[function] - MCAR MCADR &REST ARGS
Creates a multiway constructor for [-]
[function] MOD MCAR MCADR
MOD
ARG1 by ARG2
[function] COMPOSE MCAR MCADR &REST ARGS
Creates a multiway constructor for [COMPOSE
]
[function] IF-ZERO PRED THEN ELSE
checks if [PREDICATE
] is zero then take the [THEN
] branch otherwise
the [ELSE
] branch
[function] IF-LT MCAR MCADR THEN ELSE
Checks if the [MCAR
] is less than the [MCADR
] and chooses the appropriate branch
This covers transformation functions from
[method] TO-CIRCUIT (MORPHISM \
Turns a POLY
term into a Vamp-IR Gate with the given name
[method] TO-VAMPIR (OBJ INTEGER) VALUE LET-VARS
Numbers act like a constant function, ignoring input
[method] TO-VAMPIR (OBJ IDENT) VALUE LET-VARS
Identity acts as the identity function
[method] TO-VAMPIR (OBJ +) VALUE LET-VARS
Propagates the value and adds them
[method] TO-VAMPIR (OBJ *) VALUE LET-VARS
Propagates the value and times them
[method] TO-VAMPIR (OBJ -) VALUE LET-VARS
Propagates the value and subtracts them
[method] TO-VAMPIR (OBJ IF-ZERO) VALUE LET-VARS
The PREDICATE
that comes in must be 1 or 0 for the formula to work out.
This covers GEB's view on simply typed lambda calculus
This serves as a useful frontend for those wishing to write a compiler to GEB and do not wish to target the categorical model.
If one is targeting their compiler to the frontend, then the following code should be useful for you.
(in-package :geb.lambda.main)
MAIN>
(to-circuit
(lamb (list (coprod so1 so1))
(index 0))
:id)
(def id x1 = {
(x1)
};)
MAIN>
(to-circuit
(lamb (list (coprod so1 so1))
(case-on (index 0)
(lamb (list so1)
(right so1 (unit)))
(lamb (list so1)
(left so1 (unit)))))
:not)
(def not x1 = {
(((1 - x1) * 1) + (x1 * 0), ((1 - x1) * 1) + (x1 * 0))
};)
MAIN> (to-circuit (lamb (list geb-bool:bool)
(left so1 (right so1 (index 0)))) :foo)
(def foo x1 = {
(0, 1, x1)
};)
For testing purposes, it may be useful to go to the BITC
backend and
run our interpreter
MAIN>
(gapply (to-bitc
(lamb (list (coprod so1 so1))
(case-on (index 0)
(lamb (list so1)
(right so1 (unit)))
(lamb (list so1)
(left so1 (unit))))))
#*1)
#*00
MAIN>
(gapply (to-bitc
(lamb (list (coprod so1 so1))
(case-on (index 0)
(lamb (list so1)
(right so1 (unit)))
(lamb (list so1)
(left so1 (unit))))))
#*0)
#*11
This covers the various the abstract data type that is the simply
typed lambda calculus within GEB. The class presents untyped STLC
terms.
[type] STLC
Type of untyped terms of STLC
. Each class of a term has a slot for a type,
which can be filled by auxiliary functions or by user. Types are
represented as SUBSTOBJ.
[class] \
Class of untyped terms of simply typed lambda claculus. Given our presentation, we look at the latter as a type theory spanned by empty, unit types as well as coproduct, product, and function types.
[class] ABSURD \
The ABSURD
term provides an element of an arbitrary type
given a term of the empty type, denoted by SO0.
The formal grammar of ABSURD
is
(absurd tcod term)
where we possibly can include type information by
(absurd tcod term :ttype ttype)
The intended semantics are: [TCOD
] is a type whose term we want to
get (and hence a SUBSTOBJ) and [TERM
] is a term
of the empty type (and hence an STLC
.
This corresponds to the elimination rule of the empty type. Namely, given $$\Gamma \vdash \text{tcod : Type}$$ and $$\Gamma \vdash \text{term : so0}$$ one deduces $$\Gamma \vdash \text{(absurd tcod term) : tcod}$$
[class] UNIT \
The unique term of the unit type, the latter represented by
SO1. The formal grammar of UNIT
is
(unit)
where we can optionally include type information by
(unit :ttype ttype)
Clearly the type of UNIT
is SO1 but here
we provide all terms untyped.
This grammar corresponds to the introduction rule of the unit type. Namely $$\Gamma \dashv \text{(unit) : so1}$$
[class] LEFT \
Term of a coproduct type gotten by injecting into the left type of the coproduct. The formal grammar of
LEFT
is
(left rty term)
where we can include optional type information by
(left rty term :ttype ttype)
The intended semantics are as follows: RTY
should
be a type (and hence a SUBSTOBJ) and specify the
right part of the coproduct of the type TTYPE
of
the entire term. The term (and hence an STLC
) we are injecting
is TERM
.
This corresponds to the introduction rule of the coproduct type. Namely, given $$\Gamma \dashv \text{(ttype term) : Type}$$ and $$\Gamma \dashv \text{rty : Type}$$ with $$\Gamma \dashv \text{term : (ttype term)}$$ we deduce $$\Gamma \dashv \text{(left rty term) : (coprod (ttype term) rty)}$$
[class] RIGHT \
Term of a coproduct type gotten by injecting into the right type of
the coproduct. The formal grammar of RIGHT
is
(right lty term)
where we can include optional type information by
(right lty term :ttype ttype)
The intended semantics are as follows: [LTY
] should be a type (and
hence a SUBSTOBJ) and specify the left part of
the coproduct of the type [TTYPE
] of the entire term. The term (and
hence an STLC
) we are injecting is [TERM
].
This corresponds to the introduction rule of the coproduct type. Namely, given $$\Gamma \dashv \text{(ttype term) : Type}$$ and $$\Gamma \dashv \text{lty : Type}$$ with $$\Gamma \dashv \text{term : (ttype term)}$$ we deduce $$\Gamma \dashv \text{(right lty term) : (coprod lty (ttype term))}$$
[class] CASE-ON \
A term of an arbitrary type provided by casing on a coproduct term. The
formal grammar of CASE-ON
is
(case-on on ltm rtm)
where we can possibly include type information by
(case-on on ltm rtm :ttype ttype)
The intended semantics are as follows: [ON
] is a term (and hence an
STLC
) of a coproduct type, and [LTM
] and [RTM
] terms (hence
also STLC
) of the same type in the context of - appropriately
This corresponds to the elimination rule of the coprodut type. Namely, given
$$\Gamma \vdash \text{on : (coprod (mcar (ttype on)) (mcadr (ttype on)))}$$
and
$$\text{(mcar (ttype on))} , \Gamma \vdash \text{ltm : (ttype ltm)}$$
, $$\text{(mcadr (ttype on))} , \Gamma \vdash \text{rtm : (ttype ltm)}$$
we get
$$\Gamma \vdash \text{(case-on on ltm rtm) : (ttype ltm)}$$
Note that in practice we append contexts on the left as computation of
INDEX
is done from the left. Otherwise, the rules are the same as in
usual type theory if context was read from right to left.
[class] PAIR \
A term of the product type gotten by pairing a terms of a left and right
parts of the product. The formal grammar of PAIR
is
(pair ltm rtm)
where we can possibly include type information by
(pair ltm rtm :ttype ttype)
The intended semantics are as follows: [LTM
] is a term (and hence an
STLC
) of a left part of the product type whose terms we are
producing. [RTM
] is a term (hence also STLC
)of the right part
of the product.
The grammar corresponds to the introduction rule of the pair type. Given $$\Gamma \vdash \text{ltm : (mcar (ttype (pair ltm rtm)))}$$ and $$\Gamma \vdash \text{rtm : (mcadr (ttype (pair ltm rtm)))}$$ we have $$\Gamma \vdash \text{(pair ltm rtm) : (ttype (pair ltm rtm))}$$
[class] FST \
The first projection of a term of a product type.
The formal grammar of FST
is:
(fst term)
where we can possibly include type information by
(fst term :ttype ttype)
The intended semantics are as follows: TERM
is a
term (and hence an STLC
) of a product type, to whose left part
we are projecting to.
This corresponds to the first projection function gotten by induction on a term of a product type.
[class] SND \
The second projection of a term of a product type.
The formal grammar of SND
is:
(snd term)
where we can possibly include type information by
(snd term :ttype ttype)
The intended semantics are as follows: TERM
is a
term (and hence an STLC
) of a product type, to whose right
part we are projecting to.
This corresponds to the second projection function gotten by induction on a term of a product type.
[class] LAMB \
A term of a function type gotten by providing a term in the codomain
of the function type by assuming one is given variables in the
specified list of types. LAMB
takes in the TDOM
accessor a list of types - and hence of SUBSTOBJ - and in the
TERM
a term - and hence an STLC
. The formal grammar
of LAMB
is:
(lamb tdom term)
where we can possibly include type information by
(lamb tdom term :ttype ttype)
The intended semantics are: TDOM
is a list of types (and
hence a list of SUBSTOBJ) whose iterative product of
components form the domain of the function type. TERM
is a term (and hence an STLC
) of the codomain of the function type
gotten in the context to whom we append the list of the domains.
For a list of length 1, corresponds to the introduction rule of the function type. Namely, given $$\Gamma \vdash \text{tdom : Type}$$ and $$\Gamma, \text{tdom} \vdash \text{term : (ttype term)}$$ we have $$\Gamma \vdash \text{(lamb tdom term) : (so-hom-obj tdom (ttype term))}$$
For a list of length n, this corresponds to the iterated lambda type, e.g.
(lamb (list so1 so0) (index 0))
is a term of
(so-hom-obj (prod so1 so0) so0)
or equivalently
(so-hom-obj so1 (so-hom-obj so0 so0))
due to Geb's computational definition of the function type.
Note that INDEX
0 in the above code is of type SO1.
So that after annotating the term, one gets
LAMBDA> (ttype (term (lamb (list so1 so0)) (index 0)))
s-1
So the counting of indices starts with the leftmost argument for
computational reasons. In practice, typing of LAMB
corresponds with
taking a list of arguments provided to a lambda term, making it a context
in that order and then counting the index of the variable. Type-theoretically,
$$\Gamma \vdash \lambda \Delta (index i)$$ $$\Delta, \Gamma \vdash (index i)$$
So that by the operational semantics of INDEX
, the type of (index i)
in the above context will be the i'th element of the Delta context counted from
the left. Note that in practice we append contexts on the left as computation of
INDEX
is done from the left. Otherwise, the rules are the same as in
usual type theory if context was read from right to left.
[class] APP \
A term of an arbitrary type gotten by applying a function of an iterated
function type with a corresponding codomain iteratively to terms in the
domains. APP
takes as argument for the FUN
accessor
a function - and hence an STLC
- whose function type has domain an
iterated GEB:PROD
of SUBSTOBJ and for the TERM
a list of terms - and hence of STLC
- matching the types of the
product. The formal grammar of APP
is
(app fun term)
where we can possibly include type information by
(app fun term :ttype ttype)
The intended semantics are as follows:
FUN
is a term (and hence an STLC
) of a coproduct
type - say of (so-hom-obj (ttype term) y) - and TERM
is a
list of terms (hence also of STLC
) with nth term in the list having the
n-th part of the function type.
For a one-argument term list, this corresponds to the elimination rule of the function type. Given $$\Gamma \vdash \text{fun : (so-hom-obj (ttype term) y)}$$ and $$\Gamma \vdash \text{term : (ttype term)}$$ we get $$\Gamma \vdash \text{(app fun term) : y}$$
For several arguments, this corresponds to successive function application. Using currying, this corresponds to, given
G ⊢ (so-hom-obj (A₁ × ··· × Aₙ₋₁) Aₙ)
G ⊢ f : (so-hom-obj (A₁ × ··· × Aₙ₋₁)
G ⊢ tᵢ : Aᵢ
then for each i
less than n
gets us
G ⊢ (app f t₁ ··· tₙ₋₁) : Aₙ
Note again that i'th term should correspond to the ith element of the product in the codomain counted from the left.
[class] INDEX \
The variable term of an arbitrary type in a context. The formal
grammar of INDEX
is
(index pos)
where we can possibly include type information by
(index pos :ttype ttype)
The intended semantics are as follows: POS
is a
natural number indicating the position of a type in a context.
This corresponds to the variable rule. Namely given a context $$\Gamma_1 , \ldots , \Gamma_{pos} , \ldots , \Gamma_k $$ we have
$$\Gamma_1 , \ldots , \Gamma_k \vdash \text{(index pos) :} \Gamma_{pos}$$
Note that we add contexts on the left rather than on the right contra classical type-theoretic notation.
[class] ERR \
An error term of a type supplied by the user. The formal grammar of
ERR
is
(err ttype)
The intended semantics are as follows: ERR
represents an error node
currently having no particular feedback but with functionality to be of an
arbitrary type. Note that this is the only STLC
term class which does not
have TTYPE
a possibly empty accessor.
[class] PLUS \
A term representing syntactic summation of two bits
of the same width. The formal grammar of [PLUS
] is
(plus ltm rtm)
where we can possibly supply typing info by
(plus ltm rtm :ttype ttype)
Note that the summation is ranged, so if the operation goes above the bit-size of supplied inputs, the corresponding Vamp-IR code will not verify.
[class] TIMES \
A term representing syntactic multiplication of two bits
of the same width. The formal grammar of [TIMES
] is
(times ltm rtm)
where we can possibly supply typing info by
(times ltm rtm :ttype ttype)
Note that the multiplication is ranged, so if the operation goes above the bit-size of supplied inputs, the corresponding Vamp-IR code will not verify.
[class] MINUS \
A term representing syntactic subtraction of two bits
of the same width. The formal grammar of [MINUS
] is
(minus ltm rtm)
where we can possibly supply typing info by
(minus ltm rtm :ttype ttype)
Note that the subtraction is ranged, so if the operation goes below zero, the corresponding Vamp-IR code will not verify.
[class] DIVIDE \
A term representing syntactic division (floored)
of two bits of the same width. The formal grammar of [DIVIDE
] is
(minus ltm rtm)
where we can possibly supply typing info by
(minus ltm rtm :ttype ttype)
[class] BIT-CHOICE \
A term representing a choice of a bit. The formal
grammar of [BIT-CHOICE
] is
(bit-choice bitv)
where we can possibly supply typing info by
(bit-choice bitv :ttype ttype)
Note that the size of bits matter for the operations one supplies them to. E.g. (plus (bit-choice #1) (bit-choice #00)) is not going to pass our type-check, as both numbers ought to be of exact same bit-width.
[class] LAMB-EQ \
lamb-eq predicate takes in two bit inputs of same bit-width and gives true if they are equal and false otherwise. Note that for the usual Vamp-IR code interpretations, that means that we associate true with left input into bool and false with the right. Appropriately, in Vamp-IR the first branch will be associated with the 0 input and the second branch with 1.
[class] LAMB-LT \
lamb-lt predicate takes in two bit inputs of same bit-width and gives true if ltm is less than rtm and false otherwise. Note that for the usual Vamp-IR code interpretations, that means that we associate true with left input into bool and false with the right. Appropriately, in Vamp-IR the first branch will be associated with the 0 input and the second branch with 1.
[class] MODULO \
A term representing syntactic modulus of the first number
by the second number. The formal grammar of [MODULO
] is
(modulo ltm rtm)
where we can possibly supply typing info by
(modulo ltm rtm :ttype ttype)
meaning that we take ltm mod rtm
Accessors of ABSURD
[accessor] TCOD ABSURD (:TCOD)
An arbitrary type
[accessor] TERM ABSURD (:TERM)
A term of the empty type
Accessors of UNIT
Accessors of LEFT
[accessor] RTY LEFT (:RTY)
Right argument of coproduct type
[accessor] TERM LEFT (:TERM)
Term of the left argument of coproduct type
Accessors of RIGHT
[accessor] LTY RIGHT (:LTY)
Left argument of coproduct type
[accessor] TERM RIGHT (:TERM)
Term of the right argument of coproduct type
Accessors of CASE-ON
[accessor] ON CASE-ON (:ON)
Term of coproduct type
[accessor] LTM CASE-ON (:LTM)
Term in context of left argument of coproduct type
[accessor] RTM CASE-ON (:RTM)
Term in context of right argument of coproduct type
Accessors of PAIR
[accessor] LTM PAIR (:LTM)
Term of left argument of the product type
[accessor] RTM PAIR (:RTM)
Term of right argument of the product type
Accessors of FST
[accessor] TERM FST (:TERM)
Term of product type
Accessors of SND
[accessor] TERM SND (:TERM)
Term of product type
Accessors of LAMB
[accessor] TDOM LAMB (:TDOM)
Domain of the lambda term
[accessor] TERM LAMB (:TERM)
Term of the codomain mapped to given a variable of tdom
Accessors of APP
[accessor] FUN APP (:FUN)
Term of exponential type
[accessor] TERM APP (:TERM)
List of Terms of the domain
Accessors of INDEX
[accessor] POS INDEX (:POS)
Position of type
Accessor of ERR
Accessors of PLUS
Accessors of TIMES
Accessors of MINUS
Accessors of DIVIDE
Accessors of BIT-CHOICE
Accessors of LAMB-EQ
Accessors of LAMB-LT
Accessors of MODULO
This covers the main API for the STLC
module
[generic-function] ANN-TERM1 CTX TTERM
Given a list of SUBSTOBJ
objects with
SO-HOM-OBJ
occurrences replaced by FUN-TYPE
and an STLC
similarly replacing type occurrences of the hom object
to FUN-TYPE
, provides the TTYPE
accessor to all
subterms as well as the term itself, using FUN-TYPE
. Once again,
note that it is important for the context and term to be giving as
per above description. While not always, not doing so result in an error upon
evaluation. As an example of a valid entry we have
(ann-term1 (list so1 (fun-type so1 so1)) (app (index 1) (list (index 0))))
while
(ann-term1 (list so1 (so-hom-obj so1 so1)) (app (index 1) (list (index 0))))
produces an error trying to use. This warning applies to other functions taking in context and terms below as well.
Moreover, note that for terms whose typing needs addition of new context we append contexts on the left rather than on the right contra usual type theoretic notation for the convenience of computation. That means, e.g. that asking for a type of a lambda term as below produces:
LAMBDA> (ttype (term (ann-term1 (lambda (list so1 so0) (index 0)))))
s-1
as we count indices from the left of the context while appending new types to
the context on the left as well. For more info check LAMB
[function] INDEX-CHECK I CTX
Given an natural number I
and a context, checks that the context is of
length at least I
and then produces the Ith entry of the context counted
from the left starting with 0.
[function] FUN-TO-HOM T1
Given a SUBSTOBJ
whose subobjects might have a
FUN-TYPE
occurrence replaces all occurrences of FUN-TYPE
with a
suitable SO-HOM-OBJ
, hence giving a pure
SUBSTOBJ
LAMBDA> (fun-to-hom (fun-type geb-bool:bool geb-bool:bool))
(× (+ GEB-BOOL:FALSE GEB-BOOL:TRUE) (+ GEB-BOOL:FALSE GEB-BOOL:TRUE))
[function] ANN-TERM2 TTERM
Given an STLC
term with a TTYPE
accessor from
ANN-TERM1
- i.e. including possible FUN-TYPE
occurrences - re-annotates the term and its subterms with actual
SUBSTOBJ
objects.
[function] ANNOTATED-TERM CTX TERM
Given a context consisting of a list of SUBSTOBJ
with occurrences of SO-HOM-OBJ
replaced by
FUN-TYPE
and an STLC
term with similarly replaced occurrences
of SO-HOM-OBJ
, provides an STLC
with all
subterms typed, i.e. providing the TTYPE
accessor,
which is a pure SUBSTOBJ
[function] TYPE-OF-TERM-W-FUN CTX TTERM
Given a context consisting of a list of SUBSTOBJ
with
occurrences of SO-HOM-OBJ
replaced by FUN-TYPE
and an STLC
term with similarly replaced occurrences of
SO-HOM-OBJ
, gives out a type of the whole term with
occurrences of SO-HOM-OBJ
replaced by FUN-TYPE
.
[function] TYPE-OF-TERM CTX TTERM
Given a context consisting of a list of SUBSTOBJ
with
occurrences of SO-HOM-OBJ
replaced by FUN-TYPE
and an STLC
term with similarly replaced occurrences of
SO-HOM-OBJ
, provides the type of the whole term,
which is a pure SUBSTOBJ
.
[generic-function] WELL-DEFP CTX TTERM
Given a context consisting of a list of SUBSTOBJ
with occurrences of SO-HOM-OBJ
replaced by
FUN-TYPE
and an STLC
term with similarly replaced
occurrences of SO-HOM-OBJ
, checks that the term
is well-defined in the context based on structural rules of simply
typed lambda calculus. returns the t if it is, otherwise returning
nil
[class] FUN-TYPE DIRECT-POINTWISE-MIXIN CAT-OBJ
Stand-in for the SO-HOM-OBJ
object. It does not have
any computational properties and can be seen as just a function of two arguments
with accessors MCAR
to the first argument and
MCADR
to the second argument. There is an evident canonical
way to associate FUN-TYPE
and SO-HOM-OBJ
pointwise.
[function] ERRORP TTERM
Evaluates to true iff the term has an error subterm.
[function] REDUCER TTERM
Reduces a given Lambda term by applying explicit beta-reduction when possible alongside arithmetic simplification. We assume that the lambda and app terms are 1-argument
These functions deal with transforming the data structure to other data types
One important note about the lambda conversions is that all
transition functions except [TO-CAT
] do not take a context.
Thus if the [<STLC>
] term contains free variables, then call
[TO-CAT
] and give it the desired context before calling
any other transition functions
[method] TO-CAT CONTEXT (TTERM \
Compiles a checked term in said context to a Geb morphism. If the term has
an instance of an error term, wraps it in a Maybe monad, otherwise, compiles
according to the term model interpretation of STLC
[method] TO-POLY (OBJ \
I convert a lambda term into a [GEB.POLY.SPEC:POLY
] term
Note that [<STLC>
] terms with free variables require a context,
and we do not supply them here to conform to the standard interface
If you want to give a context, please call [to-cat] before calling me
[method] TO-BITC (OBJ \
I convert a lambda term into a [GEB.BITC.SPEC:BITC
] term
Note that [<STLC>
] terms with free variables require a context,
and we do not supply them here to conform to the standard interface
If you want to give a context, please call [to-cat] before calling me
[method] TO-SEQN (OBJ \
I convert a lambda term into a [GEB.SEQN.SPEC:SEQN
] term
Note that [<STLC>
] terms with free variables require a context,
and we do not supply them here to conform to the standard interface
If you want to give a context, please call [to-cat] before calling me
[method] TO-CIRCUIT (OBJ \
I convert a lambda term into a vampir term
Note that [<STLC>
] terms with free variables require a context,
and we do not supply them here to conform to the standard interface
If you want to give a context, please call [to-cat] before calling me
These are utility functions relating to translating lambda terms to other types
[function] STLC-CTX-TO-MU CONTEXT
Converts a generic (CODE SUBSTMORPH
. Note that usually contexts can be interpreted
in a category as a $Sigma$-type$, which in a non-dependent setting gives us a
usual PROD
LAMBDA> (stlc-ctx-to-mu (list so1 (fun-to-hom (fun-type geb-bool:bool geb-bool:bool))))
(× s-1
(× (+ GEB-BOOL:FALSE GEB-BOOL:TRUE) (+ GEB-BOOL:FALSE GEB-BOOL:TRUE))
s-1)
[function] SO-HOM DOM COD
Computes the hom-object of two [SUBSTMORPH
]s
Various mixins of the project. Overall all these offer various services to the rest of the project
Here we provide various mixins that deal with classes in a pointwise
manner. Normally, objects can not be compared in a pointwise manner,
instead instances are compared. This makes functional idioms like
updating a slot in a pure manner (allocating a new object), or even
checking if two objects are EQUAL
-able adhoc. The pointwise API,
however, derives the behavior and naturally allows such idioms
[class] POINTWISE-MIXIN
Provides the service of giving point wise operations to classes
Further we may wish to hide any values inherited from our superclass due to this we can instead compare only the slots defined directly in our class
[class] DIRECT-POINTWISE-MIXIN POINTWISE-MIXIN
Works like POINTWISE-MIXIN
, however functions on
[POINTWISE-MIXIN
] will only operate on direct-slots
instead of all slots the class may contain.
Further all DIRECT-POINTWISE-MIXIN
's are [POINTWISE-MIXIN
]'s
These are the general API functions on any class that have the
POINTWISE-MIXIN
service.
Functions like TO-POINTWISE-LIST
allow generic list traversal APIs to
be built off the key-value pair of the raw object form, while
OBJ-EQUALP
allows the checking of functional equality between
objects. Overall the API is focused on allowing more generic
operations on classes that make them as useful for generic data
traversal as LIST
(0
1
)'s are
[generic-function] TO-POINTWISE-LIST OBJ
Turns a given object into a pointwise LIST
(0
1
). listing
the KEYWORD
slot-name next to their value.
[generic-function] OBJ-EQUALP OBJECT1 OBJECT2
Compares objects with pointwise equality. This is a
much weaker form of equality comparison than
STANDARD-OBJECT
EQUALP
, which does the much
stronger pointer quality
[generic-function] POINTWISE-SLOTS OBJ
Works like C2MOP:COMPUTE-SLOTS
however on the object
rather than the class
Let's see some example uses of POINTWISE-MIXIN
:
(obj-equalp (geb:terminal geb:so1)
(geb:terminal geb:so1))
=> t
(to-pointwise-list (geb:coprod geb:so1 geb:so1))
=> ((:MCAR . s-1) (:MCADR . s-1))
Metadata is a form of meta information about a particular object. Having metadata about an object may be useful if the goal requires annotating some data with type information, identification information, or even various levels of compiler information. The possibilities are endless and are a standard technique.
For this task we offer the META-MIXIN
which will allow
metadata to be stored for any type that uses its service.
[class] META-MIXIN
Use my service if you want to have metadata capabilities associated with the given object. Performance covers my performance characteristics
For working with the structure it is best to have operations to treat it like an ordinary hashtable
[function] META-INSERT OBJECT KEY VALUE &KEY WEAK
Inserts a value into storage. If the key is a one time object, then the insertion is considered to be volatile, which can be reclaimed when no more references to the data exists.
If the data is however a constant like a string, then the insertion is considered to be long lived and will always be accessible
The :weak keyword specifies if the pointer stored in the value is weak
[function] META-LOOKUP OBJECT KEY
Lookups the requested key in the metadata table of the object. We look past weak pointers if they exist
The data stored is at the CLASS
level. So having your type take the
META-MIXIN
does interfere with the cache.
Due to concerns about meta information being populated over time, the table which it is stored with is in a weak hashtable, so if the object that the metadata is about gets deallocated, so does the metadata table.
The full layout can be observed from this interaction
;; any class that uses the service
(defparameter *x* (make-instance 'meta-mixin))
(meta-insert *x* :a 3)
(defparameter *y* (make-instance 'meta-mixin))
(meta-insert *y* :b 3)
(defparameter *z* (make-instance 'meta-mixin))
;; where {} is a hashtable
{*x* {:a 3}
*y* {:b 3}}
Since *z*
does not interact with storage no overhead of storage is
had. Further if `x goes out of scope, gc would reclaim the table leaving
{*y* {:b 3}}
for the hashtable.
Even the tables inside each object's map are weak, thus we can make storage inside metadata be separated into volatile and stable storage.
The Utilities package provides general utility functionality that is used throughout the GEB codebase
[type] LIST-OF TY
Allows us to state a list contains a given type.
NOTE
This does not type check the whole list, but only the first element. This is an issue with how lists are defined in the language. Thus this should be be used for intent purposes.
For a more proper version that checks all elements please look at writing code like
(deftype normal-form-list ()
`(satisfies normal-form-list))
(defun normal-form-list (list)
(and (listp list)
(every (lambda (x) (typep x 'normal-form)) list)))
(deftype normal-form ()
`(or wire constant))
Example usage of this can be used with typep
(typep '(1 . 23) '(list-of fixnum))
=> NIL
(typep '(1 23) '(list-of fixnum))
=> T
(typep '(1 3 4 "hi" 23) '(list-of fixnum))
=> T
(typep '(1 23 . 5) '(list-of fixnum))
=> T
Further this can be used in type signatures
(-> foo (fixnum) (list-of fixnum))
(defun foo (x)
(list x))
[function] SYMBOL-TO-KEYWORD SYMBOL
Turns a [symbol] into a [keyword]
[macro] MUFFLE-PACKAGE-VARIANCE &REST PACKAGE-DECLARATIONS
Muffle any errors about package variance and stating exports out of order.
This is particularly an issue for SBCL as it will error when using MGL-PAX
to do the [export] instead of DEFPACKAGE
.
This is more modular thank MGL-PAX:DEFINE-PACKAGE in that this can be used with any package creation function like UIOP:DEFINE-PACKAGE.
Here is an example usage:
(geb.utils:muffle-package-variance
(uiop:define-package #:geb.lambda.trans
(:mix #:trivia #:geb #:serapeum #:common-lisp)
(:export
:compile-checked-term :stlc-ctx-to-mu)))
[function] SUBCLASS-RESPONSIBILITY OBJ
Denotes that the given method is the subclasses responsibility. Inspired from Smalltalk
[generic-function] COPY-INSTANCE OBJECT &REST INITARGS &KEY &ALLOW-OTHER-KEYS
Makes and returns a shallow copy of OBJECT
.
An uninitialized object of the same class as OBJECT
is allocated by
calling ALLOCATE-INSTANCE
. For all slots returned by
CLASS-SLOTS, the returned object has the
same slot values and slot-unbound status as OBJECT
.
REINITIALIZE-INSTANCE
is called to update the copy with INITARGS
.
[macro] MAKE-PATTERN OBJECT-NAME &REST CONSTRUCTOR-NAMES
make pattern matching position style instead of record style. This removes the record constructor style, however it can be brought back if wanted
(defclass alias (<substmorph> <substobj>)
((name :initarg :name
:accessor name
:type symbol
:documentation "The name of the GEB object")
(obj :initarg :obj
:accessor obj
:documentation "The underlying geb object"))
(:documentation "an alias for a geb object"))
(make-pattern alias name obj)
[function] NUMBER-TO-DIGITS NUMBER &OPTIONAL REM
turns an INTEGER
into a list of its digits
[function] DIGIT-TO-UNDER DIGIT
Turns a digit into a subscript string version of the number
[function] APPLY-N TIMES F INITIAL
Applies a function, f, n TIMES
to the INITIAL
values
GEB> (apply-n 10 #'1+ 0)
10 (4 bits, #xA, #o12, #b1010)
These functions are generic lenses of the GEB codebase. If a class is defined, where the names are not known, then these accessors are likely to be used. They may even augment existing classes.
[generic-function] MCDR X
Similar to MCAR
, however acts like a CDR
for
[classes] that we wish to view as a SEQUENCE
[generic-function] OBJ X
Grabs the underlying object
[generic-function] NAME X
the name of the given object
[generic-function] FUNC X
the function of the object
[generic-function] PREDICATE X
the PREDICATE
of the
object
[generic-function] THEN X
the then branch of the object
[generic-function] ELSE X
the then branch of the object
[generic-function] CODE X
the code of the object
We use parachute as our testing framework.
Please read the manual for extra features and how to better lay out future tests
[function] RUN-TESTS &KEY (INTERACTIVE? NIL) (SUMMARY? NIL) (PLAIN? T) (DESIGNATORS '(GEB-TEST-SUITE))
Here we run all the tests. We have many flags to determine how the tests ought to work
(run-tests :plain? nil :interactive? t) ==> 'interactive
(run-tests :summary? t :interactive? t) ==> 'noisy-summary
(run-tests :interactive? t) ==> 'noisy-interactive
(run-tests :summary? t) ==> 'summary
(run-tests) ==> 'plain
(run-tests :designators '(geb geb.lambda)) ==> run only those packages
[function] CODE-COVERAGE &OPTIONAL PATH
generates code coverage, for CCL the coverage can be found at
simply run this function to generate a fresh one