commit 36ed6df77f25856b0c9f7e1acb0e7f7a44bf1271 Author: Kyle Isom Date: Tue Jan 16 09:46:44 2018 -0800 Initial import. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..8ef9c61 --- /dev/null +++ b/README.rst @@ -0,0 +1,12 @@ +SANDBOX +======= + +This is a repository of various repositories for learning or exploring +topics. + +Projects +-------- + ++ ``lpn/``: `Learn Prolog Now `_ ++ ``uc/``: `Understanding Computation `_ + diff --git a/lpn/crossword.pl b/lpn/crossword.pl new file mode 100644 index 0000000..a22406f --- /dev/null +++ b/lpn/crossword.pl @@ -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. diff --git a/lpn/eating.pl b/lpn/eating.pl new file mode 100644 index 0000000..65b6f07 --- /dev/null +++ b/lpn/eating.pl @@ -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). diff --git a/lpn/kb1.pl b/lpn/kb1.pl new file mode 100644 index 0000000..19fcf4c --- /dev/null +++ b/lpn/kb1.pl @@ -0,0 +1,5 @@ +woman(mia). +woman(jody). +woman(yolanda). +playsAirGuitar(jody). +party. diff --git a/lpn/kb2.pl b/lpn/kb2.pl new file mode 100644 index 0000000..e93fc2e --- /dev/null +++ b/lpn/kb2.pl @@ -0,0 +1,6 @@ +happy(yolanda). +listens2Music(mia). +listens2Music(yolanda):- happy(yolanda). +playsAirGuitar(mia):- listens2Music(mia). +playsAirGuitar(yolanda):- listens2Music(yolanda). +listens2Music(bruce). diff --git a/lpn/kb3.pl b/lpn/kb3.pl new file mode 100644 index 0000000..bc9afcd --- /dev/null +++ b/lpn/kb3.pl @@ -0,0 +1,9 @@ +happy(vincent). +listens2Music(butch). +playsAirGuitar(vincent):- + listens2Music(vincent), + happy(vincent). +playsAirGuitar(butch):- + happy(butch). +playsAirGuitar(butch):- + listens2Music(butch). diff --git a/lpn/kb4.pl b/lpn/kb4.pl new file mode 100644 index 0000000..fbf67bc --- /dev/null +++ b/lpn/kb4.pl @@ -0,0 +1,8 @@ +woman(mia). +woman(jody). +woman(yolanda). + +loves(vincent,mia). +loves(marsellus,mia). +loves(pumpkin,honey_bunny). +loves(honey_bunny,pumpkin). diff --git a/lpn/kb5.pl b/lpn/kb5.pl new file mode 100644 index 0000000..3419ac2 --- /dev/null +++ b/lpn/kb5.pl @@ -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. diff --git a/lpn/lexicon.pl b/lpn/lexicon.pl new file mode 100644 index 0000000..0aace82 --- /dev/null +++ b/lpn/lexicon.pl @@ -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). diff --git a/lpn/lines.pl b/lpn/lines.pl new file mode 100644 index 0000000..f2de090 --- /dev/null +++ b/lpn/lines.pl @@ -0,0 +1,2 @@ +vertical(line(point(X,Y),point(X,Z))). +horizontal(line(point(X,Y),point(Z,Y))). diff --git a/lpn/proof22.pl b/lpn/proof22.pl new file mode 100644 index 0000000..f9109bb --- /dev/null +++ b/lpn/proof22.pl @@ -0,0 +1,9 @@ +f(a). +f(b). + +g(a). +g(b). + +h(b). + +k(X) :- f(X), g(X), h(X). diff --git a/lpn/test.pl b/lpn/test.pl new file mode 100644 index 0000000..13f6ac8 --- /dev/null +++ b/lpn/test.pl @@ -0,0 +1,3 @@ +listensToMusic(X) :- happy(X). +playsAirGuitar(X) :- listensToMusic(X). +happy(a). diff --git a/uc/README.txt b/uc/README.txt new file mode 100644 index 0000000..34b26d3 --- /dev/null +++ b/uc/README.txt @@ -0,0 +1,2 @@ +This is the superproject where all my attempts at working through the +book "Understanding Computation" are. diff --git a/uc/cl/README.txt b/uc/cl/README.txt new file mode 100644 index 0000000..8ab5fce --- /dev/null +++ b/uc/cl/README.txt @@ -0,0 +1 @@ +Lisp code supporting "Universal Computation". diff --git a/uc/cl/meaning.lisp b/uc/cl/meaning.lisp new file mode 100644 index 0000000..f8471f5 --- /dev/null +++ b/uc/cl/meaning.lisp @@ -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)) + diff --git a/uc/cl/package.lisp b/uc/cl/package.lisp new file mode 100644 index 0000000..4f8e56e --- /dev/null +++ b/uc/cl/package.lisp @@ -0,0 +1,5 @@ +;;;; package.lisp + +(defpackage #:uc + (:use #:cl)) + diff --git a/uc/cl/uc.asd b/uc/cl/uc.asd new file mode 100644 index 0000000..3f047f5 --- /dev/null +++ b/uc/cl/uc.asd @@ -0,0 +1,11 @@ +;;;; uc.asd + +(asdf:defsystem #:uc + :serial t + :description "Describe uc here" + :author "Your Name " + :license "Specify license here" + :components ((:file "package") + (:file "uc") + (:file "meaning"))) + diff --git a/uc/cl/uc.lisp b/uc/cl/uc.lisp new file mode 100644 index 0000000..bdc02d3 --- /dev/null +++ b/uc/cl/uc.lisp @@ -0,0 +1,6 @@ +;;;; uc.lisp + +(in-package #:uc) + +;;; "uc" goes here. Hacks and glory await! + diff --git a/uc/hs-simple/README.txt b/uc/hs-simple/README.txt new file mode 100644 index 0000000..8643f65 --- /dev/null +++ b/uc/hs-simple/README.txt @@ -0,0 +1,2 @@ +## understanding computation + diff --git a/uc/hs-simple/meaning.hs b/uc/hs-simple/meaning.hs new file mode 100644 index 0000000..61cd02e --- /dev/null +++ b/uc/hs-simple/meaning.hs @@ -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)) + diff --git a/uc/hs-simple/meaning.md b/uc/hs-simple/meaning.md new file mode 100644 index 0000000..afbde80 --- /dev/null +++ b/uc/hs-simple/meaning.md @@ -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*. diff --git a/uc/hs-simple/programs_and_machines.lhs b/uc/hs-simple/programs_and_machines.lhs new file mode 100644 index 0000000..e060762 --- /dev/null +++ b/uc/hs-simple/programs_and_machines.lhs @@ -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/. diff --git a/uc/hs/ChangeLog.md b/uc/hs/ChangeLog.md new file mode 100644 index 0000000..1dbca41 --- /dev/null +++ b/uc/hs/ChangeLog.md @@ -0,0 +1,5 @@ +# Revision history for uc + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world. diff --git a/uc/hs/LICENSE b/uc/hs/LICENSE new file mode 100644 index 0000000..a03191a --- /dev/null +++ b/uc/hs/LICENSE @@ -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. diff --git a/uc/hs/Setup.hs b/uc/hs/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/uc/hs/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/uc/hs/images/meaning_simple_semantics.png b/uc/hs/images/meaning_simple_semantics.png new file mode 100644 index 0000000..c6bc523 Binary files /dev/null and b/uc/hs/images/meaning_simple_semantics.png differ diff --git a/uc/hs/src/Meaning.lhs b/uc/hs/src/Meaning.lhs new file mode 100644 index 0000000..db497d1 --- /dev/null +++ b/uc/hs/src/Meaning.lhs @@ -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» +``` diff --git a/uc/hs/uc.cabal b/uc/hs/uc.cabal new file mode 100644 index 0000000..eda9b39 --- /dev/null +++ b/uc/hs/uc.cabal @@ -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 + diff --git a/uc/py/simple.py b/uc/py/simple.py new file mode 100644 index 0000000..1efdbe2 --- /dev/null +++ b/uc/py/simple.py @@ -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)