Telegram bot for proof assistants.
Bridge between Telegram Bot and several proof assistants.
Currently following proof assistant supported: Agda, Arend, Coq, Idris 2, Lean, Rzk
See README.md for more details.
Proof Assistant Bot
Description
This bot provides limited Telegram interfaces to following proof assistant programs (in order of implementation):
- Coq
- Agda
- Idris 2
- Lean
- Arend
- Rzk
Installation
- Bot could be built via following commands:
cabal update
cabal build
cabal install --overwrite-policy=always
- To launch bot you need to set environmental variables, see
./config/settings.dhallfor more details.
One of them is PROOF_ASSISTANT_BOT_TOKEN. Obtain it via @BotFather and set it:
export PROOF_ASSISTANT_BOT_TOKEN="..."
- Another environmental variable that unfortunately needed is
NIX_PROFILE. Please add it as
export NIX_PROFILE="$HOME/.nix-profile"
Coq
- Install
opam >= 2.1.0, e.g. from here.
cd $HOME
opam --version
2.1.3
- Install
Coq.
cd $HOME
opam init
eval $(opam env)
opam pin add coq 8.16.1
- Locate
coqtopand set enviromental variable. Should be similar to:
export COQ_BIN_PATH="$HOME/.opam/default/bin/coqtop"
Agda
We do not need to worry about Agda since it is included in package dependencies. It will be installed automatically. Meantime, Agda standard library should be installed manually.
Get
agda-stdlibfrom Github.Unpack archive.
mkdir -p $PROOF_ASSISTANT_BOT_DIR/agda
cp agda-stdlib-1.7.1.tar.gz $PROOF_ASSISTANT_BOT_DIR/agda
cd $PROOF_ASSISTANT_BOT_DIR/agda
tar -xzvf agda-stdlib-1.7.1.tar.gz
export AGDA_STDLIB_PATH=$PROOF_ASSISTANT_BOT_DIR/agda/agda-stdlib-1.7.1
- Create file
$HOME/.agda/defaultswith following content:
standard-library
- Create file
$HOME/.agda/librarieswith following content:
$AGDA_STDLIB_PATH/standard-library.agda-lib
Idris 2
Get
nixfrom nixos.org.Install
idris2vianix:
nix-env -i idris2
- Set environmental variable:
export IDRIS2_BIN_PATH="$HOME/.nix-profile/bin/idris2"
Lean
Get
nixfrom nixos.org.Install
leanvianix:
nix-env -i lean
- Install
leanprojectvianix:
nix-env -i mathlibtools
Run
leanproject new lean.Set
LEAN_BIN_PATHenvironmental variable:
export LEAN_BIN_PATH="$HOME/.nix-profile/bin/lean"
- Set
LEAN_PROJECT_PATHto the newly created project directory.
Arend
Get
nixfrom nixos.org.Get
javaandopenjdk17vianix:
nix-env -i openjdk-17.0.4+8
Set
JAVA_HOMEenvironment variable to your openjdk location. You can usereadlink $HOME/.nix-profile/bin/javaand strip/bin/javafrom the end.Create project directory to store arend projects (for different Telegram chats) and set
AREND_ROOT_PROJECT_DIR.Get Arend standard library from the official site and store in
${AREND_ROOT_PROJECT_DIR}/libs.Point
AREND_STDLIB_PATHenvironment variable to the same location asAREND_ROOT_PROJECT_DIR.Download
Arend.jarand setAREND_PATHenvironment variable to its location, e.g.
export AREND_PATH="${AREND_ROOT_PROJECT_DIR}/Arend.jar"
Rzk
No actions required. See cabal.project for more details.
Alloy 6 / Alloy CLI Wrapper
You can read about CLI Wrapper here: https://github.com/AlloyTools/org.alloytools.alloy/issues/211
Get
nixfrom nixos.org.Get
javaandopenjdk17vianix:
nix-env -i openjdk-17.0.4+8
Set
JAVA_HOMEenvironment variable to your openjdk location. You can usereadlink $HOME/.nix-profile/bin/javaand strip/bin/javafrom the end.Get
alloy6vianix:
nix-env -i alloy6
Create project directory to store Alloy projects (for different telegram chats) and set
ALLOY_PROJECT_DIR.Create directory for Alloy CLI wrapper and prepare Alloy CLI Wrapper:
mkdir -p $ALLOY_PROJECT_DIR/bin
cd $ALLOY_PROJECT_DIR
# Download Alloy CLI Wrapper
curl https://gist.githubusercontent.com/swamp-agr/560f0d9bf8dc034f99d6055a5a197285/raw/5b547616063ba834bfa2987bc1eb539f1ec8088d/Main.java > bin/Main.java
javac -cp "$NIX_PROFILE/share/alloy/*" -Xlint:all bin/Main.java
# Test: should be empty output and exit code 0
java -cp $NIX_PROFILE/share/alloy/alloy6.jar:$ALLOY_PROJECT_DIR/bin Main
- Set up
graphvizandimagemagickfor generating plots based on Alloy CLI Wrapper output:
nix-env -i graphviz imagemagick
- Set
ALLOY_PATHenvironment variable to$NIX_PROFILE/share/alloy/alloy6.jar.
Usage
- Coq supports only typecheck of the user input via
/coqcommand. Example:
/coq Inductive day : Type :=
| monday
| tuesday
| wednesday
| thursday
| friday
| saturday
| sunday.
Definition next_weekday (d:day) : day :=
match d with
| monday => tuesday
| tuesday => wednesday
| wednesday => thursday
| thursday => friday
| friday => monday
| saturday => monday
| sunday => monday
end.
Compute (next_weekday friday).
Compute (next_weekday (next_weekday saturday)).
Example test_next_weekday:
(next_weekday (next_weekday saturday)) = tuesday.
Proof. simpl. reflexivity. Qed.
Agda is available via
/agdacommand. Bot supports several subcommands for Agda:/agda /load <input>. E.g.
/agda /load import Relation.Binary.PropositionalEquality as Eq open Eq using (_≡_; refl) open Eq.≡-Reasoning using (begin_; _≡⟨⟩_; _∎) data ℕ : Set where zero : ℕ suc : ℕ → ℕ {-# BUILTIN NATURAL ℕ #-} _+_ : ℕ → ℕ → ℕ zero + n = n (suc m) + n = suc (m + n) _ : 2 + 3 ≡ 5 _ = begin 2 + 3 ≡⟨⟩ -- is shorthand for (suc (suc zero)) + (suc (suc (suc zero))) ≡⟨⟩ -- inductive case suc ((suc zero) + (suc (suc (suc zero)))) ≡⟨⟩ -- inductive case suc (suc (zero + (suc (suc (suc zero))))) ≡⟨⟩ -- base case suc (suc (suc (suc (suc zero)))) ≡⟨⟩ -- is longhand for 5 ∎/agda /reload/agda /typeOf <expr>. E.g./agda /typeOf suc./agda <expr>. E.g./agda suc zero + suc zero.
Idris 2 via
/idris2command. Bot supports several subcommands for Idris 2:/idris2 /load <input>. E.g.
/idris2 /load module Main import System.File.ReadWrite tryPrint : Either FileError String -> IO () tryPrint (Left _) = putStrLn "error" tryPrint (Right r) = putStrLn r main : IO () main = do c <- readFile "hello.idr" tryPrint c/idris2 /typeOf <expr>. E.g./idris2 /typeOf Nat./idris2 <expr>. E.g./idris2 2 + 3.
Lean is available via
/leancommand. Typecheck supported for the user input. Only several modes were tested (calc mode, conv mode, simplifier).- Example 1:
/lean import data.nat.basic variables (a b c d e : ℕ) variable h1 : a = b variable h2 : b = c + 1 variable h3 : c = d variable h4 : e = 1 + d theorem T : a = e := calc a = b : h1 ... = c + 1 : h2 ... = d + 1 : congr_arg _ h3 ... = 1 + d : add_comm d (1 : ℕ) ... = e : eq.symm h4- Example 2:
/lean import topology.basic #check topological_space- Example 3:
/lean import algebra.group.defs variables (G : Type) [group G] (a b c : G) example : a * a⁻¹ * 1 * b = b * c * c⁻¹ := begin simp endArend is available via
/arendcommand. Only typecheck supported.- Example:
/arend \func f => 0Rzk is available via
/rzkcommand. Typechecker supported for every language.- Example:
/rzk #lang rzk-1 #def prod : (A : U) -> (B : U) -> U := \A -> \B -> ∑ (x : A), B #def isweq : (A : U) -> (B : U) -> (f : (_ : A) -> B) -> U := \A -> \B -> \f -> ∑ (g : (_ : B) -> A), prod ((x : A) -> g (f x) =_{A} x) ((y : B) -> f (g y) =_{B} y) #def weq : (A : U) -> (B : U) -> U := \A -> \B -> ∑ (f : (_ : A) -> B), isweq A B f #def Theorem-4.1 : (I : CUBE) -> (psi : (t : I) -> TOPE) -> (phi : {(t : I) | psi t} -> TOPE) -> (X : U) -> (Y : <{t : I | psi t} -> (x : X) -> U >) -> (f : <{t : I | phi t} -> (x : X) -> Y t x >) -> weq <{t : I | psi t} -> (x : X) -> Y t x [phi t |-> f t]> ((x : X) -> <{t : I | psi t} -> Y t x [phi t |-> f t x]>) := \I -> \psi -> \phi -> \X -> \Y -> \f -> (\k -> \x -> \t -> k t x, (\k -> \{t : I | psi t} -> \x -> (k x) t, (\k -> refl_{k}, \k -> refl_{k}))) #def Theorem-4.2_uncurry_ext : (I : CUBE) -> (J : CUBE) -> (psi : (t : I) -> TOPE) -> (zeta : (s : J) -> TOPE) -> (X : <{t : I | psi t} -> <{s : J | zeta s} -> U> >) -> (chi : {(t : I) | psi t} -> TOPE) -> (phi : {(s : J) | zeta s} -> TOPE) -> (f : <{(t, s) : I * J | psi t /\ zeta s} -> X t s >) -> (_ : <{t : I | psi t} -> <{s : J | zeta s} -> X t s [chi s |-> f (t, s)]> [phi t |-> \s -> f (t, s)]>) -> <{(t, s) : I * J | psi t /\ zeta s} -> X t s [(phi t /\ zeta s) \/ (psi t /\ chi s) |-> f (t, s)]> := \I -> \J -> \psi -> \zeta -> \X -> \chi -> \phi -> \f -> \k -> \(t, s) -> k t sAlloy is available via
/alloycommand.- Example:
/alloy open util/ordering[Id] sig Node { id : one Id, succ : one Node, var inbox : set Id, var outbox : set Id } sig Id {} fact ring { all i : Id | lone id.i all n : Node | Node in n.^succ } var sig elected in Node {} fact elected { always { elected = {n : Node | once (n.id in n.inbox)} } } enum Event { Send, Compute } pred send [n : Node] { some i : n.outbox { n.outbox' = n.outbox - i n.succ.inbox' = n.succ.inbox + i } all m : Node - n.succ | m.inbox' = m.inbox all m : Node - n | m.outbox' = m.outbox } fun send : Event -> Node { Send -> { n : Node | send[n] } } pred compute [n : Node] { some i : n.inbox { n.inbox' = n.inbox - i n.outbox' = n.outbox + (i - n.id.*(~next)) } all m : Node - n | m.inbox' = m.inbox all m : Node - n | m.outbox' = m.outbox } fun compute : Event -> Node { Compute -> { n : Node | compute[n] } } fun events : set Event { (send+compute).Node } pred skip { inbox' = inbox outbox' = outbox } fact init { no inbox outbox = id } fact behaviour { always (skip or some n : Node | send[n] or compute[n]) } run {} for 4 but exactly 4 Node, 10 steps run example {eventually some elected} for 3 but exactly 3 Node, 6 steps assert safety { always lone elected } check safety for 3 but 15 steps pred sendEnabled [n : Node] { some n.outbox } pred computeEnabled [n : Node] { some n.inbox } pred fairness { always (all n : Node | (always sendEnabled[n] implies eventually send[n])) always (all n : Node | (always computeEnabled[n] implies eventually compute[n])) } assert liveness { fairness and some Node implies eventually some elected } check liveness for 3- As result you can see:

Available instances
- @ProofBot (online)
- @ProofDebugBot (for debug purpose, offline most of the time)
Acknowledgements
- PLTT Community
- Nikolay Kudasov
- Andrey Borzenkov
- Matúš Tejiščák
- My wife.