From 553164310c13b8671ea21f582239a70f8264df84 Mon Sep 17 00:00:00 2001 From: Franklin Chen Date: Fri, 16 Oct 2015 18:51:33 -0400 Subject: [PATCH] Franklin's notes and solutions in progress. --- Exercises/FranklinChen/haskell/Chapter1.hs | 30 ++ Notes/FranklinChen/chapter1.md | 324 ++++++++++++++++++ Notes/FranklinChen/chapter2.md | 1 + Notes/FranklinChen/elm/.gitignore | 1 + Notes/FranklinChen/elm/Chapter1.elm | 68 ++++ Notes/FranklinChen/elm/elm-package.json | 14 + Notes/FranklinChen/foreword.md | 59 ++++ Notes/FranklinChen/haskell/Chapter1.hs | 18 + .../FranklinChen/preface-to-first-edition.md | 42 +++ .../FranklinChen/preface-to-second-edition.md | 18 + 10 files changed, 575 insertions(+) create mode 100644 Exercises/FranklinChen/haskell/Chapter1.hs create mode 100644 Notes/FranklinChen/chapter1.md create mode 100644 Notes/FranklinChen/chapter2.md create mode 100644 Notes/FranklinChen/elm/.gitignore create mode 100644 Notes/FranklinChen/elm/Chapter1.elm create mode 100644 Notes/FranklinChen/elm/elm-package.json create mode 100644 Notes/FranklinChen/foreword.md create mode 100644 Notes/FranklinChen/haskell/Chapter1.hs create mode 100644 Notes/FranklinChen/preface-to-first-edition.md create mode 100644 Notes/FranklinChen/preface-to-second-edition.md diff --git a/Exercises/FranklinChen/haskell/Chapter1.hs b/Exercises/FranklinChen/haskell/Chapter1.hs new file mode 100644 index 0000000..0b6ca88 --- /dev/null +++ b/Exercises/FranklinChen/haskell/Chapter1.hs @@ -0,0 +1,30 @@ +{-# LANGUAGE BangPatterns #-} + +module Chapter1 where + +-- | 1.5 +p () = p () + +-- | Note that Haskell is normal-order by default. +test x y = + if x == 0 + then 0 + else y + +-- | Evaluation succeeds because p () is never evaluated. +-- +-- 0 +result_1_5_returns = test 0 (p ()) + +-- | In Haskell, you can also force evaluation manually with $! +result_1_5_never_returns1 = test 0 $! p () + +-- | We can force applicative-order with a strictness annotation on +-- the called function so that all callers are affected. +testStrict x !y = + if x == 0 + then 0 + else y + +-- | This never returns either. +result_1_5_never_returns2 = testStrict 0 (p ()) diff --git a/Notes/FranklinChen/chapter1.md b/Notes/FranklinChen/chapter1.md new file mode 100644 index 0000000..5802cd8 --- /dev/null +++ b/Notes/FranklinChen/chapter1.md @@ -0,0 +1,324 @@ +# My notes: Chapter 1: Building abstractions with procedures + +## My remark on "procedure" + +Interesting that the word "procedure" is used, since at some point in +the 1980s, thanks to the influence of C, which did not distinguish +between "procedure" and "function", people started calling everything +"function" instead. + +> computation process + +> Computational processes are abstract beings that inhabit computers. + +## Lisp + +> Lisp descriptions of processes, called procedures, can themselves be +> represented and manipulated as Lisp data. + +My comment: in retrospect, this is slightly overstated and misleading: + +- In 1984, Standard ML and Haskell, providing very concise ways of + writing ASTs using algebraic data types, were not to be unleashed on + the world until 1990. +- It will always still be true, however, that working on S-expressions + as ASTs is extremely convenient, and I sure miss that when not + working in Lisp! + +## 1.1: Elements of programming + +- primitive expressions +- means of combination +- means of abstraction + +> examine some typical interactions with an interpreter + +My comment: strictly speaking, it may not be an interpreter. It's a +REPL, which may be connected to a compiler or an +interpreter. Unfortunately, for decades now, many newcomers to +programming are confused about the difference between an interpreter +and a compiler, and associate the existence of a REPL with being an +"interpreter". This may change, now that probably most newer languages +with compiled implementations also have a REPL. + +(TODO mention REPLs for C++, Swift, upcoming Java 9.) + +### Naming + +- variable +- value + +> name things with `define` + +This was uncommon back in 1984, where if you were using Pascal or C, +you don't just name things. In those languages, you write declarations +that "allocate storage" for things that are not yet constructed, and +then you fill the storage (possibly through immediate initialization, +but sometimes with delayed construction). It's a different kind of +model of computation. + +```scheme +(define circumference (* 2 pi radius)) +``` + +### Procedure definitions + +```scheme +(define (square x) (* x x)) +``` + +I don't like this pedagogy, because it makes functions seem special +when they are not. I would teach functions with a lambda up front, so +that + +```scheme +(define square + (lambda (x) (* x x))) +``` + +makes it clear that we are defining `square` to be a value that +happens to be `(lambda (x) (* x x))`, just as `circumference` above +was a definition of something that happens not to be a lambda. + +I have seen many students get confused about the status of functions +when they learn this shortcut syntax first instead of the core syntax. + +### Substitution model for procedure application + +In retrospect, having studied evaluation models formally, the +presentation here is on the mysterious side, I think. + +(TODO Link to formal operational semantics presentations.) + +### Applicative versus normal order + +These correspond to "call by value" and "call by name". + +### Conditional expressions + +#### `cond` + +I don't like `cond` at all. + +I distinctly remember first learning `cond`. I found it difficult to +read because of all the nesting, and also difficult to understand in +full generality (some of which is mentioned in +[this footnote](https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-10.html#call_footnote_Temp_26). + +Clojure's [`cond`](https://clojuredocs.org/clojure.core/cond) +is arguably friendlier than Scheme's. + +As of October 2015, Elm 1.6 is about to be released, and word is that +its `cond`-equivalent is going to be removed from the language because +of [usability issues](https://github.com/elm-lang/elm-plans/issues/6). + +#### My comment on Scheme's commitment to truthiness + +Note the footnote about conditionals in Scheme. Today we call Scheme +"truthy" (because of many languages that were inspired to do similar +things rather than have a strict two-value boolean notion of +conditional). + +> "Interpreted as either true or false" means this: In Scheme, there +> are two distinguished values that are denoted by the constants `#t` +> and `#f`. When the interpreter checks a predicate's value, it +> interprets `#f` as false. Any other value is treated as true. + +There are reasons Scheme chose truthiness (at least in slightly better +form than Lisp's). (TODO Explain this later.) + +But I think "truthiness" is simply a mistake in programming languages, +and that the past thirty years have shown this (see how it's affected +readability and correctness in C, C++, Perl, Ruby, JavaScript for +example). (TODO gather links on the gotchas from allowing truthiness.) + +#### My comment on case analysis + +A great advantage of the ML family of languages from 1990 on is that +case analysis can be done in a more formal way, and checked for +exhaustiveness by the compiler, when done as "pattern matching" on a +type. + +For example, + +```scheme +(define (abs x) + (cond ((> x 0) x) + ((= x 0) 0) + ((< x 0) (- x)))) +``` + +in Haskell would be defined as + +```haskell +abs :: (Num a, Ord a) => a -> a +abs x = + case compare x 0 of + GT -> x + EQ -> 0 + LT -> -x +``` + +An even more sophisticated Rust version (see [playground](http://is.gd/dK0D41)): + +```rust +use std::cmp::Ordering; +use std::num::Zero; +use std::ops::Neg; + +fn abs>(x: T) -> T { + match x.cmp(&T::zero()) { + Ordering::Greater => x, + Ordering::Equal => T::zero(), + Ordering::Less => -x, + } +} +``` + +It is outside the scope of this chapter to explain the Haskell and +Rust implementations, but this comparison with Scheme brings up a +pedagogical matter: it does seem that starting out programming with an +dynamically typed language like Scheme is easier, because you don't +have to deal up front with the details of expressive type systems. + +#### `and`, `or` + +These are "truthy" operators. + +### Newton's method + +I think this math/engineering-centric example is a distraction from +teaching general programming because not everyone is an MIT EECS +major! + +### Black box abstractions + +Local names, internal definitions, block structure. + +My comment: I know it's too early, but internal definitions of +functions is really sugar for `letrec` underneath. + +#### My comment on modules + +I think this 1984 presentation of the virtues of lexical scoping +for hiding information is problematic. Recall that Pascal at the time +had internal definitions also, so this was part of the culture before +**modules** came into the picture. + +Modules are a far better way to hide information *selectively*, rather +than permanently. For example, when unit testing "helper functions", +one wants them to be visible, not hidden as an internal definition +inside another function. + +Module systems were in the air in the 1970s and 1980s (see Clu, Ada, +Modula-2, Standard ML) but Scheme decided not to standardize on them +because of a lot of different competing proposals and implementations. +Note that Common Lisp in 1984 did introduce a package system. + +On the other hand, it is pedagogically useful to show how a module +system can be implemented if one doesn't already have one. This is +done later in SICP. Amusingly, thirty years after SICP, JavaScript is +still finalizing a standard module system, illustrating how +contentious the subject is if a module system is not provided built +into a language from the start! + +##### Current state of modules for Scheme + +I looked at the current state of +[Scheme standardization](http://trac.sacrideo.us/wg/wiki) and it looks +like +[modules are still being discussed](http://trac.sacrideo.us/wg/wiki/ModuleSystems) +after R7RS, +decades later. [R6RS](http://www.r6rs.org/) in 2007 did apparently +[define a module system](http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-10.html#node_chap_7). + +I'm going to make a confession here. I gave up on Scheme after 1997 (I +last used the R4RS standard) in part because of no standardization of +a module system. This was a huge problem. If you wanted to use +someone's Scheme code, it either didn't use modules at all or used the +module system of one of dozens of competing implementations of Scheme, +and if you were in luck, you might be able to do some copy/pasting to +get it working with yours. + +## 1.2: Procedures and the processes they generate + +I think this discussion of recursion is problematic. I was a TA for a +computer science class at CMU that used this kind of example (with +Standard ML as language), and I heard the comments by many +students. Many came away thinking "recursion is pointless and +inefficient" and "tail recursion is a cute trick". I haven't yet +decided how to explain this stuff better. I do know that I'm tired of +factorial. + +### Tail recursion + +Also, the fact that tail recursion is not available in many languages +gives students the impression that languages like Scheme are +pointless! + +[ECMAScript6](http://www.ecma-international.org/ecma-262/6.0/) has +[tail call elimination](http://www.ecma-international.org/ecma-262/6.0/#sec-tail-position-calls), +however. This is hugely important for useful functional idioms, but +also +object-oriented idioms, as +[passionately argued by Guy Steele in 2009](http://www.eighty-twenty.org/2011/10/01/oo-tail-calls.html). Note +he was the very inventor of Scheme in 1975, the first language to +mandate tail call elimination, so he's been waiting forty years for +language implementations to get this right! + +### Tree recursion + +I hate Fibonacci. + +> One should not conclude from this that tree-recursive processes are +> useless. When we consider processes that operate on hierarchically +> structured data rather than numbers, we will find that tree +> recursion is a natural and powerful tool. + +So why present an example that is useless and misleading? This was +terrible pedagogy. Recursion is super-important, and yet the first two +examples presented are completely pointless (factorial and Fibonacci). + +#### Counting change + +This is a tricky problem, and I don't like the solution, for a number +of reasons. + +(TODO Show a better solution and explain why.) + +### Orders of growth + +This is an abbreviated discussion of a much larger topic of +computational complexity. The discussion here is much too vague and +brief, completely horrible. + +Also, today, one could at least show live animations and graphs of all +this stuff. + +(TODO Link to good resources.) + +### Exponentiation + +More math! + +## 1.3: Formulating abstractions with higher-order procedures + +Good to bring in higher-order right away. + +More math. + +### `lambda` + +Finally introduced here. I would have done it on day one when defining +functions. + +`let` for local variables. + +I like that it discusses how `let` is just the same as applying a +`lambda` to stuff. + +The syntax for `let` is somewhat verbose because of nesting (again, +something that Clojure changed). + +There's a subtlety here in the difference with `let*` is not +discussed. diff --git a/Notes/FranklinChen/chapter2.md b/Notes/FranklinChen/chapter2.md new file mode 100644 index 0000000..83a3b4c --- /dev/null +++ b/Notes/FranklinChen/chapter2.md @@ -0,0 +1 @@ +# My notes: Chapter 2: Building abstractions with data diff --git a/Notes/FranklinChen/elm/.gitignore b/Notes/FranklinChen/elm/.gitignore new file mode 100644 index 0000000..aee9810 --- /dev/null +++ b/Notes/FranklinChen/elm/.gitignore @@ -0,0 +1 @@ +/elm-stuff/ diff --git a/Notes/FranklinChen/elm/Chapter1.elm b/Notes/FranklinChen/elm/Chapter1.elm new file mode 100644 index 0000000..e264856 --- /dev/null +++ b/Notes/FranklinChen/elm/Chapter1.elm @@ -0,0 +1,68 @@ +module Chapter1 where + +{-| Warning: this shadows the implicitly imported `abs`. +-} +abs x = + case compare x 0 of + GT -> x + EQ -> 0 + LT -> -x + + +square x = x*x + +average x y = (x+y)/2 + +{-| Newton's method. + TODO Prefer to write without explicit recursion. +-} +sqrtIter guess x = + if goodEnough guess x + then guess + else sqrtIter (improve guess x) x + +improve guess x = average guess (x/guess) + +goodEnough guess x = abs (square guess - x) < 0.001 + +ourSqrt x = sqrtIter 1.0 x + +{-| With information hiding. + My notes mention that hiding should be done with modules instead. +-} +ourSqrt2 x = + let goodEnough guess x = abs (square guess - x) < 0.001 + improve guess x = average guess (x/guess) + sqrtIter guess x = + if goodEnough guess x + then guess + else sqrtIter (improve guess x) x + in sqrtIter 1.0 x + + +{-| Counting change. +-} +countChange amount = + cc amount 5 + +-- Breaking news: Elm 1.6 will be removing its cond-equivalent +-- multiway conditional because of usability issues, so I'm using +-- a standard if/else instead. +cc amount kindsOfCoins = + if amount == 0 + then 1 + else if amount < 0 || kindsOfCoins == 0 + then 0 + else cc amount (kindsOfCoins-1) + + cc (amount - firstDenomination kindsOfCoins) kindsOfCoins + +firstDenomination kindsOfCoins = + case kindsOfCoins of + 1 -> 1 + 2 -> 5 + 3 -> 10 + 4 -> 25 + 5 -> 50 + +-- 292 +result_100 = countChange 100 diff --git a/Notes/FranklinChen/elm/elm-package.json b/Notes/FranklinChen/elm/elm-package.json new file mode 100644 index 0000000..fe5eeb8 --- /dev/null +++ b/Notes/FranklinChen/elm/elm-package.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0", + "summary": "helpful summary of your project, less than 80 characters", + "repository": "https://github.com/USER/PROJECT.git", + "license": "BSD3", + "source-directories": [ + "." + ], + "exposed-modules": [], + "dependencies": { + "elm-lang/core": "2.1.0 <= v < 3.0.0" + }, + "elm-version": "0.15.1 <= v < 0.16.0" +} \ No newline at end of file diff --git a/Notes/FranklinChen/foreword.md b/Notes/FranklinChen/foreword.md new file mode 100644 index 0000000..e383663 --- /dev/null +++ b/Notes/FranklinChen/foreword.md @@ -0,0 +1,59 @@ +# My notes: Foreword by Alan Perlis + +> Every computer program is a model, hatched in the mind, of a real or +> mental process. + +> ...we become convinced of program truth through argument. + +## His comments on Lisp + +> The list, Lisp's native data structure, is largely responsible for +> such growth of utility. + +> In Pascal the plethora of declarable data structures induces a +> specialization within functions that inhibits and penalizes casual +> cooperation. It is better to have 100 functions operate on one data +> structure than to have 10 functions operate on 10 data structures. + +This was written in 1984, when the current Algol-based typed languages +were quite impoverished. The rise of Standard ML and Haskell in 1990 +completely changed the landscape, though, I believe. The list is still +very important even in ML and Haskell, but by the 1990s, it was +already clear that overuse of lists in Lisp was a problem. + +## Looking back at Lisp from today + +### Norvig on Lisp + +Peter Norvig in his 1992 book (which I highly recommend as a deep dive +into programming in Lisp) +["Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp "](http://norvig.com/paip.html) +was already writing, +["Lisp makes it easy to rely on lists, but one must avoid the temptation to overuse lists; to use them where another data structure is more appropriate."](https://books.google.com/books?id=eH6jBQAAQBAJ&pg=PA340&lpg=PA340&dq=lisp+overuse+lists&source=bl&ots=Yiyxi4n9k7&sig=ANeroOFa0ujE4smt-QY2n_cGu0U&hl=en&sa=X&ved=0CDYQ6AEwBGoVChMIv-y42sbHyAIVQjg-Ch0F2gYn#v=onepage&q=lisp%20overuse%20lists&f=false) +(Note that Norvig himself switched to Python in around 2000 and was +instrumental to popularizing the language at Google and by extension +globally. Some links to his thoughts on Python: + +- [Python for Lisp Programmers](http://norvig.com/python-lisp.html) +- [Why he went Python](https://news.ycombinator.com/item?id=1803815) +- [Some beautiful IPython demo notebooks](http://norvig.com/ipython/) (he +regularly posts new ones) + +### Peter Seibel on Lisp + +Peter Seibel in his excellent 2005 book (now freely available online) +["Practical Common Lisp"](http://www.gigamonkeys.com/book/) wrote: + +["Historically, lists were Lisp's original composite data type, though it has been decades since they were its only such data type. These days, a Common Lisp programmer is as likely to use a vector, a hash table, or a user-defined class or structure as to use a list."](http://www.gigamonkeys.com/book/they-called-it-lisp-for-a-reason-list-processing.html) + +Interestingly, he has become an active user of Haskell in recent +years. Also, last year he taught his 8-year-old daughter Haskell and +they [coded something together](https://twitter.com/peterseibel/status/579793703193051136). + +### My observation of Lisp + +Lisp is still very much alive, and I believe it will never die, but +the comparison to Pascal is now irrelevant 30 years after the +publication of this foreword. Most languages now have built-in data +types or allow ease in creating them. You could say that many +languages have stolen many good ideas from Lisp. diff --git a/Notes/FranklinChen/haskell/Chapter1.hs b/Notes/FranklinChen/haskell/Chapter1.hs new file mode 100644 index 0000000..f7b952f --- /dev/null +++ b/Notes/FranklinChen/haskell/Chapter1.hs @@ -0,0 +1,18 @@ +module Chapter1 where + +-- | Newton's method. +-- TODO Prefer to write without explicit recursion. +sqrtIter guess x + if goodEnough guess x + then guess + else sqrtIter (improve guess x) x + +improve guess x = average guess (x/guess) + +average x y = (x+y)/2 + +goodEnough guess x = abs (square guess - x) < 0.001 + +ourSqrt x = sqrtIter 1.0 x + +square x = x*x diff --git a/Notes/FranklinChen/preface-to-first-edition.md b/Notes/FranklinChen/preface-to-first-edition.md new file mode 100644 index 0000000..345b1b2 --- /dev/null +++ b/Notes/FranklinChen/preface-to-first-edition.md @@ -0,0 +1,42 @@ +# My notes: Preface to first edition + +> "The Structure and Interpretation of Computer Programs" is the +> entry-level subject in computer science at the Massachusetts +> Institute of Technology. It is required of all students at MIT who +> major in electrical engineering or in computer science. + +> ...a computer language is not just a way of getting a computer to +> perform operations but rather that it is a novel formal medium for +> expressing ideas about methodology. + +> Scheme, the dialect of Lisp that we use, is an attempt to bring +> together the power and elegance of Lisp and Algol. + +## My comment + +This was 1984. + +In 1991, I dropped out of grad school in physics and had no plan for +my life. I was going to apply for a job as an actuary, but then +suddenly decided that I would be bored out of my mind in that +profession. Friends suggested that I go into computer programming, but +I had been too scared to take intro computer science in college, and +therefore did not write a single line of code in college and all I +knew was some BASIC, COBOL, and Pascal programming from high +school. Friends said there were jobs if you knew C and UNIX (two +things that had scared me away from freshman computer science!). One +friend, who graduated from MIT in EECS, told me I should learn +computer science for real first, by studying the textbook SICP he used +as a freshman at MIT, which used a much friendlier language called +Scheme in order to teach concepts. + +So I bought a copy of SICP and self-studied it and loved it. I bought +a used Macintosh SE/30 (since my Apple IIe I used throughout college +just for word processing was underpowered by 1991), and although +frustrated I could not get MIT Scheme for it, I found a nice Scheme +interpreter, MacGambit, and began working through the book, writing +shims as needed for MIT Scheme constructs. + +Later in the book, I switched to using a Dylan implementation, in its +original Lisp-style S-expression syntax before Dylan officially +switched to an Algol-style infix syntax! diff --git a/Notes/FranklinChen/preface-to-second-edition.md b/Notes/FranklinChen/preface-to-second-edition.md new file mode 100644 index 0000000..b2dd4a5 --- /dev/null +++ b/Notes/FranklinChen/preface-to-second-edition.md @@ -0,0 +1,18 @@ +# My notes: Preface to second edition + +## My comment + +The second edition was published in 1996, twelve years after +the 1984 first edition. + +## Changes + +They rewrote the Scheme code to conform to the IEEE 1990 +standard. Good, I had trouble with the 1984 edition because it used +weird stuff from MIT Scheme. + +> We have included new sections on concurrency and nondeterminism. + +Uh, note that `www-mitpress.mit.edu/sicp` is a dead link now, +in 2015. How the Web works. The proper link now is +[`https://mitpress.mit.edu/sicp/`](https://mitpress.mit.edu/sicp/).