Skip to content

pa-ba/monadic-compiler-calculation

Repository files navigation

This repository contains the supplementary material for the paper Monadic Compiler Calculation. The material includes Agda formalisations of all calculations in the paper. In addition, we also include Agda formalisations for calculations that were mentioned but not explicitly carried out in the paper.

To typecheck all Agda files, use the included makefile by simply invoking make in this directory. All source files in this directory have been typechecked using version 2.6.2.2 of Agda and version 1.7 of the Agda standard library.

Files

Monads

  • Partial.agda: Definition of the partiality monad Partial + proofs of its laws (section 2).
  • NonDeterm.agda: Definition of the non-determinism monad ND + proofs of its laws (section 4).
  • PartialNonDeterm.agda: Definition of the PND (called $ND_\bot$ in the paper) monad that combines Partial and ND + proofs of its laws.

Compiler calculations from the paper

  • Loop.agda: Simple arithmetic language with a degenerate loop (section 2).
  • Lambda.agda: Call-by-value lambda calculus (section 3).
  • Interrupts.agda: Interrupts language (section 4).

Additional compiler calculations

Termination arguments

In some cases, Agda's termination checker rejects the definition of the virtual machine exec. In these cases, the termination checker is disabled for exec (using the TERMINATING pragma) and termination is proved manually in the following modules:

Agda formalisation vs. paper proofs

In the paper, we use an idealised Haskell-like syntax for all definitions. Here we outline how this idealised syntax is translated into Agda.

Sized coinductive types

In the paper, we use the codata syntax to distinguish coinductively defined data types from inductive data types. In particular, we use this for the coinductive Partial type:

codata Partial a = Now a | Later (Partial a)

In Agda we use coinductive record types to represent coinductive data types. Moreover, we use sized types to help the termination checker to recognize productive corecursive function definitions. Therefore, the Partial type has an additional parameter of type Size:

data Partial (A : Set) (i : Size) : Set where
  now   : A → Partial A i
  later : ∞Partial A i → Partial A i

record ∞Partial (A : Set) (i : Size) : Set where
  coinductive
  constructor delay
  field
    force : {j : Size< i} → Partial A j

Mutual inductive and co-inductive definitions

The semantic functions eval and exec are well-defined because recursive calls take smaller arguments (either structurally or according to some measure), or are guarded by Later. For example, in the case of the Loop lanugage, eval is applied to the structurally smaller x and y in the case of Add or is guarded by Later in the case for Loop:

eval :: Expr -> Partial Int
eval (Val n)   = return n
eval (Add x y) = do m <- eval x
                    n <- eval y
                    return (m+n)
eval Loop      = Later (eval Loop)

This translates to two mutually recursive functions in Agda, one for recursive calls (applied to smaller arguments) and one for co-recursive calls (guarded by Later). We use the prefix to distinguish the co-recursive function from the recursive function. For example, for the Loop language we write an eval and an ∞eval function:

mutual
  eval : ∀ {i} → Expr → Partial ℕ i
  eval (Val x) = return x
  eval (Add x y) =
    do n ← eval x
       m ← eval y
       return (n + m)
  eval Loop = later (∞eval Loop)

  ∞eval : ∀ {i} → Expr → ∞Partial ℕ i
  force (∞eval x) = eval x

Partial pattern matching in do notation

The Haskell syntax in the paper uses partial pattern matching in do notation, e.g. in the following fragment from section 3.1:

eval (Add x y) e = do Num m <- eval x e
                      Num n <- eval y e
                      return (Num (m + n))

To represent partial pattern matching in Agda, we use an auxiliary function (getNum : ∀ {i} → Value → Partial ℕ i for the code fragment above) that performs the pattern matching and behaves like the fail method if pattern matches fails:

eval (Add x y) e =
  do n ← eval x e >>= getNum
     m ← eval y e >>= getNum
     return (Num (n + m))

Idris-style pattern matching in do notation

In section 4, we use a more general pattern matching syntax in do notation that is inspired by Idris, e.g. the following fragment from section 4.1:

eval' (Add x y) i = do Just m ← eval x i  | return Nothing
                       Just n ← eval y i  | return Nothing
                       return (Just (m + n))

Agda supports a similar pattern matching syntax. The above fragment would translate to the following Agda code:

eval' (Add x y) i = do just m ← eval x i  where _ → return nothing
                       just n ← eval y i  where _ → return nothing
                       return (just (m + n))

However, using this syntax is inappropriate since the pattern matching clauses are translated into fresh anonymous functions internally. This has the undesireable effect that syntactically identical terms cannot be proved equal. For example, we cannot prove the following equality:

(do just n ← eval x i  where _ → return nothing
    return (just n+1))
==
(do just n ← eval x i  where _ → return nothing
    return (just n+1))

Therefore, we use helper functions that encode the desired pattern matching. For example, the abovementioned code from the paper is translated into the following Agda code.

eval' (Add x y) i  =
   eval x i >>= getJust (return nothing) λ m →
   eval y i >>= getJust (return nothing) λ n →
   return (just (m + n))

where the helper function getJust is of type

∀ {A B : Set} → ND B → (A → ND B) → Maybe A → ND B

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published