Skip to content

Instantly share code, notes, and snippets.

@celadevra
Last active December 14, 2015 23:48
Show Gist options
  • Save celadevra/5167911 to your computer and use it in GitHub Desktop.
Save celadevra/5167911 to your computer and use it in GitHub Desktop.
Course Notes for the Coursera Programming Languages Course

Concepts

Values

Values are expressions that cannot be further simplified. Thus all values are expressions, but expressions are not always values.

Static vs. Dynamic

Functional vs. Object Oriented

One very powerful feature of functional language is that the contents of a binding cannot be changed. You may shadow a variable but the original variable name in the original environment will always point to the original expression. This is called immutability.

Type inference

As SML uses pattern-matching to process arguments passed to the function in the form of tuples, it is not necessary to write types for each member of the tuple.

Type-check vs. Ducktyping

Polymorphic

In SML, some functions may accept multiple types of arguments.

Inheritance

First-Class Functions

Lexical Scope

Thunks

Building blocks

Types

There are basic types and compound types. For example, int, bool and unit are basic types. int list or int * bool are compound types. Compound types can be constructed using 3 basic building blocks:

  • Each of (product type, and): quite common, such as types of tuples and lists;
  • One of (sum type, or): contain a type or not. Often implemented with datatype bindings in SML;
  • Self-reference: refer to itself in definition to describe recursive data structures. For example, int list is defined as a type that contains nothing or an int and another int list.

Options

In SML, sometimes you need a value to stand for “there isn’t a value you are looking for” or “there is a value you may be interested”, and they use options.

An option value is either NONE or SOME e where e can be evaluated to v. The type of NONE is \'a option and SOME e t option if e has type t. One can use isSome to see if an option is NONE and valOf to extract value v carried by SOME e.

Datatype Bindings

A way to construct “one-of” types. Syntac-wise, a datatype binding binds the name of the type to a series of constructors.

A constructor is either: a function to create values of the new type or a value of the new type. For example, in

datatype mytype = TwoInts of int * int
                | Str of string 
                | Pizza         

TwoInts is a function of type int * int -> mytype, Str is a function of type string -> mytype, and Pizza is a value of type mytype.

The datatype expression is essentially a case expression, and the retrieval of the binding is usually done with case expressions.

Lists and options are datatype bindings too, and this is why you can use pattern matching to access list and option values.

Type synonyms

One can use the type keyword to give a type a name that is easier to refer to.

Data Structure

Pairs and Tuples

In ML, the syntax to construct a pair is (e1, e2). When evaluated it yields (v1, v2). Both e1 and e2 can themselves be pairs, and pairs can be used to construct many other useful data structures such as lists.

By generalizing the concept of pairs we get tuples. But tuples are in fact records with syntactic sugar that saves the trouble of writing field names.

{1=e1, ... n=en} => (e1, ..., en)

Lists

In ML lists do not have to declare their length when type-checking. Functions dealing with lists are almost always recursive.

Structs

Patterns

Functions

In ML, a program is a series of bindings. A function binding is how we define and use functions.

A function binding in ML often look like this:

fun x0 (x1 : t1, ..., xn : tn) = e

This is binding for a function named x0. During type-checking, type of x1 is mapped to t1, …, xn to tn, and x0 to t1 * ... * tn -> t. Because x0 is in the environment, it is possible to recursively call the function. For the whole function binding to type-check, e must have type t.

Evaluation of a function binding is trivial. It just adds x0 to the environment. Function calls are more significant. It finds the expressions corresponding to variables x1, ..., xn, evaluate them to get values v1, ..., vn, and use them to evaluate e (bound to function name x0) to obtain the result e. In the process everything must type-check.

Every function in SML has exactly one argument. The argument can be a tuple. When the function is called, the values in the tuple are extracted using pattern matching.

Let

Let-expressions allows local expressions. Or one can say it defines a special area in the environment where only the e in the let-expression can use. This area can have variables with the same name as those outside the let-expression, and when evaluating the let-expression the outside variables with the same name will be shadowed.

Better still, one can define a local function with let-expression in functional programming, which is a powerful feature.

Closure

Macro

Class

Method

Block

Proc

Idioms

Closure: callbacks

Closure: currying

Lambda

Techniques

Exceptions and Handling

Double Dispatch and Dynamic Dispatch

Mutual Recursion

Tail Recursion

Difference from the “usual” recursion: tail recursion uses a local helper function. The base case in the non-accumulator version becomes the initial accumulator and the base case is the accumulator version return the accumulator.

Delay and Force

Streams

Memoization

Subtyping

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment