Cognitive Modeling Language (CML) vs Imperative Programming vs Declarative Programming - imperative-programming

I'm reading this article by John Funge about Cognitive Modeling for Computer Games: http://www.qrg.northwestern.edu/resources/aigames.org/1999/fungegame99.pdf
And some further detailed reading about it in this URL:
http://www.msci.memphis.edu/~classweb/public_html/comp7990/Spring2000/Wally/Presentation5/tsld014.htm
I'm having a hard time to understand the CML:
I understand it's some kind of mix between Imperative and Declarative programming, I just didn't understand how:
For example the following pseudo-code in Imperative Programming:
Declare a new List called ExpensiveItemsNames;
Foreach Item in Items:
If ItemPrice > 100 then
Add ItemName to ExpensiveItemsNames
vs the Declarative version: (using SQL)
SELECT ItemName FROM Items
WHERE ItemPrice > 100
What would be the CML version of it?
And how does CML help the programmer? I didn't understand that too.
As well, in the further detailed (as mentioned before - meaning the second link I put) website they also talk about pruning to reduce the space complexity.
While I know what is pruning, I couldn't understand it's relation to CML. They look like two unrelated things to me at the moment.

Related

Is there a software-engineering methodology for functional programming? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Software Engineering as it is taught today is entirely focused on object-oriented programming and the 'natural' object-oriented view of the world. There is a detailed methodology that describes how to transform a domain model into a class model with several steps and a lot of (UML) artifacts like use-case-diagrams or class-diagrams. Many programmers have internalized this approach and have a good idea about how to design an object-oriented application from scratch.
The new hype is functional programming, which is taught in many books and tutorials. But what about functional software engineering?
While reading about Lisp and Clojure, I came about two interesting statements:
Functional programs are often developed bottom up instead of top down ('On Lisp', Paul Graham)
Functional Programmers use Maps where OO-Programmers use objects/classes ('Clojure for Java Programmers', talk by Rich Hickley).
So what is the methodology for a systematic (model-based ?) design of a functional application, i.e. in Lisp or Clojure? What are the common steps, what artifacts do I use, how do I map them from the problem space to the solution space?
Thank God that the software-engineering people have not yet discovered functional programming. Here are some parallels:
Many OO "design patterns" are captured as higher-order functions. For example, the Visitor pattern is known in the functional world as a "fold" (or if you are a pointy-headed theorist, a "catamorphism"). In functional languages, data types are mostly trees or tuples, and every tree type has a natural catamorphism associated with it.
These higher-order functions often come with certain laws of programming, aka "free theorems".
Functional programmers use diagrams much less heavily than OO programmers. Much of what is expressed in OO diagrams is instead expressed in types, or in "signatures", which you should think of as "module types". Haskell also has "type classes", which is a bit like an interface type.
Those functional programmers who use types generally think that "once you get the types right; the code practically writes itself."
Not all functional languages use explicit types, but the How To Design Programs book, an excellent book for learning Scheme/Lisp/Clojure, relies heavily on "data descriptions", which are closely related to types.
So what is the methodology for a systematic (model-based ?) design of a functional application, i.e. in Lisp or Clojure?
Any design method based on data abstraction works well. I happen to think that this is easier when the language has explicit types, but it works even without. A good book about design methods for abstract data types, which is easily adapted to functional programming, is Abstraction and Specification in Program Development by Barbara Liskov and John Guttag, the first edition. Liskov won the Turing award in part for that work.
Another design methodology that is unique to Lisp is to decide what language extensions would be useful in the problem domain in which you are working, and then use hygienic macros to add these constructs to your language. A good place to read about this kind of design is Matthew Flatt's article Creating Languages in Racket. The article may be behind a paywall. You can also find more general material on this kind of design by searching for the term "domain-specific embedded language"; for particular advice and examples beyond what Matthew Flatt covers, I would probably start with Graham's On Lisp or perhaps ANSI Common Lisp.
What are the common steps, what artifacts do I use?
Common steps:
Identify the data in your program and the operations on it, and define an abstract data type representing this data.
Identify common actions or patterns of computation, and express them as higher-order functions or macros. Expect to take this step as part of refactoring.
If you're using a typed functional language, use the type checker early and often. If you're using Lisp or Clojure, the best practice is to write function contracts first including unit tests—it's test-driven development to the max. And you will want to use whatever version of QuickCheck has been ported to your platform, which in your case looks like it's called ClojureCheck. It's an extremely powerful library for constructing random tests of code that uses higher-order functions.
For Clojure, I recommend going back to good old relational modeling. Out of the Tarpit is an inspirational read.
Personally I find that all the usual good practices from OO development apply in functional programming as well - just with a few minor tweaks to take account of the functional worldview. From a methodology perspective, you don't really need to do anything fundamentally different.
My experience comes from having moved from Java to Clojure in recent years.
Some examples:
Understand your business domain / data model - equally important whether you are going to design an object model or create a functional data structure with nested maps. In some ways, FP can be easier because it encourages you to think about data model separately from functions / processes but you still have to do both.
Service orientation in design - actually works very well from a FP perspective, since a typical service is really just a function with some side effects. I think that the "bottom up" view of software development sometimes espoused in the Lisp world is actually just good service-oriented API design principles in another guise.
Test Driven Development - works well in FP languages, in fact sometimes even better because pure functions lend themselves extremely well to writing clear, repeatable tests without any need for setting up a stateful environment. You might also want to build separate tests to check data integrity (e.g. does this map have all the keys in it that I expect, to balance the fact that in an OO language the class definition would enforce this for you at compile time).
Prototying / iteration - works just as well with FP. You might even be able to prototype live with users if you get very extremely good at building tools / DSL and using them at the REPL.
OO programming tightly couples data with behavior. Functional programming separates the two. So you don't have class diagrams, but you do have data structures, and you particularly have algebraic data types. Those types can be written to very tightly match your domain, including eliminating impossible values by construction.
So there aren't books and books on it, but there is a well established approach to, as the saying goes, make impossible values unrepresentable.
In so doing, you can make a range of choices about representing certain types of data as functions instead, and conversely, representing certain functions as a union of data types instead so that you can get, e.g., serialization, tighter specification, optimization, etc.
Then, given that, you write functions over your adts such that you establish some sort of algebra -- i.e. there are fixed laws which hold for these functions. Some are maybe idempotent -- the same after multiple applications. Some are associative. Some are transitive, etc.
Now you have a domain over which you have functions which compose according to well behaved laws. A simple embedded DSL!
Oh, and given properties, you can of course write automated randomized tests of them (ala QuickCheck).. and that's just the beginning.
Object Oriented design isn't the same thing as software engineering. Software engineering has to do with the entire process of how we go from requirements to a working system, on time and with a low defect rate. Functional programming may be different from OO, but it does not do away with requirements, high level and detailed designs, verification and testing, software metrics, estimation, and all that other "software engineering stuff".
Furthermore, functional programs do exhibit modularity and other structure. Your detailed designs have to be expressed in terms of the concepts in that structure.
One approach is to create an internal DSL within the functional programming language of choice. The "model" then is a set of business rules expressed in the DSL.
See my answer to another post:
How does Clojure aproach Separation of Concerns?
I agree more needs to be written on the subject on how to structure large applications that use an FP approach (Plus more needs to be done to document FP-driven UIs)
While this might be considered naive and simplistic, I think "design recipes" (a systematic approach to problem solving applied to programming as advocated by Felleisen et al. in their book HtDP) would be close to what you seem to be looking for.
Here, a few links:
http://www.northeastern.edu/magazine/0301/programming.html
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371
I've recently found this book:
Functional and Reactive Domain Modeling
I think is perfectly in line with your question.
From the book description:
Functional and Reactive Domain Modeling teaches you how to think of the domain model in terms of pure functions and how to compose them to build larger abstractions. You will start with the basics of functional programming and gradually progress to the advanced concepts and patterns that you need to know to implement complex domain models. The book demonstrates how advanced FP patterns like algebraic data types, typeclass based design, and isolation of side-effects can make your model compose for readability and verifiability.
There is the "program calculation" / "design by calculation" style associated with Prof. Richard Bird and the Algebra of Programming group at Oxford University (UK), I don't think its too far-fetched to consider this a methodology.
Personally while I like the work produced by the AoP group, I don't have the discipline to practice design in this way myself. However that's my shortcoming, and not one of program calculation.
I've found Behavior Driven Development to be a natural fit for rapidly developing code in both Clojure and SBCL. The real upside of leveraging BDD with a functional language is that I tend to write much finer grain unit tests than I usually do when using procedural languages because I do a much better job of decomposing the problem into smaller chunks of functionality.
Honestly if you want design recipes for functional programs, take a look at the standard function libraries such as Haskell's Prelude. In FP, patterns are usually captured by higher order procedures (functions that operate on functions) themselves. So if a pattern is seen, often a higher order function is simply created to capture that pattern.
A good example is fmap. This function takes a function as an argument and applies it to all the "elements" of the second argument. Since it is part of the Functor type class, any instance of a Functor (such as a list, graph, etc...) may be passed as a second argument to this function. It captures the general behavior of applying a function to every element of its second argument.
Well,
Generally many Functional Programming Languages are used at universities for a long time for "small toy problems".
They are getting more popular now since OOP has difficulties with "paralel programming" because of "state".And sometime functional style is better for problem at hand like Google MapReduce.
I am sure that, when functioanl guys hit the wall [ try to implement systems bigger than 1.000.000 lines of code], some of them will come with new software-engineering methodologies with buzz words :-). They should answer the old question: How to divide system into pieces so that we can "bite" each pieces one at a time? [ work iterative, inceremental en evolutionary way] using Functional Style.
It is sure that Functional Style will effect our Object Oriented
Style.We "still" many concepts from Functional Systems and adapted to
our OOP languages.
But will functional programs will be used for such a big systems?Will they become main stream? That is the question.
And Nobody can come with realistic methodology without implementing such a big systems, making his-her hands dirty.
First you should make your hands dirty then suggest solution. Solutions-Suggestions without "real pains and dirt" will be "fantasy".

How does functional programming apply to simulations?

Besides the general question in the title,
How do functional programmers and functional languages approach the domain of simulations, which seem to be most naturally handled by object-oriented languages?
Are there open-source examples of complex simulations written in a (mostly) functional style?
What changes of perspective would an OO-programmer need, in order to approach simulations from a functional paradigm?
I'm asking this while learning how Clojure's creator Rich Hickey specifically sought to tame the "incidental complexity" of OO-programming and mutable state, e.g. Clojure's separation of identity and state makes a lot of sense (Hickey's ants.clj is on the study list). Another related area is using functional programming for games, which are often simulations with lots of stateful "things" all over the place; there are some articles/papers written about FP and games, more would be welcome.
Perhaps experienced functional programmers can provide additional background and advice on how to re-orient one's thinking to functional style, specifically for simulations. Thanks in advance!
Michal's answer is excellent, but I thought I'd add a couple other neat examples I've personally found helpful/interesting.
The first is a post (and code) about functional fluid dynamics by Lau Jenson. Though he definitely goes the mutable route for speed here, the style is rather functional. I'd bet by Clojure 1.3 this could be done (mostly!) immutably with reasonable performance.
The next is a simple Snake game implemented in Clojure. Easy enough to read though in an hour or so, and the style is really pleasant and cohesive.
Also, some neat code to look at (I think!) is code modeling neural networks. Jeff Foster has some single layer perceptron code, and some more idiomatic revisions of the code. Worth looking at, even if you're not acquainted with NNs. He also has some more recent posts regarding fluid dynamics, though this time in Haskell. (Part I and Part II) Also fun, I think, is his implementation of the Game of Life (& Part II).
Finally, as Michal mentioned before me, Brian Carper is working on a RPG in Clojure. he recently posted some artwork for the game, so I'm betting it's still being worked on ;)
I love using the sequence libraries for working with tons of data; it feels more natural using abstractions like map and reduce, and fun, handy tools like juxt rather than simple imperative iterations. You do pay a tax, I've found, by using Clojure/functional langs in reimplementing well-known and well-implemented imperative algorithms.
Have fun!
I'm not sure that I'm up to the challenge of writing up a comprehensive analysis of the problem posed in the question, but I can at least post some links interesting on the FP vs. games front:
Jörg W. Mittag provides a number of interesting examples in this answer to a question on "real world" Haskell programming (with links to some interesting write-ups -- the Purely Functional Retrogames series is really worth a read).
In Clojure land, Phil Hagelberg has implemented a text-based adventure game for his PeepCode screencast on Clojure programming; the code is available on GitHub. Then there's Brian Carper's RPG project; no code publicly released yet and just that one post from a while ago (it looked very cool, though, so let's all come together to pressure Brian to continue ;-)). Finally, here's an example of a simple game using Penumbra (for some reason -- possibly unrelated to Clojure -- I couldn't get it to work, but may you will, plus there's a write-up attached).
As for simulations, studying ants.clj is a great idea. Also, I remember seeing a series of SICP-based lectures from an introductory programming course at UC Berkeley (I think...?) available somewhere (90% it was on their YouTube channel); they've got three lectures on OOP in Scheme and I think they mention simulation as a domain providing good use cases for the approach. Note that I have a pretty vague memory of this one, so it's hard for me to say how useful it might be to you.
I'm writing a game in Clojure, using a mostly functional style. For example, the entire game state is modelled as an immutable data structure.
This has required some slightly convoluted coding. For example, you frequently end up creating functions with a lot of parameters to pass around various elements of game state and ensure that application of game state updates happens to the very latest game version.
But it has also thrown up some really nice advantages, for example concurrency has proved pretty trivial and you can do fun things like cloning the entire game state to run different simulations in the AI.
Overall, I'm delighted with Clojure as a language for simulations / games.
Based on this experience, the things I think would improve Clojure for games / simulations would be:
Better support for primitives, especially as function parameters and return values
Implementation of core language functions that are less hard on memory allocations (GC pressure is an issue for interactive games!)
You can see an early version of the game here: Ironclad - Generals of Steam. It's basically a steampunk themed strategy game.
Uncle Bob has been playing with Clojure lately and in particular writing an orbital simulator as his most public example.
Some links:
Blog post
Code on github
To complement the other answers: There is a discipline called Functional Reactive Programming that addresses the issue of functional representation of systems that change in time and react to outer events. See
What is (functional) reactive programming?
Is functional GUI programming possible?
Functional Reactive Programming at The Haskell Wiki.
Simulations are a form of interpreter -- which are easy to write in a functional style. They can be also designed as self-optimizing simulators, based on treating them as a compiler.

Why would one use a functional language in an otherwise Imperative project? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Why would one use a functional language in an otherwise Imperative project?
Many tasks are inherently addressed by functional concepts, such as composable calculations. It is feasible that you will encounter these kinds of problems in projects which have otherwise been developed in an object-oriented fashion.
The best tool for a job is independent of that tool's dominant paradigm.
If your project is truly imperative, you probably don't want a purely functional language. But you probably still want a language with functional features; functional style addresses low-level code structure in the same way that object-oriented style addresses high-level structure. Both allow you to package certain common patterns in a language-supported way.
In a primarily imperative project, functional style is useful at the expression and statement level, allowing you to abstract common loops and sequences:
For example, take this common pattern:
newlist = []
for x in oldlist:
y = dosomething(x)
newlist.append(y)
That's map:
newlist = map(dosomething, oldlist)
Or this:
total = 1
for n in numbers:
total = total * n
Becomes fold (also known as reduce):
total = fold(*, 1, numbers)
Imperative style does not address this low-level duplication all that well--hence the "I wish I had a nickel for every time I typed for(int i = 0; ...)". Even in OO languages without functional features, code inside methods doesn't differ much from similar non-OO languages.
Some IDEs for address this by providing code snippets. This addresses the lack of abstraction power in the wrong way. The way to handle a repeated pattern is not to encourage cut-and-paste with little holes for variable names, but to abstract the pattern into a reusable unit.
Note: I addressed embedding functional code in an imperative project. A top-to-bottom project in functional style will look different. Here are some links taken from similar Stack Overflow questions:
http://www.25hoursaday.com/weblog/2008/06/16/FunctionalProgrammingInC30HowMapReduceFilterCanRockYourWorld.aspx
http://www.joelonsoftware.com/items/2006/08/01.html
Many methods in languages like Java and C++ could be written in a more readable and dense form using FP concepts such as higher-order functions, currying, closures, etc.
See Scala for many interesting examples.
Probably, the most common reason - is to localise and restrict the imperative part (i.e., potentially dangerous and harder to debug, analyse and maintain).
A common problem faced by application developers is building queries: "I want all customers over the age of 18 who have spent $10K in the past year." The process of defining a query that returns all customers, then filtering it by one criterion, then another, and finally a third, is called composing the query. Composition is a strong suit of a functional approach, meaning the query definition problem and a functional solution are well-matched. To see this in action in a popular object-oriented language, see C# and LINQ
Why would one use a functional language in an otherwise Imperative project?
Some examples:
The Mathematica kernel is written in a proprietary dialect of C. Mathematica is a functional language so it makes it much easier to implement many mathematical algorithms and, consequently, it became the language of choice for implementing large parts of Mathematica itself. Retrospectively, this was a huge success as it made it possible to implement many more algorithms quickly and cheaply using a more expressive language.
Joule is a front-end GUI app for traders from the market leader of European energy trading. Although most of the lines of code in Joule are C# they chose to implement some parts of it in F#. In particular, the implied prices engine that combines real bids and asks across related contracts to create implicit bids and asks was written entirely in F#. Retrospectively, this was a huge success as it replaced a substantial (~50kLOC) C++ code base with a comparatively tiny (~1kLOC) F# code base that does the same thing more quickly. The author of Joule's implied prices engine wrote a related article here.
Microsoft's Halo 3 computer game and Bing search engine both have some of their dense mathematical code written in Microsoft's own functional language F#.

Patterns for functional, dynamic and aspect-oriented programming

We have a very nice GoF book (Design Patterns: Elements of Reusable Object-Oriented Software) about patterns in Object Oriented Programming, and plenty of articles and resources in the web on this subject.
Are there any books (articles, resources) on patterns(best practices) for functional programming?
For dynamic programming in languages like Python and Ruby?
For AOP?
A related question was asked before: "Does functional programming replace GoF design patterns", with great responses.
The equivalent of "design patterns" is very vague in FP. In general, every time you see a "pattern" in your code you should create something to cover all of its uses in a uniform way. Often it will be a higher-order function.
For example, the following C code
for (int i = 0; i < n; i++)
if (a[i] == 42)
return true;
return false;
can be thought of some basic "design pattern" - checking if there's some special element on the list. This snippet could appear many times in code with different conditions. In FP, you simply use a higher order function several times. It's not a "pattern" anymore.
Functional programming has its own practices, but they are much different from "design patterns" in OOP. They include use of polymorphism, lists, higher-order functions, immutability/purity, laziness [not all are essential or specific to FP]... See also "what are core concepts of FP". Also, type classes (Haskell), modules and functors (OCaml), continuations, monads, zippers, finger trees, monoids, arrows, applicative functors, monad transformers, many purely functional data structures (book) etc. Functional pearls, already mentioned by Randall Schulz, form a very rich resource of FP at its best.
To learn how to write idiomatic code, any book/resource on a functional programming language will suffice IMHO (for example, RWH and LYAH); differences between thinking imperatively and functionally are always explained there.
In dynamic languages, Jeff Foster's link is a good collection; here is a very clever use of memoization in JavaScript that could be considered a "design pattern".
The list of design patterns described in GoF is written for languages like C++ and Java. It is sometimes considered a list of workarounds to make inflexible languages more dynamic. For example the Visitor pattern is not really needed in Ruby because you can simply change add member functions to your class at runtime. The Decorator pattern is obsolete if you can use mixins.
It's my experience that when I'm implementing a solution in C++ I tend to spend most of my time writing scaffolding code. I begin with creating a platform that allows me to think in my application's program domain. Design patterns probably were developed as a way to categorize different kinds of scaffolding.
I should mention that when I am programming in Ruby I don't have much supporting code. There just doesn't seem to be a need for it.
My theory is that other languages don't emphasize the concept of design patterns simply because their basic language constructs are sufficient. In defense of Java and C++: maybe this is because functional and AOP languages are often used in more specific problem domains or niches, while Java and C++ are used for everything.
And now for something different. If you are getting a bit bored with OO design and you want to learn something new then you might be interested in the the book Elements of Programming written by Stepanov. In this book he explains how programming can be approached from a mathematical point of view. For a preview, check out his Class notes for Adobe (found among others on this page). You may also be interested in Adobe's Collected Papers.
Here's a link to Design Patterns in Dynamic Programming
Aren't the Functional Pearls (one of) the canonical set(s) of design patterns for functional programming?
There is a Design patten in Ruby.
Beside the design patterns mentioned in GOF, it also list some others pattern like Convention over Configuration .
Personally my most important pattern for dynamic languages - write tests. It's even more important than in statically-typed languages.

Concepts that surprised you when you read SICP?

SICP - "Structure and Interpretation of Computer Programs"
Explanation for the same would be nice
Can some one explain about Metalinguistic Abstraction
SICP really drove home the point that it is possible to look at code and data as the same thing.
I understood this before when thinking about universal Turing machines (the input to a UTM is just a representation of a program) or the von Neumann architecture (where a single storage structure holds both code and data), but SICP made the idea much more clear. Scheme (Lisp) helped here, as the syntax for a program is exactly the same as the syntax for lists in general, namely S-expressions.
Once you have the "equivalence" of code and data, suddenly a lot of things become easy. For example, you can write programs that have different evaluation methods (lazy, nondeterministic, etc). Previously, I might have thought that this would require an extension to the programming language; in reality, I can just add it on to the language myself, thus allowing the core language to be minimal. As another example, you can similarly implement an object-oriented framework; again, this is something I might have naively thought would require modifying the language.
Incidentally, one thing I wish SICP had mentioned more: types. Type checking at compilation time is an amazing thing. The SICP implementation of object-oriented programming did not have this benefit.
I didn't read that book yet, I have only looked at the video courses, but it taught me a lot. Functions as first class citizens was mind blowing for me. Executing a "variable" was something very new to me. After watching those videos the way I now see JavaScript and programming in general has greatly changed.
Oh, I think I've lied, the thing that really struck me was that + was a function.
I think the most surprising thing about SICP is to see how few primitives are actually required to make a Turing complete language--almost anything can be built from almost nothing.
Since we are discussing SICP, I'll put in my standard plug for the video lectures at http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/, which are the best Introduction to Computer Science you could hope to get in 20 hours.
The one that I thought was really cool was streams with delayed evaluation. The one about generating primes was something I thought was really neat. Like a "PEZ" dispenser that magically dispenses the next prime in the sequence.
One example of "the data and the code are the same thing" from A. Rex's answer got me in a very deep way.
When I was taught Lisp back in Russia, our teachers told us that the language was about lists: car, cdr, cons. What really amazed me was the fact that you don't need those functions at all - you can write your own, given closures. So, Lisp is not about lists after all! That was a big surprise.
A concept I was completely unfamiliar with was the idea of coroutines, i.e. having two functions doing complementary work and having the program flow control alternate between them.
I was still in high school when I read SICP, and I had focused on the first and second chapters. For me at the time, I liked that you could express all those mathematical ideas in code, and have the computer do most of the dirty work.
When I was tutoring SICP, I got impressed by different aspects. For one, the conundrum that data and code are really the same thing, because code is executable data. The chapter on metalinguistic abstractions is mind-boggling to many and has many take-home messages. The first is that all the rules are arbitrary. This bothers some students, specially those who are physicists at heart. I think the beauty is not in the rules themselves, but in studying the consequence of the rules. A one-line change in code can mean the difference between lexical scoping and dynamic scoping.
Today, though SICP is still fun and insightful to many, I do understand that it's becoming dated. For one, it doesn't teach debugging skills and tools (I include type systems in there), which is essential for working in today's gigantic systems.
I was most surprised of how easy it is to implement languages. That one could write interpreter for Scheme onto a blackboard.
I felt Recursion in different sense after reading some of the chapters of SICP
I am right now on Section "Sequences as Conventional Interfaces" and have found the concept of procedures as first class citizens quite fascinating. Also, the application of recursion is something I have never seen in any language.
Closures.
Coming from a primarily imperative background (Java, C#, etc. -- I only read SICP a year or so ago for the first time, and am re-reading it now), thinking in functional terms was a big revelation for me; it totally changed the way I think about my work today.
I read most part of the book (without exercise). What I have learned is how to abstract the real world at a specific level, and how to implement a language.
Each chapter has ideas surprise me:
The first two chapters show me two ways of abstracting the real world: abstraction with the procedure, and abstraction with data.
Chapter 3 introduces time in the real world. That results in states. We try assignment, which raises problems. Then we try streams.
Chapter 4 is about metalinguistic abstraction, in other words, we implement a new language by constructing an evaluator, which determines the meaning of expressions.
Since the evaluator in Chapter 4 is itself a Lisp program, it inherits the control structure of the underlying Lisp system. So in Chapter 5, we dive into the step-by-step operation of a real computer with the help of an abstract model, register machine.
Thanks.

Resources