LeanElpi

Getting started

LeanElpi ships as a Lake package for Lean 4 and as a command-line program. This chapter builds both and runs the same small program three ways.

Build it

You need Lean 4 via elan. The project pins its toolchain in lean-toolchain.

git clone https://github.com/Volland/lean-elpi
cd lean-elpi
lake build

The build produces the library and the lean-elpi executable at .lake/build/bin/lean-elpi.

Use it from your own Lake project

Add it as a dependency in your lakefile.lean:

require «lean-elpi» from git "https://github.com/Volland/lean-elpi" @ "v0.1.0"

Then import Elpi in any Lean file.

A first program

Save this as family.elpi:

kind person type.
type ann, bob, carl person.

pred parent o:person, o:person.
parent ann bob.
parent bob carl.

pred ancestor o:person, o:person.
ancestor X Y :- parent X Y.
ancestor X Z :- parent X Y, ancestor Y Z.

main :- ancestor ann carl, print "ann is an ancestor of carl".

1. As a standalone program

.lake/build/bin/lean-elpi family.elpi -test

-test runs the predicate main. You can also run any predicate with -exec, or start the interactive loop by giving no query:

printf 'ancestor ann X.\n' | .lake/build/bin/lean-elpi family.elpi

The REPL prints each answer and asks whether you want more.

2. Inside Lean

import Elpi
open Elpi.Cmd

elpi_program family
elpi_accumulate family lp%{
  kind person type.
  type ann, bob, carl person.
  pred parent o:person, o:person.
  parent ann bob.
  parent bob carl.
  pred ancestor o:person, o:person.
  ancestor X Y :- parent X Y.
  ancestor X Z :- parent X Y, ancestor Y Z.
}%

#elpi family lp%{ ancestor ann X }%

Lean prints X = bob. The block lp%{ … }% holds raw ELPI source; errors inside it are reported at the right line of your Lean file. #elpi shows one answer by default; set elpi.solutions to see more.

3. As a library

import Elpi
open Elpi

#eval show IO Unit from do
  match Program.load "p 1. p 2." with
  | .ok prog =>
    for r in (solve prog "p X").toList do
      match r with
      | .ok s => IO.println s
      | .error e => IO.println s!"{e}"
  | .error e => IO.println s!"{e}"

solve returns a lazy stream of answers, so asking for the first one costs only the first one. This prints X = 1 and X = 2.

What next

The next chapters teach the language from the ground up. If you already know Prolog, skim Terms, clauses and search and go straight to Higher-order programming.