Seven Languages in Seven Weeks provides an overview of the following programming languages:
Each programming language is presented on about 40 pages, starting with examples of the basic syntax and then moving quickly to more advanced examples. This is completed by an overview of the strengths and weaknesses of the respective language and an interview with either the language's creator or a user of the language.
I found Seven Languages in Seven Weeks an interesting read, especially because I only knew one from the seven languages – Ruby. All other languages were new to me. I won't be able to use any of those new languages in practice after reading this book, but at least I know what they are about. And it helps with the decision, which language(s) you want to explore more in-depth.
What I didn't like about the book were the references to films – each language was associated with a film – as I haven't seen most of them. Also the interviews could have been longer.
Matz, Ruby's creator, doesn't worry about the efficiency of the language. He optimizes the efficiency if the programmers.
Ruby is an interpreted, object-oriented, dynamically typed language from a family of so-called scripting languages. Interpreted means that Ruby code is executed by an interpreter rather than a compiler. Dynamically typed means that types are bound at execution time rather than compile time. In general, the trade-off for such a strategy is flexibility versus execution safety.
When you first learn a language, your job is to learn how to use it to do the jobs you already know how to do.
Duck typing is extremely important when it comes to clean object-oriented design. An important tenet of design philosophy is to code to interfaces rather than implementations. If you're using duck typing, this philosophy is easy to support with very little added ceremony. If an object has
pop methods, you can treat it like a stack. If it doesn't, you can't.
Application performance is secondary. Ruby is about the performance of the programmer.
Ruby's core strengths are its syntax and flexibility. The core weaknesses are around performance, though the performance is reasonable for many purposes.
Most of Io's community is focused on Io as an embeddable language with a tiny virtual machine and rich concurrency.
Io syntax simply chains messages together, with each message returning an object and each message taking optional parameters in parentheses. In Io, everything is a message that returns another receiver. There are no keywords and only a handful of characters that behave like keywords.
In a prototype language, every object is a clone of an existing object rather than a class.
The basic rules of the prototype programming paradigm:
- Every thing is an object.
- Every interaction with an object is a message.
- You don't instantiate classes; you clone other objects called prototypes.
- Objects remember their prototypes.
- Objects have slots.
- Slots contain objects, including method objects.
- A message returns the value in a slot or invokes the method in a slot.
- If an object can't respond to a message, it sends that message to its prototype.
The balance of syntactic sugar is a difficult one. Add too much sugar, and it's difficult to learn a language and remember how to use it. Add too little, and you need to spend more time to express code and potentially more energy to debug it.
Prolog is a declarative language. You'll throw some facts and inferences at Prolog and let it do the reasoning for you.
Prolog is a logic programming language.
Prolog has two parts: one to express the data and one to query the data. In Prolog, the data is in the form of logical rules. These are the building blocks:
- Facts. A fact is a basic assertion about some world. (Babe is a pig; pigs like mud)
- Rules. A rule is an inference about the facts in that world. (An animal likes mud if it is a pig)
- Query. A query is a question about that world. (Does Babe like mud?)
Facts and rules will go into a knowledge base. A Prolog compiler compiles the knowledge base into a form that's efficient for queries.
Prolog is not about writing algorithms to solve logical problems. Prolog is about describing your world as it is and presenting logical problems that your computer can try to solve.
You don't have to describe the solution to a problem. You have only to describe the problem. And the language for description of the problem is logic, only pure logic.
Prolog programming has two major steps. Start by building a knowledge base, composed of logical facts and inferences about the problem domain. Next, compile your knowledge base, and ask questions about the domain. Some of the questions can be assertions, and Prolog will respond with
no. Other queries have variables. Prolog fills in these gaps that makes those queries true.
Prolog is not a general-purpose language.
Scala is a hybrid language, meaning that it intentionally tries to bridge the gaps between programming paradigms. In this case, the bridge is between object-oriented languages and functional languages.
Scala offers tight integration into Java, offering a chance for people to protect their investment in many ways:
- Scala runs on the Java virtual machine, so it can run side-by-side with existing deployments.
- Scala can use Java libraries directly, so developers can leverage existing frameworks and legacy code.
- Like Java, Scala is statically typed, so the languages share a philosophical bond.
- Scala's syntax is relatively close to Java's, so developers can learn the basics quickly.
- Scala supports both object-oriented and functional programming paradigms, so programmers can gradually learn to apply functional programming ideas to their code.
Scala infers variable types where possible.
Pure functional languages allow a style of programming that has strong mathematical foundations. A functional language has these characteristics:
- Functional programs are made up of functions.
- A function always returns a value.
- A function, given the same inputs, will return the same values.
- Functional programs avoid changing state or mutating data. Once you've set a value, you have to leave it alone.
Strictly speaking, Scala is not a pure functional programming language. It allows mutable values, which can lead to functions with the same inputs but different outputs. But it offers tools that allow developers to use functional abstractions where they make sense.
Scala is strongly typed. It will use type inference, so most of the time, it will understand the types of variables through syntactical clues.
String typing means the language detects when two types are compatible, throwing an error or coercing the types if they are not.
Statically typed languages enforce polymorphism based on the structure of the types. Is it a duck by the genetic blueprint (static), or is it a duck because it quacks or walks like one (dynamic)?
Scala classes work much like they do in Java, but they don't support class methods. Instead, Scala uses a concept called companion objects to mix class and instance methods on the same class.
The key element that differentiates functional programming from object-oriented programming: mutable state limits concurrency.
A higher-order function is one that takes other functions as input parameters or returns functions as output. Composing functions that use other functions in this way is a critical concept for the functional family of languages.
Functional languages use currying to transform a function with multiple parameters to several functions with their own parameter lists.
Much of functional programming is learning to manipulate collections with higher-level constructs instead of Java-style iteration.
One of the most important aspects of Scala is the way it handles concurrency. The primary constructs are actors and message passing. Actors have pools of threads and queues. When you send a message to an actor, you place an object on its queue. The actor reads the message and takes action.
The best way to establish a new programming community is to fully embrace an existing one. Scala does a good job offering a more concise Java.
A better programming language should allow you to express more complex ideas with fewer lines of code, with minimal overhead.
Erlang is a functional language – one with many reliability features cooked in.
The key Erlang capability is concurrency.
Erlang uses actors for concurrency, so message passing is a critical concept. An actor represents a lightweight process. The actor reads inbound messages from a queue and uses pattern matching to decide how to process it.
The Erlang mantra is "Let it crash". Since Erlang makes it easy to monitor the death of a process, killing related processes and starting new ones are trivial exercises.
With Erlang you can hot-swap code, meaning you can replace pieces of your application without stopping your code.
When you remove mutable state from the equation, concurrency gets dramatically simpler.
Erlang is not a pure functional language; it does allow a few exceptions.
Erlang is dynamically typed. It will bind types at run time, based on syntactic clues such as quotes or decimal points.
Clojure is Lisp on the JVM.
Clojure is dynamically typed.
Clojure is a functional language, with emphasis on functions without side effects. But when you do use mutable state, the language supports a number of concepts to help.
In Clojure, you'll enclose any function call entirely in parentheses. The first element is the function name, and the rest are the arguments.
Unlike other Lisp dialects, Clojure adds a little syntax, preferring braces for maps and brackets for vectors. You can use commas for whitespace and omit parentheses in some places.
Functional languages depend on recursion rather than iteration.
In mathematics, infinite sequences of numbers are often easier to describe. In functional languages, you'd often like to have the same benefits, but you can't actually compute an infinite sequence. The answer is lazy evaluation. Using this strategy, Clojure's sequence library computes values only when they are actually consumed.
From a Clojure perspective, the best parts of OO are types and protocols (such as interfaces), and the worst parts are implementation inheritance. Clojure's
protocol preserve the good parts of OO and leave the rest.
Macro expansion is perhaps the most powerful feature of Lisp.
The Clojure approach to concurrency is to use software transactional memory (STM).
When you want to change the state of a reference in Clojure, you must do so within the scope of a transaction.
A future is a concurrency construct that allows an asynchronous return before the computation is complete. We can use futures to allow several long-running functions to run in parallel.
Haskell is a pure functional language and it will challenge you when it's time to do I/O or accumulate state.
Haskell was built from the ground up to be a pure functional language, combining ideas from the best functional languages, with special emphasis on lazy processing.
Haskell has string, static typing, and the type model is mostly inferred.
As a pure functional language, Haskell does not do side effects. Instead, a function can return a side effect, which is later executed.
In Haskell, indentation is significant.
The centerpiece of the whole Haskell programming paradigm is the function. Since Haskell has strong, static typing, you'll specify each function in two parts: an optional type specification and the implementation.
Haskell's type system is one of its strongest features.
Haskell supports a wide variety of functional capabilities including list comprehension, lazy computing strategies, partially applied functions, and currying. In fact, by default, Haskell functions process one parameter at a time, using currying to support multiple arguments.
Usually, the Haskell programmer is not burdened with type details except in the function declarations, but the type system protects users from all kinds of type errors.
Emerging programming languages are changing the way we think about program organization and construction.
Concurrency and reliability are starting to nudge us in the direction of a higher-level programming language.
The current "king of the hill" is object orientation. This programming paradigm has three major ideas: encapsulation, inheritance, and polymorphism.
Prototype languages are a subset of object-oriented languages. Rather than working through a class, all prototypes are object instances. Some specially designated instances serve as prototypes for other object instances. Prototype languages are typically dynamically typed.
The degree of purity found in functional programming languages differs, but the concepts are consistent throughout. Functional programs are made up of mathematical functions. Calling the same function more than once will give you the same result each time, and side effects are either frowned on or forbidden. You can compose those functions in a variety of ways.
Functional programming languages are usually more expressive than object-oriented languages.
When mutable state goes away, so do many of the traditional concurrency problems.