• Stars
    star
    146
  • Rank 252,769 (Top 5 %)
  • Language
    Haskell
  • License
    MIT License
  • Created almost 8 years ago
  • Updated about 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Let GHC prove program equations for you

Prove program equations with GHC

This GHC plugin allows you to embed code equation into your code, and have them checked by GHC.

Synopsis

See the GHC.Proof module for the documentation, but there really isn't much more to it than:

{-# OPTIONS_GHC -O -fplugin GHC.Proof.Plugin #-}
module Simple where

import GHC.Proof
import Data.Maybe

my_proof1 = (\f x -> isNothing (fmap f x))
        === (\f x -> isNothing x)

If you compile this, you will reassurringly read:

$ ghc Simple.hs
[1 of 1] Compiling Simple           ( Simple.hs, Simple.o )
GHC.Proof: Proving my_proof1 …
GHC.Proof proved 1 equalities

See the examples/ directory for more examples of working proofs.

If you have proof that GHC cannot prove, for example

not_a_proof = (2+2::Int) === (5::Int)

then the compiler will tell you so, and abort the compilation:

$ ghc Simple.hs
[1 of 1] Compiling Simple           ( Simple.hs, Simple.o )
GHC.Proof: Proving not_a_proof …
Proof failed
    Simplified LHS: GHC.Types.I# 4#
    Simplified RHS: GHC.Types.I# 5#
    Differences:
    • 4# /= 5#

Simple.hs: error: GHC.Proof could not prove all equalities

How does it work?

GHC is a mighty optimizing compiler, and the centerpiece of optimizing, the simplifier is capable of quite a bit of symbolic execution. We can use this to prove program equalities, simply by taking two expressions, letting GHC simplify them as far as possible. If the resulting expressions are the same, then the original expressions are – as far as the compiler is concerned – identicial.

The GHC simplifier works on the level of the intermediate language GHC Core, and failed proofs will be reported as such.

The gory details are as follows: The simplifier is run 8 times, twice for each of the simplifier phases 4, 3, 2 and 1. In between, the occurrence analiser is run. Near the end, we also run common-subexpression elimination.

The simplifier is run with more aggressive flags. In particular, it is instructed to inline functions aggressively and without worrying about code size.

Why is this so great?

  • You can annotate your code with proofs, in the same file, in the same language, without extra tools (besides the plugin).
  • The proofs stay with the code and are run with every compilation.
  • The proof goes through when the compiler thinks the expressions are the same. There is no worry about whether an external proof tools captures the semantics of GHC’s Haskell precisely.
  • It suports, in principle, all of Haskell’s syntax, including a huge number of extensions.
  • It is super easy (if it works).
  • Using rewrite rules allows proofs with regard to some theory (e.g. with regard to equations about foldr and other list combinators), independent of whether these are proven.
  • It works with the 8.2, 8.4 and 8.6 versions of the GHC compiler.

Why is this not so great?

  • It can only prove quite simple things right now.
  • Even for easy things, the proof might fail because GHC simply simplifies the expressions slightly different, and there is not always an easy way of fixing this.
  • The proofs depend on optimization flags.
  • There is no guarantee that the next GHC release will be able to prove the same things.
  • Failed proofs are reported in GHC Core instead of Haskell.

What can it prove?

Not everything. By far.

But some nice, practical results work, see for example the proofs of the Applicative and Monad laws for Control.Applicative.Succs.

Best results were observed with compositions of non-recursive functions that handle non-recursive data or handle lists usind standard list combinators.

The GHC simplifier generally refuses to inline recursive functions, so there is not much we can do with these for now.

My proof is not found. What can I do?

The plugin searches for top-level bindings of the form

somename = proof expression1 expression2

or

somename = expression1 === expression2

GHC will drop them before the plugin sees them, though, if somename is not exported, so make sure it is exported. If you really do not want to export it, then you can keep it alive using the trick of

{-# RULES "keep somename alive" id somename = somename #-}

If it still does not work, check the output of -dverbose-core2core for why your binding does not have the expected form. Maybe you can fix it somehow.

My proof does not go through. Can I fix that?

Maybe. Here are some tricks that sometimes help:

  • Check if your functions are properly unfolded in the proof. Maybe an INLINEABLE pragma helps.

  • Use {-# LANGUAGE BangPatterns #-} and mark some arguments as strict:

    my_proof2 = (\d !x -> fromMaybe d x)
            === (\d !x -> maybe d id x)

    GHC makes these functions strict by putting the body in a case. This has roughly the same effect as s case split in interactive theorem proving.

  • Allow GHC to assume one of your functions is strict:

    str :: (a -> b) -> (a -> b)
    str f x = x `seq` f x
    
    monad_law_3 = (\ (x::Succs a) k h -> x >>= (\x -> k x >>= str h))
              === (\ (x::Succs a) k h -> (x >>= k) >>= str h)
  • Instead of using recursion, try to use combinators (e.g. filter, map, ++ etc.).

  • Add rewrite rules to tell GHC about some program equations that it should use while simplifying. This is in particular useful when working with list functions

    Here are some examples:

    {-# RULES "mapFB/id" forall c . mapFB c (\x -> x) = c #-}
    {-# RULES "foldr/nil" forall k n . GHC.Base.foldr k n [] = n #-}
    {-# RULES "foldr/mapFB" forall c f g n1 n2 xs.
        GHC.Base.foldr (mapFB c f) n1 (GHC.Base.foldr (mapFB (:) g) n2 xs)
        = GHC.Base.foldr (mapFB c (f.g)) (GHC.Base.foldr (mapFB c f) n1 n2) xs
        #-}

    But note that these apply to your whole module, and are exported from it, so you should not attempt to add such a rule to the same module where you prove the rule. And if you don’t want these rules to be applied in normal code, put your proofs into a separate Proof module that is never imported.

Shall I use this in production?

You can try. It certainly does not hurt, and proofs that go through are fine. It might not prove enough to be really useful.

What next?

It remains to be seen how useful this approach really is, and what can be done to make it more useful. So we need to start proving some things.

Here are some aspects that likely need to be improved:

  • The user should be put into control of some of the simplifier settings. Depending on the proof, one might want to go through more or less of the simplifier phases, or disable and enable certain rules.

  • Maybe a syntax that does not abuse term-level bindings can be introduced. Currently, though, this is not possible for a plugin.

  • If deemed useful, this functionaly maybe can become part of GHC, and the simplifier could get a few extra knobs to turn.

  • A custom function to compare expressions that relates more than just alpha-equivalence could expand the scope of this plugin.

  • The reporting of failed proofs can be improved.

  • Come up with a better story about recursive functions.

How else can I prove things about Haskell?

  • Wait for full dependent types in Haskell and express your equivalences as types.
  • Manually or mechanically (e.g. using Haskabelle) rewrite your code in a theorem prover such as Isabelle, Agda or Coq, and prove stuff there.
  • Write your code in these theorem provers in the first place and export them to Haskell.
  • You might be able to prove a few things using Liquid Haskell or, actually quite similar to this, HERMIT.

Can I comment or help?

Sure! We can use the GitHub issue tracker for discussions, and obviously contributions are welcome.

More Repositories

1

incredible

The Incredible Proof Machine
Haskell
359
star
2

arbtt

arbtt, the automatic rule-based time-tracker
Haskell
261
star
3

inspection-testing

Inspection Testing for Haskell
Haskell
171
star
4

ghc-justdoit

A magic typeclass that just does it
Haskell
120
star
5

udp-broadcast-relay

Small daemon to relay udp broadcast packages on a different subnet.
C
94
star
6

gtk-vector-screenshot

C
93
star
7

veggies

Hopefully verifiable code generation in GHC
Haskell
92
star
8

gipeda

Git Performance Dashboard
Haskell
76
star
9

loogle

Mathlib search tool
Lean
53
star
10

haskell-for-readers

Lecture notes for teaching Haskell to those who want to read it
Haskell
53
star
11

git-multisect

find git commits that matter
Python
49
star
12

ghc-heap-view

Extract the heap representation of Haskell values and thunks
Haskell
48
star
13

screen-message

Very simple tool to display some text as large as possible
C
47
star
14

bisect-binary

Tool to determine relevant parts of binary data
Haskell
44
star
15

haskell-on-fastly

Experiments about running Haskell via WebAssembly on Fastly
Haskell
38
star
16

kaleidogen

Grow procedual art
Haskell
36
star
17

cabal-plan-bounds

Calculate Haskell dependency ranges from multiple build plans
Haskell
32
star
18

ic-telegram-bot

A telegram bot on the Internet Computer
Rust
25
star
19

ghcjs2gh-pages

Example repository setup that deploys GHCJS programs to GitHub Pages
Haskell
24
star
20

haskell-bounds-bump-action

Create PR to bump Haskell dependency bounds
24
star
21

ic-http-lambda

A HTTP-to-IC bridge (proof of concept)
Rust
24
star
22

bSpokeLight

Custom firmware for the YQ8003 bicycle spoke light
Nix
22
star
23

ic-certification

Motoko library for all things related to certification
Nix
22
star
24

tasty-expected-failure

Mark test cases as expected-failure
Haskell
20
star
25

containers-verified

A package re-exporting the verified subset of containers
Haskell
20
star
26

capture-the-ic-token

Hack the canister, get the token
Motoko
19
star
27

secp265k1-lookup-table

A lookup table for the discrete log in secp265k1
C
19
star
28

haskell-rec-def

APIs for more recursive definitions
Haskell
18
star
29

lean-wf-induct

Lean
17
star
30

motoko-certified-http

A motoko canister that can server certified HTTP assets
Modelica
17
star
31

safe-docker

Sandbox untrusted code using docker
Perl
15
star
32

haskell-via-sokoban

A haskell tutorial using CodeWorld
Haskell
15
star
33

share-file

FileTea-like command line tool
Python
10
star
34

ghc-core-smallstep

A small-step semantics for Core
Haskell
10
star
35

codeworld-talk

The Codeworld-implemented sildes to my CodeWorld talk
Haskell
9
star
36

hs-all-in-one

Merges Haskell modules into one
Haskell
9
star
37

lean-calcify

Lean
9
star
38

free-theorems-static-webui

A browser-only web interface to the free-theorems library
Haskell
8
star
39

pybotbrain

Learn programming with Python and Telegram
PureScript
8
star
40

ic-barrier

Delay responses on the Internet Computer, for testing and other purposes
Modelica
6
star
41

list-producing-monads

Benchmarks for http://www.joachim-breitner.de/blog/684-constructing-a-list-in-a-monad-revisited
Haskell
6
star
42

git-post-squash

Squash-merge aware merge method
Shell
6
star
43

tiptoi-arm-investigation

Temporary repository to investigate ARM binaries in GME files
Shell
5
star
44

list-fusion-probe

Haskell
5
star
45

haskell-successors

An applicative functor to manage successors
Haskell
5
star
46

haskell-candid

A candid library for Haskell
Haskell
5
star
47

slidingright

The code for the slidingright paper
Haskell
5
star
48

unicode-screensaver

Unicode Screensaver
C
5
star
49

bootstrapping-haskell

Feeble attempts at bootstrapping Haskell
Haskell
5
star
50

list-fusion-lab

Benchmarks for list fusion experiments
Haskell
4
star
51

haskell-bytes-bobkonf2021

The “Haskell Bytes” tutorial at Bobkonf 2021
Haskell
4
star
52

ghc-proposals-stats

Statistics for the GHC proposal repository
Haskell
4
star
53

motoko-sbc2020

Code for Motoko talk at SBC2020
Modelica
4
star
54

hackage-ghcjs-overlay

GHCJS-related packages missing from Hackage
4
star
55

hal2016-website

Webseite for Haskell in Leipzig 2016
HTML
4
star
56

umklappbot

A telegram bot to play the Umklappspiel
Haskell
4
star
57

libnss-myhostname

C
4
star
58

rec-def-pearl

Source for the ICFP'23 Functional Pearl
HTML
3
star
59

reflex-dom-fragment-shader-canvas

A reflex-dom widget to draw on a canvas with a fragment shader program
Haskell
3
star
60

ipatch

interactive patch editor
Haskell
3
star
61

you-say-first

You Say First!
JavaScript
3
star
62

mitschriebwiki

Source for http://mitschriebwiki.nomeata.de/
TeX
3
star
63

nofib-analyse

Parse and compare nofib runs
Haskell
3
star
64

ghc-complete

A history of GHC fingerprint files; used for travis CI.
Shell
3
star
65

yucata-tools

Inofficial tools for heavy yucata.de users.
Python
3
star
66

funcCF

Haskell
2
star
67

link-monitor-applet

C
2
star
68

incredible-demo

The github pages for http://github.com/nomeata/incredible
JavaScript
2
star
69

cabal-force-upper-bound

Reliably exercise the upper bound of your cabal dependencies
Haskell
2
star
70

lean-stage0-audit

HTML
2
star
71

lean-trie

Lean
2
star
72

squasher

Python
2
star
73

badminton-junkies

Badminton Junkies e.V. site
Haskell
2
star
74

haskell-leb128-cereal

LEB128 encoding for Haskell
Haskell
2
star
75

sat-britney

Haskell
2
star
76

lean4-memo-nat

Lean
2
star
77

L-seed

JavaScript
2
star
78

latexki

Static Site Generator for Latex-based Wiki
Haskell
2
star
79

info-beamer-wikicloud

Plug-In for info-beamer
Lua
2
star
80

led-display

Markup language for the 7×21 USB LED Display from Dream Cheeky/get-digital.de
Haskell
2
star
81

circle-packing

Simple heuristic for packing discs of varying radii in a circle
Haskell
2
star
82

libnss-gw-name

C
1
star
83

rerolling-sixes

Lean
1
star
84

colibri-funcup-2023

Python
1
star
85

lean-darray

Lean
1
star
86

lean-simplc

Experimental local confluence checker for Lean simp lemmas
Lean
1
star
87

lean-derec-lab

Lean
1
star
88

conteq

LaTeX class to typeset continued equalities
TeX
1
star
89

sumserum

Sim-Serim clone as a browser game
JavaScript
1
star
90

zpub

zpub is a server to collaboratively work on DocBook-based documentation.
Shell
1
star
91

tttool-nofib

Real-world nofib addition proposal
Haskell
1
star
92

smime-smtp-proxy

Python
1
star
93

motoko-scc

A Strongly Connected Components algorithm for Motoko
Motoko
1
star
94

nt-coerce

Explicit newtype coercion lifting in Haskell
Haskell
1
star
95

haskell-spacegoo

Haskell
1
star
96

random-haskell-code

Generates random Haskell code based on some existing corpus of modules
Haskell
1
star
97

parser-calledarityanalysis

Slowdown in Called arity analysis from GHC 8.0.2 to GHC 8.2.1
Haskell
1
star
98

lean-constructions

Lean
1
star