loa

Virtual machine for the Logic of Assumptions
Log | Files | Refs

commit 4427db8f18e506b5b1ab4c23964b3f0518850a98
parent ccde1ec5e431ac13cb0028fa8a9510a41cff333e
Author: Juan F. Meleiro <juan@juanmeleiro.mat.br>
Date:   Sat,  6 Jan 2024 13:35:11 -0300

Add writings and restructure dirs

Diffstat:
A.gitignore | 10++++++++++
Rvm.c -> coding/vm.c | 0
Awriting/2023-05-30-tentative-syntax.ebnf | 6++++++
Awriting/all.do | 2++
Awriting/default.pdf.do | 5+++++
Awriting/journal/2023-11-22-declare-prove-use.lua | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awriting/journal/2023-11-22-declare-prove-use.tex | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awriting/journal/2023-11-29-unification.tex | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awriting/main.tex | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awriting/manage | 12++++++++++++
Awriting/notes/2jd6ltd1j0k0.md | 1+
Awriting/simple-case.lisp | 35+++++++++++++++++++++++++++++++++++
12 files changed, 660 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,10 @@ +*.aux +*.log +*.out +*.bcf +*.run.xml +*.bbl +*.blg +*.rubbercache +*.toc +*.pdf diff --git a/vm.c b/coding/vm.c diff --git a/writing/2023-05-30-tentative-syntax.ebnf b/writing/2023-05-30-tentative-syntax.ebnf @@ -0,0 +1,6 @@ +context = "[" , { assumption } , "]" ; +assumption = name , context , term , proof ; +proof = "{" , { name , proof } , "}" ; +term = "(" , name , term , { term } , ")" | name ; +name = namechar , { namechar } ; +namechar = ? any character ? - ( "[" | "]" | "{" | "}" | "(" | ")" ) ; diff --git a/writing/all.do b/writing/all.do @@ -0,0 +1,2 @@ +redo main.pdf +echo DONE 1>&2 diff --git a/writing/default.pdf.do b/writing/default.pdf.do @@ -0,0 +1,5 @@ +src="$2.tex" +build=.tmp/$(dirname "$2") +mkdir -p "$build" 1>&2 +xelatex -halt-on-error -interaction=nonstopmode -output-directory "$build" "$src" >&2 +cp ".tmp/$1" "$3" 1>&2 diff --git a/writing/journal/2023-11-22-declare-prove-use.lua b/writing/journal/2023-11-22-declare-prove-use.lua @@ -0,0 +1,175 @@ +local pprint = require("pprint").pprint +-- Possible actions: +-- * start subcontext +-- * declare judgment +-- * use declared judgment + +function die(err, msg, ...) + msg = tostring(msg or err) + if err then + io.stderr:write(string.format(msg .. "\n", ...)) + os.exit(1) + end +end + +local test = { + { + method = "down" + }, + { + method = "declare", + params = { + name = "cv", + judgment = { + head = "var", + body = { + { + head = "x" + } + } + } + } + }, + { + method = "up" + }, + { + method = "declare", + params = { + name = "var-wff", + judgment = { + head = "wff", + body = { + { + head = "x" + } + } + } + } + } +} + +local actions = {} + +function actions.down(state) + die(state.subcontext, "Unresolved subcontext") + table.insert(state.theories, {}) + return state +end + +function actions.up(state) + state.subcontext = state.theories[#state.theories] + table.remove(state.theories) + return state +end + +function pressupositions(state, judgment) + -- This is a rabbit hole. Where do pressupositions come from? Two + -- options: 1) we fix the forms of judgments and hard-code the + -- pressupositions; 2) we find a way for the user to create + -- *declaration rules* that work just like assumptions but are + -- only used at top-level declarations. + return {} +end + +function actions.declare(state, params) + die(state.focus, "Proof in progress") + local assumption = { + name = params.name, + conclusion = params.judgment, + subcontext = state.subcontext + } + state.subcontext = nil + table.insert(state.theories[#state.theories], assumption) + state.goals = pressupositions(state, params.judgment) + return state +end + +function prune(state) + while #state.focus.subgoals == 0 do + die(not match(state.focus.goal, state.focus.candidate), "Used assumption does not prove the goal") + if state.focus.goal.head == "var" then + -- oh boy + -- perform unifications + end + if state.focus.parent then + state.focus = state.focus.parent + table.remove(state.focus.subgoals) + else + table.remove(state.goals) + if #state.goals > 0 then + state.focus = state.goals[#state.goals] + else + state.focus = nil + end + return state + end + end + return state +end + +function use(state, assumption) + state.focus.candidate = assumption.conclusion + if #assumption.subcontext > 0 then + for i = #candidate.subcontext, 1, -1 do + table.insert(state.focus.subgoals, candidate.subcontext[i]) + state.focus.subgoals[i].parent = state.focus + end + state.focus = state.focus.subgoals[#state.focus.subgoals] + end +end + +function actions.use(state, name) + die(not state.goals.focus, "Nothing to prove") + + local assumption = state.assets[name] + die(not assumption, "No assumption under that name") + state = use(state, assumption) + state = prune(state) + return state +end + +function interpret(program, state) + for _,c in ipairs(program) do + die(not actions[c.method], "No such action '%s'", c.method) + state = actions[c.method](state, c.params) + end + return state +end + +function judgment2str(judg) + local res = judg.head + if judg.body then + res = res .. "(" + for i = 1, #judg.body do + res = res .. judgment2str(judg.body[i]) + if i < #judg.body then + res = res .. "," + end + end + res = res .. ")" + end + return res +end + +function printassumption(assump, level) + if assump.subcontext then + printtheory(assump.subcontext, level + 1) + end + io.write(string.rep(" ", level) .. assump.name .. " " .. judgment2str(assump.conclusion) .. "\n") +end + +function printtheory(th, level) + level = level or 0 + for _,a in ipairs(th) do + printassumption(a, level) + end +end + +local init = { + goals = {}, + theories = {{}} +} + +local bigres = interpret(test, init) +printtheory(bigres.theories[1]) diff --git a/writing/journal/2023-11-22-declare-prove-use.tex b/writing/journal/2023-11-22-declare-prove-use.tex @@ -0,0 +1,200 @@ +\documentclass[../main.tex]{subfiles} + +\begin{document} + + \section{2023-11-22 Declare, prove, and use} + + In the Logic of Assumptions, judgments appear under three + different \emph{moods}. That is, these syntactical objects wich + express judgments, without any commitment, can appear or be + refered to in places where they take on different meanings. + + The three moods are those of \definiendum{declaration}, + \definiendum{assertion}, and \definiendum{use}. + + There is also an interplay between them. The three moods grant + rights and impose demands (under the general obligation of the + user to say coherent things), which involve the other moods. + + It works like this: + + \begin{description} + \item[Declare] When one declares a judgment, one is taking it + to be true. This can be read in a few ways. For one, it can be + thought of as a \emph{postulate} -- \ie, the assertion that, + pragmatically, from now on, we'll take that judgement to the + the case. On the other hand, hooking into the double meaning + of theories in the Logic of Assumptions, we can understand a + declaration as a specification of part of the interface such a + theory is describing -- \ie, from then on, we're talking about + contexts which implement this judgment. + + To declare something, one must \emph{prove} its + pressupositions. So, here, in order to \emph{be able} to declare, + one \emph{is required} to prove. + + \item[Proof] To prove a judgment (in a collection of contexts) + means to establish that context implements that judgment. Or, in + the dual reading, that under the assumptions from such context, + we can conclude the given judgment. And, since judgments are + hypothetical, this means one can draw the conclusion of the + judgment if one has its hypothesis. + + Proof is impositional. One, at least intentionally speaking, + doesn't \emph{choose} to prove. One is \emph{required} to do + so. But the counterpart is this: when such imposition is made, + we also get the right to \emph{use} the assumptions of the + judgment, besides the given context. The movement is ``assume + the hypotheses, show the conclusion'', and so the mood of proof + enables the mood of use. + + \item[Use] To use a judgment means to refer to it when it is + already established -- that is, declared. But \emph{use}, in this + sense, means to apply judgments as rules. That is, if we wish + to use the conclusion (\eg, in the course of an ongoing proof), + one must first \emph{prove} the hypotheses of the judgment under + the current context. So \emph{use} demands both \emph{proof} + and \emph{declaration}. + + \end{description} + + To summarize: + + \begin{itemize} + \item To declare, one must prove (the pressupositions). + \item To prove, one \emph{can} use. + \item To use, one must prove (and have declared). + \end{itemize} + + In practice, the flow of theory development is a sequence + of declarations. On each declaration, we prove the + pressupositions. And then we alternate between \emph{having to + prove} judgmenets, thereby enabling the use of its hypotheses, + and \emph{being able to use} judgments, which requires us to + prove its hypotheses. + + \subsection{Implementation} + + Let's think of the data structures needed to keep track of all these needs ands cans. + + When building a theory, on the top level, we are always declaring + judgments. On the context, we have two structures. + + The first is the \definiendum{goal tree}, which is a tree made + of judgments. If the tree is not empty, the root represents the + judgment currently being declared, and it is a \definiendum{use + node}. Goal nodes have zero or one child, which (if it exists) + represent the judgment that is being used to prove that + goal. The children of a used judgment are the goals that it + entails. Additionally, the forest has a \emph{pointer}. + + The other is for \definiendum{assets}, or things which we've + gotten granted the right of using, and it is an assoctiation + list between names and judgments (with links to the places + in the goal tree where they were introduced). + + When focusing on something to prove, or when using something, + those lists will be modified. + + So, at any point in top-level, we've got an empty tree of + goals (\ie, we have a valid theory so far), and all previous + declarations show up as assets. Here's how states work: + depending on the goal tree, the user has some valid actions, + and each action generates some changes to the state. + + \begin{tabular}{lcr} + Condition & Actions & Parameters \\ + \hline + The tree is empty & Declare a judgment & A judgment \\ + The focused goal has a candidate & Focus on a subgoal & An index \\ + The focused goal has no candidate & Use an asset to prove the goal & A name + \end{tabular} + + Here's what the actions do: + + \begin{description} + \item[Declare] Place each pressuposition of the judgment as a + root in the goals tree, with no candidate. + + \item[Focus] Add that subgoal's assumptions the assets table, + with a link to the subgoal. Focus on the subgoal. + + \item[Use] Set the given name as the candidate for the focused + goal. If the name refers to an asset with assumptions, place those + assumptions as children of the current goal with no candidates. If + it does not, and it matches the focused goal, simply remove the + focused goal, remove assets introduced at that goal, and shift + focus to its parent. If instead it does not match, raise an error. + \end{description} + + There's one thing missing from this description, which is + how to build subcontexts. Ideally, those would come from the + same process as the top-level context. And, actually, even the + top-level context can reasonably be expected to be packaged in + much the same way as the subcontexts. So, this suggests the + following approach, analogous to how we are handling goals: + build a context, then package it up into a subcontext of a new + goal. Repeat. + + One choice: we have to either signal where we are \emph{starting} + a new subcontext (which drops us into the children of a + yet-to-be-defined judgment), or we can say, when \emph{closing} + it, where it starts (perhaps by refering to a label). The latter + is kinda clumsy and not very readable. So let's go for the first. + + An alternative to the use-focus duo, and which would be terser, + would be to automatically focus on the first subgoal, when + applicable. An example of syntax for this second style could be + like this: + + \begin{lstlisting} + #syntax suffix wff + #syntax infix : + #syntax suffix wft + + { // start subcontext + [cv] x var + } [var-wff] x wff + + { + [cc] c const // No pressups + } [cw] c wff; + + [tc] type const // No pressup + [tt] type wft // Pressuposition: `type wff` + cw // Adds cc as subgoal + tc // Solves it, unifies `c` and `type` + + + { + [av] a var // No pressupositions + [at] a : type // Pressup `type wft`, `a wff` + tt // Solves first + var-wff // Adds cv + av // Solves it, unifies + } [type-wft] a wft + \end{lstlisting} + + \subsection{Discussion} + + The way we are approaching this is not the way its usually done + in logic, but rather in the narrow field of proofs assistants. In + the end, its the same thing: in logic, we describe these pristine + objects, regardless of how they could be built, and provide + validity conditions that distinguish them among the many others + syntactically correct, but invalid siblings. + + In proof assistants, we describe \emph{programs} that run on + machines which \emph{build} such objects. A valid object, then, is + one that can be built by this machine using a particular program. + + It's the same thing with grammars and automata. + + The objects that we are describing are the contexts in the Logic + of Assumptions. They are a dependent list of assumptions, each + possibly using and refering to the previous ones, each carrying + with it the necessary proofs of its pressupositions. But, + of course, its easier to think about how one would go about + \emph{building} such a thing. + +\end{document} diff --git a/writing/journal/2023-11-29-unification.tex b/writing/journal/2023-11-29-unification.tex @@ -0,0 +1,65 @@ +\documentclass[../main.tex]{subfiles} + +\begin{document} + + \section{2023-11-29 Unification in LoA proofs} + + At a particular point during interaction with the LoA assistant, + when we finally apply to a goal an assumption without + subassumptions, the system will perform the simplification + procedure on the goal tree. That procedure involves recursively + (depth-first) checking that each applied assumption indeed matches + the goal at that level. That is, \emph{after} substitution. And that + is the snag. Substitution, at this point, is what ends up demanding + of us that we bake variable bindings onto terms; of course, + otherwise, we'd have no way of knowing which variables we \emph{can} + and which variables we \emph{cannot} replace. + + Let's look at this example: + + \begin{lstlisting} + { + [tp] t param + [xp] x param + [xv] x var + xp + [tt] t term + tp + } [lambda-term] lam(x, t) term + \end{lstlisting} + + If we want to prove that \lstinline{lam(x, x) term}, we'd proceed as + follows: + + \begin{lstlisting} + { + [yp] y param + [yv] y var + yp + } ! id-term lam(y, y) term + lambda-term // push `lam(y, t) term` as candidate + // First subgoal: `t param` + var-term // Use `y term`; subgoal: `y var` + yv // Substitution: `t := y` + // Second subgoal: `y param` + var-term + yv // Substitution: `y := y` + // Third subgoal: `y var` + yv + // Fourth subgoal: `y term` + var-term + yv + \end{lstlisting} + + The variable is a parameter on the assumption as well. Otherwise, + we'd have no way of using other variables that are not the one + mentioned verbatim on the subcontext of the assumption. That is, + things that are not parameters work almost like punctuation. + + The above proof, compacted, would read: + + \begin{lstlisting} + { @ yp (param y) @ yv (var y) yp } ! id-term (term (lam y y)) lambda-term var-term yv var-term yv yv var-term yv + \end{lstlisting} + +\end{document} diff --git a/writing/main.tex b/writing/main.tex @@ -0,0 +1,149 @@ +\documentclass{article} + +\usepackage[english]{personal} + +\setmainfont{EB Garamond} +% \setmonofont{} +% \setsansfont{} +\setmainlanguage{english} + +\title{A Logic of Assumptions} +\author{Juan F. Meleiro} +\date{2023} + +\begin{document} + + \maketitle + + A logical system is usually composed of a series of data. At least a type of signatures; and for each signature a type of judgments; and for each judgment a type of proofs; or alternativelly a notion of (in)compatibility between the judgments; etc. + + But could we subsume all this concepts into a single one? Yes, I believe, and that would be the concept of \emph{assumption}. More specifically, of \emph{relative assumptions}. Let's get straight into it. + + The system is composed of two layers. The first, purely combinatorial. It has two concepts, which define just the plain syntactical objects (grammar notwithstanding). Those are \definiendum{terms} and \definiendum{judgment forms}. + + Then, the second layer, properlyl logical. It contains five interconected concepts, the most substantial of which is the \definiendum{assumption}, as we said. Assumptions are organized into \definiendum{contexts}, which relate to one another via \definiendum{translations}, which can be \definiendum{applied} to terms, and that is the basis of \definiendum{proofs}, which finally make assumptions well-formed. + + So, schematically, + + \begin{center} + \parbox{\textwidth}{ + \framebox[0.5\textwidth]{Terms} + \framebox[0.5\textwidth]{Judgments} + } + \parbox{\textwidth}{ + \framebox[0.2\textwidth]{Assumptions} + \framebox[0.2\textwidth]{Contexts} + \framebox[0.2\textwidth]{Translations} + \framebox[0.2\textwidth]{Application} + \framebox[0.2\textwidth]{Proofs} + } + \end{center} + + \section{Definitions} + + We start by supposing a fixed type for symbols \note{which must include three distinguished symbols called $\mathsf{symb}$, $\mathsf{var}$, and $\mathsf{judg}$}. + + \begin{definition}[Terms] + \definiendum{Terms} are labelled trees of symbols. That is, a symbol called its \definiendum{head} plus a list of terms called its \definiendum{subterms}. + \end{definition} + + \begin{definition}[Judgment forms] + \definiendum{Judgment forms} are a family of symbols associated with arities. Each judgment form has some \definiendum{pressupositions} which are other judgment forms using the same arguments. + \end{definition} + + That's it for syntax. + + Now for the logic. + + \begin{definition}[Context] + A \definiendum{context} is either empty or a context (called its \definiendum{precontext}) and an assumption over it. + \end{definition} + + \begin{definition}[Assumption] + An \definiendum{assumption} over a context is a judgment, a context (called its \definiendum{subcontext}), plus proofs for its pressupositions under the given context and the subcontext. + \end{definition} + + \begin{definition}[Proof] + A \definiendum{proof} of a judgment in a collection of contexts is an assumption in one of them, plus a morphism from its subcontext into the collection. + \end{definition} + + \begin{definition}[Morphism] + A morphism from a context (the \definiendum{domain}) into a collection of contexts (the \definiendum{codomain}) is + \begin{itemize} + \item If the domain is empty, nothing. + \item If the domain is a precontext and an assumption, a morphism from that precontext to the codomain plus a function taking a morphism from the assumptions subcontext to the codomain to a proof in the codomain of the translation of the assumption's judgment under the precontext morphism. + \end{itemize} + \end{definition} + + \begin{definition}[Translation] + The translation of a judgment via a morphism is that same judgment form applied to the translated terms. A term translated via a morphism is the term associated with its head symbol with the translated subterms substituted into the variables. + \end{definition} + + \section{Examples} + + Let's build modus ponens. Our judgments are unary $\mathsf{true}$, unary $\mathsf{prop}$, plus the standard ones $\mathsf{symb}$ and $\mathsf{var}$. $\mathsf{true}$ judgments pressupose $\mathsf{prop}$ ones, and $\mathsf{prop}$ ones pressupose $\mathsf{var}$ ones (this means \emph{not} that all propositions are variables, but that to \emph{assume} something is a proposition, it must be known to be a variable). + + \begin{lstlisting}[ + mathescape=true, + literate={=>}{{$\Rightarrow$}{ }}{2}{:=}{{$\defeq$}}{1}{->}{{$\to$}{ }}{2}, + basicstyle=\sffamily, + ] + var2prop { + var$_\alpha$ {} $\alpha$ var + triv + } $\alpha$ prop + + fn-form { + var$_\alpha$ {} $\alpha$ var + triv + var$_\beta$ {} $\beta$ var + triv + prop$_\alpha$ {} $\alpha$ prop + prop$_\beta$ {} $\beta$ prop + } $\alpha$ -> $\beta$ prop + + mp { + var$_\alpha$ {} $\alpha$ var + triv + var$_\beta$ {} $\beta$ var + triv + maj {} $\alpha$ -> $\beta$ true + fn-form + var$_\alpha$ => $\alpha$ + var$_\beta$ => $\beta$ + prop$_\alpha$ => var2prop + var$_\alpha$ => $\alpha$ + prop$_\beta$ => var2prop + var$_\alpha$ => $\alpha$ + min {} $\alpha$ true + var2prop + var$_\alpha$ => $\alpha$ + } $\beta$ true + var2prop + var$_\alpha$ => @var$_\beta$ + + dt { + var$_\beta$ {} $\beta$ var + triv + var$_\alpha$ {} $\alpha$ var + triv + prop$_\beta$ {} $\beta$ prop + var$_\beta$ + prop$_\alpha$ {} $\alpha$ prop + var$_\alpha$ + hyp { + cond {} $\alpha$ true + prop$_\alpha$ + } $\beta$ true + prop$_\beta$ + } $\alpha$ -> $\beta$ true + fn-form + var$_\alpha$ => @var$_\alpha$ + var$_\beta$ => @var$_\beta$ + prop$_\alpha$ => var2prop + var$_\alpha$ => @var$_\alpha$ + prop$_\beta$ => var2prop + var$_\alpha$ => @var$_\beta$ + \end{lstlisting} + +\end{document} diff --git a/writing/manage b/writing/manage @@ -0,0 +1,12 @@ +#!/usr/bin/fish +switch $argv[1] + case monitor + fd --extension tex --extension bib | entr redo + case workspace + rubber -m xelatex main.tex + r main.pdf >/dev/null 2>&1 & + emacsclient -c -n main.tex & + ./manage monitor + case clean + rm -f *.{aux,log,out,bcf,run.xml,bbl,blg,nav,rubbercache,snm,toc,vrb} +end diff --git a/writing/notes/2jd6ltd1j0k0.md b/writing/notes/2jd6ltd1j0k0.md @@ -0,0 +1 @@ +Declaring a judgement means from then on, when you have to prove it, you can use its subjudgements, and you must prove its subjudgements in order to use it. diff --git a/writing/simple-case.lisp b/writing/simple-case.lisp @@ -0,0 +1,35 @@ +(assums (a x) (var x)) +(assums (b x) (var x)) +(assums (c x) (var x)) + +;; CTX := (NAME [ASSUM]) +;; ASSUM := (NAME CTX EXPR [PROOF]) +;; PROOF := (NAME => (NAME [PROOF])) + +(careful + + (a2b (vx () (var x) () + aa () (a x) (vx)) + (b x)) + + (b2c (vx () (var x) () + ab () (b x) (vx)) + (c x))) + +(hasty + + (a2c (aa () (a x)) + (c x))) + +(implement hasty careful + (a2c b2c + (ab a2b + (a2c.aa)))) + +(prove hasty.a2c ; goals: [a2c] + careful.b2c ; goals: [careful.b2c.vx, careful.b2c.ab] + triv ; goals: [careful.b2c.ab] + careful.a2b ; goals: [careful.a2b.vx, careful.a2b.aa] + triv ; goals: [careful.a2b.aa] + hasty.a2c.aa ; goals: [] + )