Initial import.
This commit is contained in:
commit
36ed6df77f
|
@ -0,0 +1,12 @@
|
|||
SANDBOX
|
||||
=======
|
||||
|
||||
This is a repository of various repositories for learning or exploring
|
||||
topics.
|
||||
|
||||
Projects
|
||||
--------
|
||||
|
||||
+ ``lpn/``: `Learn Prolog Now <http://lpn.swi-prolog.org/>`_
|
||||
+ ``uc/``: `Understanding Computation <http://computationbook.com/>`_
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
%% Exercise 2.4: Here are six Italian words:
|
||||
%%
|
||||
%% - astante
|
||||
%% - astoria
|
||||
%% - baratto
|
||||
%% - cobalto
|
||||
%% - pistola
|
||||
%% - statale
|
||||
%%
|
||||
%% They are to be arranged, crossword puzzle fashion, in the following grid:
|
||||
%%
|
||||
%% V1 V2 V3
|
||||
%% . . .
|
||||
%% H1 . 1 . 4 . 7 .
|
||||
%% . . .
|
||||
%% H2 . 2 . 5 . 8 .
|
||||
%% . . .
|
||||
%% H3 . 3 . 6 . 9 .
|
||||
%% . . .
|
||||
%%
|
||||
%% The following knowledge base represents a lexicon containing these words:
|
||||
|
||||
word(astante, a,s,t,a,n,t,e).
|
||||
word(astoria, a,s,t,o,r,i,a).
|
||||
word(baratto, b,a,r,a,t,t,o).
|
||||
word(cobalto, c,o,b,a,l,t,o).
|
||||
word(pistola, p,i,s,t,o,l,a).
|
||||
word(statale, s,t,a,t,a,l,e).
|
||||
|
||||
%% Write a predicate crossword/6 that tells us how to fill in the
|
||||
%% grid. The first three arguments should be the vertical words from
|
||||
%% left to right, and the last three arguments the horizontal words
|
||||
%% from top to bottom.
|
||||
|
||||
crossword(V1, V2, V3, H1, H2, H3) :-
|
||||
word(V1, _, _1, _, _2, _, _3, _),
|
||||
word(V2, _, _4, _, _5, _, _6, _),
|
||||
word(V3, _, _7, _, _8, _, _9, _),
|
||||
word(H1, _, _1, _, _4, _, _7, _),
|
||||
word(H2, _, _2, _, _5, _, _8, _),
|
||||
word(H3, _, _3, _, _6, _, _9, _),
|
||||
V1 \= V2, V1 \= V3, V1 \= H1, V1 \= H2, V1 \= H3,
|
||||
V2 \= V3, V2 \= H1, V2 \= H2, V2 \= H3,
|
||||
V3 \= H1, V3 \= H2, V3 \= H3,
|
||||
H1 \= H2, H1 \= H3, H2 \= H3.
|
||||
|
||||
%% NB: execute the following query to find out the correct arrangement.
|
||||
%% crossword(A, B, C, D, E, F).
|
||||
|
||||
%% $ swipl -q
|
||||
%% 1 ?- [italiano].
|
||||
%% true.
|
||||
%%
|
||||
%% 2 ?- crossword(A, B, C, D, E, F).
|
||||
%% A = astante,
|
||||
%% B = cobalto,
|
||||
%% C = pistola,
|
||||
%% D = astoria,
|
||||
%% E = baratto,
|
||||
%% F = statale ;
|
||||
%% A = astoria,
|
||||
%% B = baratto,
|
||||
%% C = statale,
|
||||
%% D = astante,
|
||||
%% E = cobalto,
|
||||
%% F = pistola ;
|
||||
%% false.
|
||||
|
||||
%% Note that there are two solutions; these are mirrors of each other
|
||||
%% with the vertical words substituted for the horizontal words.
|
|
@ -0,0 +1,8 @@
|
|||
is_digesting(X,Y) :- just_ate(X,Y).
|
||||
is_digesting(X,Y) :-
|
||||
just_ate(X,Z),
|
||||
is_digesting(Z,Y).
|
||||
|
||||
just_ate(mosquito,blood(john)).
|
||||
just_ate(frog,mosquito).
|
||||
just_ate(stork,frog).
|
|
@ -0,0 +1,5 @@
|
|||
woman(mia).
|
||||
woman(jody).
|
||||
woman(yolanda).
|
||||
playsAirGuitar(jody).
|
||||
party.
|
|
@ -0,0 +1,6 @@
|
|||
happy(yolanda).
|
||||
listens2Music(mia).
|
||||
listens2Music(yolanda):- happy(yolanda).
|
||||
playsAirGuitar(mia):- listens2Music(mia).
|
||||
playsAirGuitar(yolanda):- listens2Music(yolanda).
|
||||
listens2Music(bruce).
|
|
@ -0,0 +1,9 @@
|
|||
happy(vincent).
|
||||
listens2Music(butch).
|
||||
playsAirGuitar(vincent):-
|
||||
listens2Music(vincent),
|
||||
happy(vincent).
|
||||
playsAirGuitar(butch):-
|
||||
happy(butch).
|
||||
playsAirGuitar(butch):-
|
||||
listens2Music(butch).
|
|
@ -0,0 +1,8 @@
|
|||
woman(mia).
|
||||
woman(jody).
|
||||
woman(yolanda).
|
||||
|
||||
loves(vincent,mia).
|
||||
loves(marsellus,mia).
|
||||
loves(pumpkin,honey_bunny).
|
||||
loves(honey_bunny,pumpkin).
|
|
@ -0,0 +1,11 @@
|
|||
loves(vincent,mia).
|
||||
loves(marsellus,mia).
|
||||
loves(pumpkin,honey_bunny).
|
||||
loves(honey_bunny,pumpkin).
|
||||
|
||||
/**
|
||||
* original definition:
|
||||
* jealous(X,Y):- loves(X,Z), loves(Y,Z).
|
||||
*/
|
||||
|
||||
jealous(X,Y):- loves(X,Z), loves(Y,Z), X \= Y.
|
|
@ -0,0 +1,13 @@
|
|||
word(determiner,a).
|
||||
word(determiner,every).
|
||||
word(noun,criminal).
|
||||
word(noun,'big kahuna burger').
|
||||
word(verb,eats).
|
||||
word(verb,likes).
|
||||
|
||||
sentence(Word1,Word2,Word3,Word4,Word5):-
|
||||
word(determiner,Word1),
|
||||
word(noun,Word2),
|
||||
word(verb,Word3),
|
||||
word(determiner,Word4),
|
||||
word(noun,Word5).
|
|
@ -0,0 +1,2 @@
|
|||
vertical(line(point(X,Y),point(X,Z))).
|
||||
horizontal(line(point(X,Y),point(Z,Y))).
|
|
@ -0,0 +1,9 @@
|
|||
f(a).
|
||||
f(b).
|
||||
|
||||
g(a).
|
||||
g(b).
|
||||
|
||||
h(b).
|
||||
|
||||
k(X) :- f(X), g(X), h(X).
|
|
@ -0,0 +1,3 @@
|
|||
listensToMusic(X) :- happy(X).
|
||||
playsAirGuitar(X) :- listensToMusic(X).
|
||||
happy(a).
|
|
@ -0,0 +1,2 @@
|
|||
This is the superproject where all my attempts at working through the
|
||||
book "Understanding Computation" are.
|
|
@ -0,0 +1 @@
|
|||
Lisp code supporting "Universal Computation".
|
|
@ -0,0 +1,114 @@
|
|||
;;; # The Meaning of Programs
|
||||
|
||||
;;; What is computation? what a computer does.
|
||||
|
||||
;;; computation environment:
|
||||
;;;
|
||||
;;; * machine
|
||||
;;; * language
|
||||
;;; * program
|
||||
;;;
|
||||
;;; Fundamentally, programming is about ideas: a program is a snapshot of
|
||||
;;; an idea, a codification of the mental structures of the programmer(s)
|
||||
;;; involved.
|
||||
;;;
|
||||
;;; ## The Meaning of "Meaning"
|
||||
;;;
|
||||
;;; *semantics* is the connection between words and meanings: while "dog"
|
||||
;;; is an arrangment of shapes on a page, an actual dog is a very separate
|
||||
;;; things. Semantics relates concrete signifiers and abstract meanings,
|
||||
;;; and attempts to study the fundamental nature of the abstract meanings.
|
||||
;;;
|
||||
;;; *Formal semantics* is the attempt at formalising the meanings of
|
||||
;;; programmings, and using this formalisation to reason about
|
||||
;;; languages. In order to specify a programming language, we need to
|
||||
;;; define both its *syntax* (the representation) and its *semantics* (the
|
||||
;;; meaning).
|
||||
;;;
|
||||
;;; Most languages lack a formal specification and opt to use a canonical
|
||||
;;; reference implementation. An alternative is to write a prose
|
||||
;;; specification, which is the approach of C++. A third approach is to
|
||||
;;; mathematically specify the language such that automated mathematical
|
||||
;;; analysis can be done.
|
||||
;;;
|
||||
;;; ### Syntax
|
||||
;;;
|
||||
;;; The language's syntax is what differentiates valid examples of code like
|
||||
|
||||
(defun y (x) (+ x 1))
|
||||
|
||||
;;; from nonsense like `$%EHI`. In general, a parser reads a string
|
||||
;;; (like "(defun y (x) (+ x 1)") and turns it into an *abstract
|
||||
;;; syntax tree*. Syntax is ultimately only concerned with the surface
|
||||
;;; appearance of the program
|
||||
|
||||
;;; ### Operational Semantics
|
||||
|
||||
;;; A practical means of thinking about the meaning of a program is
|
||||
;;; *what it does*. *operational semantics* defines rules for how
|
||||
;;; programs run on some machine (often an *abstract machine*).
|
||||
|
||||
;;; ### Small-Step Semantics
|
||||
;;;
|
||||
;;; Let's imagine an abstract machine that evaluates by directly
|
||||
;;; operating on the syntax, reducing it iteratively to bring about the
|
||||
;;; final result.
|
||||
;;;
|
||||
;;; For example, to evaluate (1 * 2) + (3 * 4):
|
||||
|
||||
;;; 0. Compute the left-hand multiplication (1 * 2 -> 2), simplifying
|
||||
;;; the expression to 2 + (3 * 4).
|
||||
|
||||
;;; 0. Compute the right-hand multiplication (3 * 14 -> 12),
|
||||
;;; simplifying the expression to 2 + 12.
|
||||
|
||||
;;; 0. Carry out the addition, resulting in 14.
|
||||
|
||||
;;; We determine that 14 is the result because it cannot be simplified
|
||||
;;; any further. It is a special type of algebraic expression (a
|
||||
;;; *value*) with its own meaning.
|
||||
|
||||
;;; We can write down formal rules like these describing how to proceed
|
||||
;;; with each small reduction step; these rules are written in a
|
||||
;;; *metalanguage* (which is often mathematical notation).
|
||||
|
||||
;;; Let's explore the semantics of a toy language called SIMPLE. There
|
||||
;;; is a formal mathematical language to this, but we'll use a
|
||||
;;; programming language to make it more clear.
|
||||
|
||||
(defclass ast-node ()
|
||||
()
|
||||
(:documentation "The superclass of all AST nodes."))
|
||||
|
||||
(defclass ast-number (ast-node)
|
||||
((value :initarg :value :accessor nvalue))
|
||||
(:documentation "A discrete, irreducible value."))
|
||||
|
||||
(defclass ast-add (ast-node)
|
||||
((left :initarg :left :accessor ast-left)
|
||||
(right :initarg :right :accessor ast-right))
|
||||
(:documentation "AST node that adds its arguments together."))
|
||||
|
||||
(defclass ast-mult (ast-node)
|
||||
((left :initarg :left :accessor ast-left)
|
||||
(right :initarg :right :accessor ast-right))
|
||||
(:documentation "AST node that multiplies its arguments together."))
|
||||
|
||||
(defun make-node (&key (type :number) (values (list 0 0)))
|
||||
(cond
|
||||
((equal type :number) (make-instance 'ast-number :value (car values)))
|
||||
((equal type :add) (make-instance 'ast-add :left (car values) :right (cadr values)))
|
||||
((equal type :mult) (make-instance 'ast-mult :left (car values) :right (cadr values)))))
|
||||
|
||||
(make-node :type :add
|
||||
:values (list (make-node :type :mult
|
||||
:values (list (make-node :values (list 1))
|
||||
(make-node :values (list 2))))
|
||||
(make-node :type :mult
|
||||
:values (list (make-node :values (list 3))
|
||||
(make-node :values (list 4))))))
|
||||
|
||||
;;; We can use these to build an AST by hand:
|
||||
|
||||
;;; print Add(Multiply (Number 2) Multiply (Number 3))
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
;;;; package.lisp
|
||||
|
||||
(defpackage #:uc
|
||||
(:use #:cl))
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
;;;; uc.asd
|
||||
|
||||
(asdf:defsystem #:uc
|
||||
:serial t
|
||||
:description "Describe uc here"
|
||||
:author "Your Name <your.name@example.com>"
|
||||
:license "Specify license here"
|
||||
:components ((:file "package")
|
||||
(:file "uc")
|
||||
(:file "meaning")))
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
;;;; uc.lisp
|
||||
|
||||
(in-package #:uc)
|
||||
|
||||
;;; "uc" goes here. Hacks and glory await!
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
## understanding computation
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
-- # The Meaning of Programs
|
||||
|
||||
-- What is computation? what a computer does.
|
||||
|
||||
-- computation environment:
|
||||
--
|
||||
-- * machine
|
||||
-- * language
|
||||
-- * program
|
||||
--
|
||||
-- Fundamentally, programming is about ideas: a program is a snapshot of
|
||||
-- an idea, a codification of the mental structures of the programmer(s)
|
||||
-- involved.
|
||||
--
|
||||
-- ## The Meaning of "Meaning"
|
||||
--
|
||||
-- *semantics* is the connection between words and meanings: while "dog"
|
||||
-- is an arrangment of shapes on a page, an actual dog is a very separate
|
||||
-- things. Semantics relates concrete signifiers and abstract meanings,
|
||||
-- and attempts to study the fundamental nature of the abstract meanings.
|
||||
--
|
||||
-- *Formal semantics* is the attempt at formalising the meanings of
|
||||
-- programmings, and using this formalisation to reason about
|
||||
-- languages. In order to specify a programming language, we need to
|
||||
-- define both its *syntax* (the representation) and its *semantics* (the
|
||||
-- meaning).
|
||||
--
|
||||
-- Most languages lack a formal specification and opt to use a canonical
|
||||
-- reference implementation. An alternative is to write a prose
|
||||
-- specification, which is the approach of C++. A third approach is to
|
||||
-- mathematically specify the language such that automated mathematical
|
||||
-- analysis can be done.
|
||||
--
|
||||
-- ### Syntax
|
||||
--
|
||||
-- The language's syntax is what differentiates valid examples of code like
|
||||
|
||||
y x = x + 1
|
||||
|
||||
-- from nonsense like `$%EHI`. In general, a parser reads a string
|
||||
-- (like "y x = x + 1") and turns it into an /abstract syntax
|
||||
-- tree/. Syntax is ultimately only concerned with the surface
|
||||
-- appearance of the program
|
||||
|
||||
-- ### Operational Semantics
|
||||
|
||||
-- A practical means of thinking about the meaning of a program is
|
||||
-- *what it does*. *operational semantics* defines rules for how
|
||||
-- programs run on some machine (often an *abstract machine*).
|
||||
|
||||
-- ### Small-Step Semantics
|
||||
--
|
||||
-- Let's imagine an abstract machine that evaluates by directly
|
||||
-- operating on the syntax, reducing it iteratively to bring about the
|
||||
-- final result.
|
||||
--
|
||||
-- For example, to evaluate (1 * 2) + (3 * 4):
|
||||
|
||||
-- 0. Compute the left-hand multiplication (1 * 2 -> 2), simplifying
|
||||
-- the expression to 2 + (3 * 4).
|
||||
|
||||
-- 0. Compute the right-hand multiplication (3 * 14 -> 12),
|
||||
-- simplifying the expression to 2 + 12.
|
||||
|
||||
-- 0. Carry out the addition, resulting in 14.
|
||||
|
||||
-- We determine that 14 is the result because it cannot be simplified
|
||||
-- any further. It is a special type of algebraic expression (a
|
||||
-- *value*) with its own meaning.
|
||||
|
||||
-- We can write down formal rules like these describing how to proceed
|
||||
-- with each small reduction step; these rules are written in a
|
||||
-- *metalanguage* (which is often mathematical notation).
|
||||
|
||||
-- Let's explore the semantics of a toy language called SIMPLE. There
|
||||
-- is a formal mathematical language to this, but we'll use a
|
||||
-- programming language to make it more clear.
|
||||
|
||||
class AST a where
|
||||
reduce :: a -> Number
|
||||
reducible :: a -> Bool
|
||||
|
||||
data Number = Number { value :: Integer } deriving (Show)
|
||||
|
||||
instance AST Number where
|
||||
reduce (Number value) = (Number value)
|
||||
reducible (Number _) = False
|
||||
|
||||
data Add = Add { leftAdd :: Number
|
||||
, rightAdd :: Number
|
||||
} deriving (Show)
|
||||
|
||||
instance AST Add where
|
||||
reduce (Add x y) = Number (value x + value y)
|
||||
reducible (Add _ _) = True
|
||||
|
||||
data Multiply = Multiply { leftMult :: Number
|
||||
,rightMult :: Number
|
||||
} deriving (Show)
|
||||
|
||||
-- We can use these to build an AST by hand:
|
||||
|
||||
-- print Add(Multiply (Number 2) Multiply (Number 3))
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
-- # The Meaning of Programs
|
||||
|
||||
-- What is computation? what a computer does.
|
||||
|
||||
-- computation environment:
|
||||
-- * machine
|
||||
-- * language
|
||||
-- * program
|
||||
--
|
||||
-- Fundamentally, programming is about ideas: a program is a snapshot of
|
||||
-- an idea, a codification of the mental structures of the programmer(s)
|
||||
-- involved.
|
||||
--
|
||||
-- ## The Meaning of "Meaning"
|
||||
--
|
||||
-- /semantics/ is the connection between words and meanings: while "dog"
|
||||
-- is an arrangment of shapes on a page, an actual dog is a very separate
|
||||
-- things. Semantics relates concrete signifiers and abstract meanings,
|
||||
-- and attempts to study the fundamental nature of the abstract meanings.
|
||||
--
|
||||
-- *Formal semantics* is the attempt at formalising the meanings of
|
||||
-- programmings, and using this formalisation to reason about
|
||||
-- languages. In order to specify a programming language, we need to
|
||||
-- define both its *syntax* (the representation) and its *semantics* (the
|
||||
-- meaning).
|
||||
--
|
||||
-- Most languages lack a formal specification and opt to use a canonical
|
||||
-- reference implementation. An alternative is to write a prose
|
||||
-- specification, which is the approach of C++. A third approach is to
|
||||
-- mathematically specify the language such that automated mathematical
|
||||
-- analysis can be done.
|
||||
--
|
||||
-- ### Syntax
|
||||
--
|
||||
-- The language's syntax is what differentiates valid examples of code like
|
||||
|
||||
y x = x + 1
|
||||
|
||||
-- from nonsense like `$%EHI`. In general, a parser reads a string
|
||||
-- (like "y x = x + 1") and turns it into an /abstract syntax
|
||||
-- tree/. Syntax is ultimately only concerned with the surface
|
||||
-- appearance of the program
|
||||
|
||||
-- ### Operational Semantics
|
||||
|
||||
-- A practical means of thinking about the meaning of a program is
|
||||
-- *what it does*.
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
What is computation? what a computer does.
|
||||
|
||||
computation environment:
|
||||
|
||||
* machine
|
||||
|
||||
* language
|
||||
|
||||
* program
|
||||
|
||||
Fundamentally, programming is about ideas: a program is a snapshot of
|
||||
an idea, a codification of the mental structures of the programmer(s)
|
||||
involved.
|
||||
|
||||
== The Meaning of "Meaning"
|
||||
|
||||
/semantics/ is the connection between words and meanings: while "dog"
|
||||
is an arrangment of shapes on a page, an actual dog is a very separate
|
||||
things. Semantics relates concrete signifiers and abstract meanings,
|
||||
and attempts to study the fundamental nature of the abstract meanings.
|
||||
|
||||
_Formal semantics_ is the attempt at formalising the meanings of
|
||||
programmings, and using this formalisation to reason about
|
||||
languages. In order to specify a programming language, we need to
|
||||
define both its /syntax/ (the representation) and its /semantics/ (the
|
||||
meaning).
|
||||
|
||||
Most languages lack a formal specification and opt to use a canonical
|
||||
reference implementation. An alternative is to write a prose
|
||||
specification, which is the approach of C++. A third approach is to
|
||||
mathematically specify the language such that automated mathematical
|
||||
analysis can be done.
|
||||
|
||||
=== Syntax
|
||||
|
||||
The language's syntax is what differentiates valid examples of code like
|
||||
|
||||
> y x = x + 1
|
||||
|
||||
from nonsense like `$%EHI`. In general, a parser reads a string (like
|
||||
"y x = x + 1") and turns it into an /abstract syntax tree/. Syntax is ultimately only concerned with the surface appearance of the program
|
||||
|
||||
=== Operational Semantics
|
||||
|
||||
A practical means of thinking about the meaning of a program is /what it does/.
|
|
@ -0,0 +1,5 @@
|
|||
# Revision history for uc
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
* First version. Released on an unsuspecting world.
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2016 Kyle Isom
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,2 @@
|
|||
import Distribution.Simple
|
||||
main = defaultMain
|
Binary file not shown.
After Width: | Height: | Size: 526 KiB |
|
@ -0,0 +1,228 @@
|
|||
Chapter 2: The Meaning of Programs
|
||||
==================================
|
||||
|
||||
> module Meaning where
|
||||
|
||||
Programming is about ideas, not about the programs themselves;
|
||||
they're manifestations (sometimes even physical) of someone's ideas;
|
||||
there must be some *raison d'être* for program to be written. That
|
||||
reason can be to scratch an itch, but some motivation needs to drive
|
||||
the program.
|
||||
|
||||
Linguistics has a field called *semantics*, which "is the study of
|
||||
the connection between words and their meanings;" computer science
|
||||
has *formal semantics*, which deals with specifying these connections
|
||||
mathemtically (more or less). Specifying a programming language
|
||||
requires both a syntax (*how does the program look*?) and semantics
|
||||
(*what does the program mean*?). Funny enough, many (most?) PL's
|
||||
don't have some canonical formal specification, relying on
|
||||
"specification by implementation."
|
||||
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
Remember: syntax = how does the program look?
|
||||
|
||||
Programming is currently done largely as text files, which are
|
||||
really just long strings of characters where newlines give the
|
||||
appearance of some structure. We fead this string into a parser of
|
||||
some sort, which knows the language's syntax rules. The parser takes
|
||||
the string and converts into an *abstract syntax tree*, which is
|
||||
the computer's representation of the program in memory. **N.B.**:
|
||||
parsers are covered later.
|
||||
|
||||
Syntax provides a mechanism for the meaning of a program to be
|
||||
expressed, but it doesn't have any deeper meaning. The connection
|
||||
is entirely arbitrary, though there are common patterns that have
|
||||
arisen (c.f. the C-style of langauges).
|
||||
|
||||
|
||||
Operational Semantics
|
||||
---------------------
|
||||
|
||||
"The most practical way to think about the meaning of a program is
|
||||
*what it does*." That is, what happens when it runs? I really liked
|
||||
how Tom phrased it: "How do different constructs in the programming
|
||||
language behave at run time, and what effect do they have when
|
||||
they're plugged together to make larger programs?"
|
||||
|
||||
*Operational semantics* defines rules for how programs execute,
|
||||
typically on an *abstract machine*, allowing for more precise
|
||||
specifications of a language's behaviour.
|
||||
|
||||
|
||||
Small-Step Semantics
|
||||
--------------------
|
||||
|
||||
How do we go about designing an abstract machine? A first step is
|
||||
to think about how a program that works by reduction on the syntax.
|
||||
For example, evaluating (1 × 2) + (3 × 4):
|
||||
|
||||
1. First, the leftmost expression is reduced: × is applied to 1
|
||||
and 2. This leaves us with 2 + (3 × 4).
|
||||
2. The rightmost expression is reduced: × is applied to 3
|
||||
and 4. This leaves us with 2 + 12.
|
||||
3. Finally, + is applied to 2 and 12, yielding 14.
|
||||
4. 14 can't be reduced any further, so this is taken as the result
|
||||
of the expression. This is a *value*, which has a standalone
|
||||
meaning.
|
||||
|
||||
The rules for these reductions need to be codified in their own
|
||||
*metalanguage*. To explore the semantics of a very simple programming
|
||||
language called SIMPLE.
|
||||
|
||||
[ insert meaning_simple_semantics.png here ]
|
||||
|
||||
These are *inference rules* defining a *reduction relation* on
|
||||
SIMPLE ASTs. "Practically speaking, it's a bunch of weird symbols
|
||||
that don't say anything intelligible about the meaning of computer
|
||||
programs." Time to dive into an implementation!
|
||||
|
||||
Tom is careful to note that this isn't an attempt at specification
|
||||
by implementation; it's making the description more approachable
|
||||
for readers without a mathematical background.
|
||||
|
||||
|
||||
Expressions
|
||||
-----------
|
||||
|
||||
We can start by defining some types for the different elements ofthe AST.
|
||||
|
||||
> data Element = Number Int |
|
||||
> Boolean Bool |
|
||||
> Add Element Element |
|
||||
> Multiply Element Element |
|
||||
> LessThan Element Element |
|
||||
> Equals Element Element |
|
||||
> GreaterThan Element Element
|
||||
|
||||
For convenience, we'll override show. In the book, SIMPLE ASTs are
|
||||
displayed inside «».
|
||||
|
||||
> -- strElement returns the string representation of an AST element.
|
||||
> strElement :: Element -> String
|
||||
> strElement (Number n) = show n
|
||||
> strElement (Boolean b) = show b
|
||||
> strElement (Add a b) = (strElement a) ++ " + " ++ (strElement b)
|
||||
> strElement (Multiply a b) = (strElement a) ++ " × " ++ (strElement b)
|
||||
|
||||
> -- showElement implements show for Element.
|
||||
> showElement :: Element -> String
|
||||
> showElement e = "«" ++ (strElement e) ++ "»"
|
||||
> instance Show Element where show = showElement
|
||||
|
||||
These elements can be strung together to hand-build an AST:
|
||||
|
||||
> anExpression = Add (Multiply (Number 1) (Number 2))
|
||||
> (Multiply (Number 3) (Number 4))
|
||||
|
||||
This *should* display as
|
||||
|
||||
```
|
||||
Ok, modules loaded: Meaning.
|
||||
*Meaning>
|
||||
*Meaning> anExpression
|
||||
«1 × 2 + 3 × 4»
|
||||
```
|
||||
|
||||
Note that this doesn't take into account operator precedence, so
|
||||
`anExpression` is the same as `otherExpression` defined below:
|
||||
|
||||
> otherExpression = Multiply (Number 1)
|
||||
> (Multiply (Add (Number 2) (Number 3))
|
||||
> (Number 4))
|
||||
|
||||
With proper precendence, this should be `1 * (2 + 3) * 4`, but note
|
||||
|
||||
```
|
||||
*Meaning> anExpression
|
||||
«1 × 2 + 3 × 4»
|
||||
*Meaning> otherExpression
|
||||
«1 × 2 + 3 × 4»
|
||||
```
|
||||
|
||||
This is a problem, but it's not relevant to the discussion of the
|
||||
semantics of SIMPLE.
|
||||
|
||||
Now we need to implement a reducer. First, let's determine if an
|
||||
expression *can* be reduced:
|
||||
|
||||
> -- reducible returns true if the expression is reducible.
|
||||
> reducible :: Element -> Bool
|
||||
> reducible (Number _) = False
|
||||
> reducible _ = True
|
||||
|
||||
> -- reduce reduces the expression.
|
||||
> reduce :: Element -> Element
|
||||
> reduce n@(Number _) = n
|
||||
> reduce (Add (Number a) (Number b)) = Number (a + b)
|
||||
> reduce (Add a b) = if (reducible a)
|
||||
> then (Add (reduce a) b)
|
||||
> else (Add a (reduce b))
|
||||
> reduce (Multiply (Number a) (Number b)) = Number (a * b)
|
||||
> reduce (Multiply a b) = if (reducible a)
|
||||
> then (Multiply (reduce a) b)
|
||||
> else (Multiply a (reduce b))
|
||||
|
||||
Using `reduce` right now means repeatedly running `reduce` over
|
||||
functions:
|
||||
|
||||
```
|
||||
*Meaning> reduce anExpression
|
||||
«2 + 3 × 4»
|
||||
*Meaning> reduce (reduce anExpression)
|
||||
«2 + 12»
|
||||
*Meaning> reduce (reduce (reduce anExpression))
|
||||
«14»
|
||||
```
|
||||
|
||||
Note that `reduce` returns different expressions for `anExpression`
|
||||
and `otherExpression`, which demonstrates that the string display
|
||||
issue is tangential to our semantics:
|
||||
|
||||
```
|
||||
*Meaning> reduce anExpression
|
||||
«2 + 3 × 4»
|
||||
*Meaning> reduce otherExpression
|
||||
«1 × 5 × 4»
|
||||
```
|
||||
|
||||
Let's abstract this into a machine; note that this won't work quite
|
||||
like the Ruby examples in the book, as we can't really keep state
|
||||
in Haskell.
|
||||
|
||||
> -- Machine abstracts the SIMPLE VM.
|
||||
> data Machine = Machine Element
|
||||
> instance Show Machine where
|
||||
> show (Machine e) = "EXP ← " ++ (show e)
|
||||
|
||||
We can define two functions on it: `step` and `run`:
|
||||
|
||||
> -- step runs a single reduction.
|
||||
> step :: Machine -> Machine
|
||||
> step (Machine e) = Machine (reduce e)
|
||||
|
||||
> -- run keeps calling step until a non-reducible expression is
|
||||
> -- found.
|
||||
> run :: Machine -> IO Machine
|
||||
> run m@(Machine e) = do
|
||||
> putStrLn (show m)
|
||||
> if (reducible e)
|
||||
> then run (step m)
|
||||
> else return (Machine e)
|
||||
|
||||
Let's try this with `anExpression`:
|
||||
|
||||
> anMachine = Machine anExpression
|
||||
|
||||
```
|
||||
*Meaning> step anMachine
|
||||
EXP ← «2 + 3 × 4»
|
||||
*Meaning> run anMachine
|
||||
EXP ← «1 × 2 + 3 × 4»
|
||||
EXP ← «2 + 3 × 4»
|
||||
EXP ← «2 + 12»
|
||||
EXP ← «14»
|
||||
EXP ← «14»
|
||||
```
|
|
@ -0,0 +1,67 @@
|
|||
-- Initial uc.cabal generated by cabal init. For further documentation,
|
||||
-- see http://haskell.org/cabal/users-guide/
|
||||
|
||||
-- The name of the package.
|
||||
name: uc
|
||||
|
||||
-- The package version. See the Haskell package versioning policy (PVP)
|
||||
-- for standards guiding when and how versions should be incremented.
|
||||
-- https://wiki.haskell.org/Package_versioning_policy
|
||||
-- PVP summary: +-+------- breaking API changes
|
||||
-- | | +----- non-breaking API additions
|
||||
-- | | | +--- code changes with no API change
|
||||
version: 0.1.0.0
|
||||
|
||||
-- A short (one-line) description of the package.
|
||||
synopsis: Literate Haskell explorations from the book 'Understanding Computation'.
|
||||
|
||||
-- A longer description of the package.
|
||||
-- description:
|
||||
|
||||
-- The license under which the package is released.
|
||||
license: MIT
|
||||
|
||||
-- The file containing the license text.
|
||||
license-file: LICENSE
|
||||
|
||||
-- The package author(s).
|
||||
author: Kyle Isom
|
||||
|
||||
-- An email address to which users can send suggestions, bug reports, and
|
||||
-- patches.
|
||||
maintainer: kyle@imap.cc
|
||||
|
||||
-- A copyright notice.
|
||||
-- copyright:
|
||||
|
||||
category: Meta
|
||||
|
||||
build-type: Simple
|
||||
|
||||
-- Extra files to be distributed with the package, such as examples or a
|
||||
-- README.
|
||||
extra-source-files: ChangeLog.md
|
||||
|
||||
-- Constraint on the version of Cabal needed to build this package.
|
||||
cabal-version: >=1.10
|
||||
|
||||
|
||||
library
|
||||
-- Modules exported by the library.
|
||||
-- exposed-modules:
|
||||
|
||||
-- Modules included in this library but not exported.
|
||||
-- other-modules:
|
||||
|
||||
-- LANGUAGE extensions used by modules in this package.
|
||||
-- other-extensions:
|
||||
|
||||
-- Other library packages from which modules are imported.
|
||||
build-depends: base >=4.9 && <4.10
|
||||
|
||||
-- Directories containing source files.
|
||||
hs-source-dirs: src
|
||||
|
||||
-- Base language which the package is written in.
|
||||
default-language: Haskell2010
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
# # The Meaning of Programs
|
||||
# Let's define a simple *AST*, with three basic members. An AST node can
|
||||
# be reducible, like the + in (2 + 2), or not-reducible, like the
|
||||
# number 2. We'll also reduce from left to right.
|
||||
class Number:
|
||||
def __init__(self, value):
|
||||
self.value = int(value)
|
||||
def __repr__(self):
|
||||
return "#" + str(self.value)
|
||||
def is_reducible(self):
|
||||
return False
|
||||
|
||||
class Add:
|
||||
def __init__(self, l, r):
|
||||
self.left = l
|
||||
self.right = r
|
||||
def __repr__(self):
|
||||
return repr(self.left) + " + " + repr(self.right)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
if self.left.is_reducible():
|
||||
return Add(self.left.reduce(env), self.right)
|
||||
elif self.right.is_reducible():
|
||||
return Add(self.left, self.right.reduce(env))
|
||||
else:
|
||||
return Number(self.left.value + self.right.value)
|
||||
|
||||
class Mult:
|
||||
def __init__(self, l, r):
|
||||
self.left = l
|
||||
self.right = r
|
||||
def __repr__(self):
|
||||
return repr(self.left) + " * " + repr(self.right)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
if self.left.is_reducible():
|
||||
return Mult(self.left.reduce(env), self.right)
|
||||
elif self.right.is_reducible():
|
||||
return Mult(self.left, self.right.reduce(env))
|
||||
else:
|
||||
return Number(self.left.value * self.right.value)
|
||||
|
||||
ast1 = Add(Mult(Number(1), Number(2)),
|
||||
Mult(Number(3), Number(4)))
|
||||
|
||||
# It would be nice to have a machine that could run these, so we don't
|
||||
# have to run them by hand.
|
||||
|
||||
class Machine:
|
||||
def __init__(self, expr, env):
|
||||
self.expr = expr
|
||||
self.env = env
|
||||
self.iters = 0
|
||||
|
||||
def step(self):
|
||||
self.iters += 1
|
||||
self.expr = self.expr.reduce(self.env)
|
||||
|
||||
def run(self):
|
||||
while self.expr.is_reducible():
|
||||
print self
|
||||
self.step()
|
||||
print "execution complete in ", self.iters, " steps."
|
||||
return self.expr
|
||||
|
||||
def __repr__(self):
|
||||
return "(" + str(self.iters) + ") " + repr(self.expr) + "\n" + repr(self.env)
|
||||
|
||||
class Boolean:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __repr__(self):
|
||||
return str(self.value).lower()
|
||||
def is_reducible(self):
|
||||
return False
|
||||
|
||||
class LessThan:
|
||||
def __init__(self, l, r):
|
||||
self.left = l
|
||||
self.right = r
|
||||
def __repr__(self):
|
||||
return repr(self.left) + " < " + repr(self.right)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
if self.left.is_reducible():
|
||||
return LessThan(self.left.reduce(env), self.right)
|
||||
elif self.right.is_reducible():
|
||||
return LessThan(self.left, self.right.reduce(env))
|
||||
else:
|
||||
return Boolean(self.left.value < self.right.value)
|
||||
|
||||
ast2 = LessThan(Number(5), Add(Number(2), Number(2)))
|
||||
ast2b = LessThan(Add(Number(2), Number(2)), Number(5))
|
||||
|
||||
class Variable:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
def __repr__(self):
|
||||
return "$" + self.name
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
return env[self.name]
|
||||
|
||||
ast3 = Add(Variable('x'), Variable('y'))
|
||||
env3 = {'x': Number(3), 'y': Number(4)}
|
||||
m3 = lambda : Machine(ast3, env3)
|
||||
|
||||
class Nop:
|
||||
def __init__(self):
|
||||
pass
|
||||
def __repr__(self):
|
||||
return "@NOP"
|
||||
def eq(self, st):
|
||||
return st.instanceof(Nop)
|
||||
def is_reducible(self):
|
||||
return False
|
||||
|
||||
class Assign:
|
||||
def __init__(self, name, expr):
|
||||
self.name = name
|
||||
self.expr = expr
|
||||
def __repr__(self):
|
||||
return self.name + " = " + repr(self.expr)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
|
||||
def reduce(self, env):
|
||||
if self.expr.is_reducible():
|
||||
return Assign(self.name, self.expr.reduce(env)), env.copy()
|
||||
else:
|
||||
env[self.name] = self.expr
|
||||
return Nop(), env.copy()
|
||||
|
||||
stmt4 = Assign("x", Add(Variable("x"), Number(1)))
|
||||
env4 = {"x": Number(2)}
|
||||
|
||||
class Machine2:
|
||||
def __init__(self, stmt, env):
|
||||
self.stmt = stmt
|
||||
self.env = env
|
||||
self.iters = 0
|
||||
def __repr__(self):
|
||||
return "(" + str(self.iters) + ") " + repr(self.stmt) + "\n" + repr(self.env)
|
||||
def step(self):
|
||||
self.iters += 1
|
||||
self.stmt, self.env = self.stmt.reduce(self.env)
|
||||
print self
|
||||
|
||||
def run(self):
|
||||
while self.stmt.is_reducible():
|
||||
self.step()
|
||||
print "execution complete in ", self.iters, " steps."
|
||||
print self
|
||||
|
||||
stmt5 = Assign("x", Add(Variable("x"), Number(1)))
|
||||
env5 = {"x": Number(2)}
|
||||
m5 = lambda : Machine2(stmt5, env5)
|
||||
|
||||
class If:
|
||||
def __init__(self, cond, cons, alt):
|
||||
self.cond = cond
|
||||
self.cons = cons
|
||||
self.alt = alt
|
||||
def __repr__(self):
|
||||
s = "if |" + repr(self.cond) + "| then |" + repr(self.cons)
|
||||
return s + "| else |" + repr(self.alt) + "|"
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
if self.cond.is_reducible():
|
||||
return If(self.cond.reduce(env), self.cons, self.alt), env.copy()
|
||||
elif self.cond.value:
|
||||
return self.cons, env.copy()
|
||||
else:
|
||||
return self.alt, env.copy()
|
||||
|
||||
stmt6 = If(Variable("x"), Assign("y", Number(1)), Assign("y", Number(2)))
|
||||
env6 = {"x": Boolean(True)}
|
||||
m6 = lambda : Machine2(stmt6, env6)
|
||||
|
||||
class Sequence:
|
||||
def __init__(self, first, second):
|
||||
self.first = first
|
||||
self.second = second
|
||||
def __repr__(self):
|
||||
return repr(self.first) + ";" + repr(self.second)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
if isinstance(self.first, Nop):
|
||||
return self.second, env.copy()
|
||||
else:
|
||||
first, env = self.first.reduce(env)
|
||||
return Sequence(first, self.second), env.copy()
|
||||
|
||||
stmt7 = Sequence(Assign("x", Add(Number(1), Number(1))),
|
||||
Assign("y", Add(Variable("x"), Number(3))))
|
||||
env7 = {}
|
||||
m7 = lambda : Machine2(stmt7, env7)
|
||||
|
||||
class While:
|
||||
def __init__(self, cond, body):
|
||||
self.cond = cond
|
||||
self.body = body
|
||||
def __repr__(self):
|
||||
return "while |" + repr(self.cond) + "|:\n\t" + repr(self.body)
|
||||
def is_reducible(self):
|
||||
return True
|
||||
def reduce(self, env):
|
||||
return If(self.cond, Sequence(self.body, self), Nop()), env.copy()
|
||||
|
||||
stmt8 = While(LessThan(Variable("x"), Number(5)),
|
||||
Assign("x", Mult(Variable("x"), Number(3))))
|
||||
env8 = {"x": Number(1)}
|
||||
m8 = lambda : Machine2(stmt8, env8)
|
Loading…
Reference in New Issue