Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/alhassy/agdacheatsheet
Basics of the dependently-typed functional language Agda ^_^
https://github.com/alhassy/agdacheatsheet
agda cheatsheet emacs html pdf
Last synced: 18 days ago
JSON representation
Basics of the dependently-typed functional language Agda ^_^
- Host: GitHub
- URL: https://github.com/alhassy/agdacheatsheet
- Owner: alhassy
- License: gpl-3.0
- Created: 2019-10-02T17:37:24.000Z (about 5 years ago)
- Default Branch: master
- Last Pushed: 2022-01-06T01:46:40.000Z (almost 3 years ago)
- Last Synced: 2024-10-11T21:51:06.645Z (about 1 month ago)
- Topics: agda, cheatsheet, emacs, html, pdf
- Language: Agda
- Size: 460 KB
- Stars: 37
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
README
# Created 2019-10-04 Fri 16:08
#+OPTIONS: toc:nil d:nil
#+OPTIONS: toc:nil d:nil
#+TITLE: Agda CheatSheet
#+AUTHOR: [[http://www.cas.mcmaster.ca/~alhassm/][Musa Al-hassy]]
#+export_file_name: README.orgBasics of Agda, a total and dependently-typed functional language (•̀ᴗ•́)و
*The listing sheet, as PDF, can be found
[[file:CheatSheet.pdf][here]]*,
or as a [[file:CheatSheet_Portrait.pdf][single column portrait]],
while below is an unruly html rendition.This reference sheet is built from a
[[https://github.com/alhassy/CheatSheet][CheatSheets with Org-mode]]
system. This is a /literate/ Agda file written in Org-mode using
[[https://github.com/alhassy/org-agda-mode][org-agda-mode]].#+toc: headlines 2
#+macro: blurb Basics of Agda, a total and dependently-typed functional language (•̀ᴗ•́)و
#+latex_header: \usepackage{titling,parskip}
#+latex_header: \usepackage{eufrak} % for mathfrak fonts
#+latex_header: \usepackage{multicol,xparse,newunicodechar}#+latex_header: \usepackage{etoolbox}
#+latex_header: \newif\iflandscape
#+latex_header: \landscapetrue#+latex_header_extra: \iflandscape \usepackage[landscape, margin=0.5in]{geometry} \else \usepackage[margin=0.5in]{geometry} \fi
#+latex_header: \def\cheatsheetcols{2}
#+latex_header: \AfterEndPreamble{\begin{multicols}{\cheatsheetcols}}
#+latex_header: \AtEndDocument{ \end{multicols} }#+latex_header: \let\multicolmulticols\multicols
#+latex_header: \let\endmulticolmulticols\endmulticols
#+latex_header: \RenewDocumentEnvironment{multicols}{mO{}}{\ifnum#1=1 #2 \def\columnbreak{} \else \multicolmulticols{#1}[#2] \fi}{\ifnum#1=1 \else \endmulticolmulticols\fi}#+latex_header: \def\maketitle{}
#+latex: \fontsize{9}{10}\selectfont#+latex_header: \def\cheatsheeturl{}
#+latex_header: \usepackage[dvipsnames]{xcolor} % named colours
#+latex: \definecolor{grey}{rgb}{0.5,0.5,0.5}#+latex_header: \usepackage{color}
#+latex_header: \definecolor{darkgreen}{rgb}{0.0, 0.3, 0.1}
#+latex_header: \definecolor{darkblue}{rgb}{0.0, 0.1, 0.3}
#+latex_header: \hypersetup{colorlinks,linkcolor=darkblue,citecolor=darkblue,urlcolor=darkgreen}#+latex_header: \setlength{\parindent}{0pt}
#+latex_header: \def\cheatsheetitemsep{-0.5em}
#+latex_header: \let\olditem\item
#+latex_header_extra: \def\item{\vspace{\cheatsheetitemsep}\olditem}#+latex_header: \usepackage{CheatSheet/UnicodeSymbols}
#+latex_header: \makeatletter
#+latex_header: \AtBeginEnvironment{minted}{\dontdofcolorbox}
#+latex_header: \def\dontdofcolorbox{\renewcommand\fcolorbox[4][]{##4}}
#+latex_header: \makeatother#+latex_header: \RequirePackage{fancyvrb}
#+latex_header: \DefineVerbatimEnvironment{verbatim}{Verbatim}{fontsize=\scriptsize}#+latex_header: \def\cheatsheeturl{https://github.com/alhassy/AgdaCheatSheet}
#+latex_header: \def\cheatsheetcols{2}
#+latex_header: \landscapetrue
#+latex_header: \def\cheatsheetitemsep{-0.5em}#+latex_header: \newunicodechar{‼}{\ensuremath{!\!!}}
#+latex_header: \newunicodechar{𝕨}{\ensuremath{\mathbb{w}}}
#+latex_header: \newunicodechar{≈}{\ensuremath{\approx}}
#+latex_header: \newunicodechar{ℓ}{\ensuremath{\ell}}
#+latex_header: \newunicodechar{ω}{\ensuremath{\omega}}
#+latex_header: \newunicodechar{⁰}{\ensuremath{^0}}
#+latex_header: \newunicodechar{⁴}{\ensuremath{^4}}
#+latex_header: \newunicodechar{♯}{\ensuremath{\sharp}}
#+latex_header: \newunicodechar{α}{\ensuremath{\alpha}}
#+latex_header: \newunicodechar{β}{\ensuremath{\beta}}#+latex_header: \newunicodechar{⇨}{\ensuremath{\circlearrowright}}
#+begin_quote
- [[#administrivia][Administrivia]]
- [[#dependent-functions][Dependent Functions]]
- [[#reads][Reads]]
- [[#dependent-datatypes][Dependent Datatypes]]
- [[#the-curry-howard-correspondence----propositions-as-types][The Curry-Howard Correspondence ---“Propositions as Types”]]
- [[#adding-to-the-table][Adding to the table]]
- [[#equality][Equality]]
- [[#break][break]]
- [[#modules----namespace-management][Modules ---Namespace Management]]
- [[#anonymous-modules-and-variables][Anonymous Modules and Variables]]
- [[#break][break]]
- [[#module-keywords][Module Keywords]]
- [[#records][Records]]
- [[#interacting-with-the-real-world----compilation-haskell-and-io][Interacting with the real world ---Compilation, Haskell, and IO]]
- [[#absurd-patterns][Absurd Patterns]]
- [[#preconditions-as-proof-object-arguments][Preconditions as proof-object arguments]]
- [[#mechanically-moving-from-bool-to-set----avoiding-boolean-blindness][Mechanically Moving from ~Bool~ to ~Set~ ---Avoiding “Boolean Blindness”]]
#+end_quote* Administrivia
#+latex: \hspace{-1.3em}
Agda is based on intuitionistic type theory.| Agda | ≈ | Haskell + Harmonious Support for Dependent Types |
In particular, /types ≈ terms/ and so, for example,
~ℕ ∶ Set = Set₀~ and ~Setᵢ ∶ Setᵢ₊₁~.
One says /universe/ ~Setₙ~ has /level/ $n$.⇨ It is a programming language and a proof assistant.
#+latex: \newline {\color{white}.}\hspace{0.3em}
A proposition is proved by writing a program of the corresponding type.⇨ Its Emacs interface allows programming by gradual refinement
of incomplete type-correct terms. One uses the “hole” marker ~?~
as a placeholder that is used to stepwise write a program.⇨ Agda allows arbitrary mixfix Unicode lexemes, identifiers.
- Underscores are used to indicate where positional arguments.
- Almost anything can be a valid name; e.g., ~[]~ and ~_∷_~ below.
Hence it's important to be liberal with whitespace: ~e:T~ is a valid identifier
whereas ~e ∶ T~ declares ~e~ to be of type ~T~.#+latex: \begin{parallel}
#+latex: \begin{tiny}
#+begin_src agda
module CheatSheet whereopen import Level using (Level)
open import Data.Nat
open import Data.Bool hiding (_>_~ and ~_>>=_~ need to be in scope ---that is all.
The type of ~IO._>>_~ takes two “lazy” IO actions and yield a non-lazy IO action.
The one below is a homogeneously typed version.#+begin_src agda
infixr 1 _>>=_ _>>__>>=_ : ∀ {ℓ} {α β : Set ℓ} → IO α → (α → IO β) → IO β
this >>= f = ♯ this IO.>>= λ x → ♯ f x_>>_ : ∀{ℓ} {α β : Set ℓ} → IO α → IO β → IO β
x >> y = x >>= const y
#+end_srcOddly, Agda's standard library comes with ~readFile~ and
~writeFile~, but the symmetry ends there since it provides ~putStrLn~
but not [[https://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#v:getLine][~getLine~]]. Mimicking the ~IO.Primitive~ module, we define /two/
versions ourselves as proxies for Haskell's ~getLine~ ---the second one
below is bounded by 100 characters, whereas the first is not.#+begin_src agda
postulate
getLine∞ : Primitive.IO Costring{-# FOREIGN GHC
toColist :: [a] -> MAlonzo.Code.Codata.Musical.Colist.AgdaColist a
toColist [] = MAlonzo.Code.Codata.Musical.Colist.Nil
toColist (x : xs) =
MAlonzo.Code.Codata.Musical.Colist.Cons x (MAlonzo.RTE.Sharp (toColist xs))
#-}{- Haskell's prelude is implicitly available; this is for demonstration. -}
{-# FOREIGN GHC import Prelude as Haskell #-}
{-# COMPILE GHC getLine∞ = fmap toColist Haskell.getLine #-}-- (1)
-- getLine : IO Costring
-- getLine = IO.lift getLine∞getLine : IO String
getLine = IO.lift
$ getLine∞ Primitive.>>= (Primitive.return ∘ S.fromList ∘ B.toList ∘ take 100)
#+end_src
We obtain ~MAlonzo~ strings, then convert those to colists, then
eventually lift those to the wrapper ~IO~ type.Let's also give ourselves Haskell's ~read~ method.
#+begin_src agda
postulate readInt : L.List Char → ℕ
{-# COMPILE GHC readInt = \x -> read x :: Integer #-}
#+end_srcNow we write our ~main~ method.
#+begin_src agda
main : Primitive.IO ⊤
main = run do putStrLn "Hello, world! I'm a compiled Agda program!"putStrLn "What is your name?"
name ← getLineputStrLn "Please enter a number."
num ← getLine
let tri = show $ sum $ upTo $ suc $ readInt $ S.toList num
putStrLn $ "The triangle number of " ++ num ++ " is " ++ triputStrLn "Bye, "
-- IO.putStrLn∞ name {- If we use approach (1) above. -}
putStrLn $ "\t" ++ name
#+end_src
For example, the $12^{th}$ [[https://en.wikipedia.org/wiki/Triangular_number][triangle number]] is $\sum_{i=0}^{12} i = 78$.
Interestingly, when an integer parse fails, the program just crashes!
Super cool dangerous stuff!Calling this file ~CompilingAgda.agda~, we may compile then run it with:
#+begin_src shell :tangle no
NAME=CompilingAgda; time agda --compile $NAME.agda; ./$NAME
#+end_srcThe very first time you compile may take ∼80 seconds since some prerequisites need to be compiled,
but future compilations are within ∼10 seconds.The generated Haskell source lives under the newly created MAlonzo directory; namely
~./MAlonzo/Code/CompilingAgda.hs~. Here's some fun: Write a parameterised module with multiple declarations,
then use those in your ~main~; inspect the generated Haskell to see that the module is thrown away in-preference
to top-level functions ---as mentioned earlier.- When compiling you may see an error ~Could not find module ‘Numeric.IEEE’~.
- Simply open a terminal and install the necessary Haskell library:
#+begin_src shell :tangle no
cabal install ieee754
#+end_src* Absurd Patterns
#+latex: \hspace{-1.3em}
When there are no possible constructor patterns, we may match on the pattern ~()~
and provide no right hand side ---since there is no way anyone could provide an argument
to the function.For example, here we define the datatype family of numbers smaller than a given natural number:
~fzero~ is smaller than ~suc n~ for any ~n~, and if ~i~ is smaller than ~n~ then ~fsuc i~ is smaller
than ~suc n~.#+latex: \begin{parallel}
#+begin_src agda
{- Fin n ≅ numbers i with i < n -}
data Fin : ℕ → Set where
fzero : {n : ℕ} → Fin (suc n)
fsuc : {n : ℕ}
→ Fin n → Fin (suc n)
#+end_src
#+latex: \columnbreakFor each $n$, the type ~Fin n~ contains $n$ elements;
e.g., ~Fin 2~ has elements ~fsuc fzero~ and ~fzero~,
whereas ~Fin 0~ has no elements at all.#+latex: \end{parallel} \vspace{-1em}
Using this type, we can write a safe indexing function that never “goes out of bounds”.
#+begin_src agda_‼_ : {A : Set} {n : ℕ} → Vec A n → Fin n → A
[] ‼ ()
(x ∷ xs) ‼ fzero = x
(x ∷ xs) ‼ fsuc i = xs ‼ i
#+end_srcWhen we are given the empty list, ~[]~, then ~n~ is necessarily ~0~,
but there is no way to make an element of type ~Fin 0~ and so we have the absurd pattern.
That is, since the empty type ~Fin 0~ has no elements there is nothing to define
---we have a definition by /no cases/.Logically [[https://en.wikipedia.org/wiki/Principle_of_explosion][“anything follows from false”]] becomes the following program:
#+begin_src agda
data False : Set wheremagic : {Anything-you-want : Set} → False → Anything-you-want
magic ()
#+end_srcStarting with ~magic x = ?~ then casing on ~x~ yields the program above
since there is no way to make an element of ~False~
---we needn't bother with a result(ing right side), since there's no way to make
an element of an empty type.** Preconditions as proof-object arguments
Sometimes it is not easy to capture a desired precondition in the types, and
an alternative is to use the following ~isTrue~-approach of passing around
explicit proof objects.#+latex: \begin{parallel}
#+begin_src agda
{- An empty record has only
one value: record {} -}
record True : Set whereisTrue : Bool → Set
isTrue true = True
isTrue false = False
#+end_src
#+latex: \columnbreak
#+begin_src agda
_<₀_ : ℕ → ℕ → Bool
_ <₀ zero = false
zero <₀ suc y = true
suc x <₀ suc y = x <₀ y
#+end_src
#+latex: \end{parallel} \vspace{-1em}#+begin_src agda
find : {A : Set} (xs : List A) (i : ℕ) → isTrue (i <₀ length xs) → A
find [] i ()
find (x ∷ xs) zero pf = x
find (x ∷ xs) (suc i) pf = find xs i pfhead′ : {A : Set} (xs : List A) → isTrue (0 <₀ length xs) → A
head′ [] ()
head′ (x ∷ xs) _ = x
#+end_srcUnlike the ~_‼_~ definition, rather than there being no index into the empty list,
there is no proof that a natural number ~i~ is smaller than 0.* Mechanically Moving from ~Bool~ to ~Set~ ---Avoiding “Boolean Blindness”
#+latex: \hspace{-1.3em}
In Agda we can represent a proposition as a type whose elements denote proofs
of that proposition. Why would you want this? Recall how awkward it was to request
an index be “in bounds” in the ~find~ method, but it's much easier to encode this
using ~Fin~ ---likewise, ~head′~ obtains a more elegant type when the non-empty precondition
is part of the datatype definition, as in ~head~.Here is a simple recipe to go from Boolean functions to inductive datatype families.
1. Write the Boolean function.
2. Throw away all the cases with right side ~false~.
3. Every case that has right side ~true~ corresponds to a new nullary constructor.
4. Every case that has $n$ recursive calls corresponds to an ~n~-ary constructor.Following these steps for ~_<₀_~, from the left side of the page, gives us:
#+begin_src agda
data _<₁_ : ℕ → ℕ → Set where
z< : {y : ℕ} → zero <₁ y
s< : {x y : ℕ} → x <₁ y → suc x <₁ suc y
#+end_srcTo convince yourself you did this correctly, you can prove “soundness”
---constructed values correspond to Boolean-true statements---
and “completeness” ---true things correspond to terms formed from constructors.
The former is ensured by the second step in our recipe!#+begin_src agda
completeness : {x y : ℕ} → isTrue (x <₀ y) → x <₁ y
completeness {x} {zero} ()
completeness {zero} {suc y} p = z<
completeness {suc x} {suc y} p = s< (completeness p)
#+end_srcWe began with ~completeness {x} {y} p = ?~, then we wanted to case on ~p~
but that requires evaluating ~x <₀ y~ which requires we know the shapes of ~x~ and ~y~.
/The shape of proofs usually mimics the shape of definitions they use/; e.g., ~_<₀_~ here.