Programming Language Concepts

PLC > Objectives

Understand syntax vs semantics

Why is it more important to understand semantics than syntax?

Show that programming languages share many features

Most languages will have a subset of "all" programming features

PLC > Topics

Values and types

Memory

Environments

Variable bindings

Operators

Functions

Closures

Homogeneous types (lists)

Heterogenous types (tuples)

Enums and ADTs

Pattern matching

Destructuring

Currying

Generics

PLC > Values and types

A Value is a piece of data that can belong to one or more types and have zero or more operations performed on it

 

A Type is a set of possible values and a set of operations on those values

 

    Type: (values: Set, operations: Set)

 

Examples:

    String: ({ "a", "b", ... }, { concatenate, substring, ... })

    Integer: ({ ..., -1, 0, 1, ... }, { +, -, *, / })

PLC > Memory

In computing, Memory is the space used to store values

0x01

0x00

0x02

0x03

0x04

0x05

0x06

0x07

char

short

int

long

Visualization of values of some C types

PLC > Memory > Addresses

An Address refers to the location of a value in memory

0x01

0x00

0x02

0x03

0x04

0x05

0x06

0x07

char

short

int

long

Address

In low-level programming languages, raw addresses are referred to as Pointers

'h'

'e'

'l'

'l'

'o'

'\0'

Address of 'e'

PLC > Memory > Structs

There are "compound" data types which combine values of several other types

char

int

In C, a struct defines a block of memory with named regions.

The size of the struct is the sum of the sizes of the regions*.

struct person {
    int age;
    char name[4];
}

struct person

age

name

PLC > Environments and Variable Bindings

An Environment is a mechanism used for mapping variable names to the value they represent.

// environment_example.c
int main(void) {

    int foo = 42;
    printf("foo: %d\n", foo);

    return 0;
}
foo 42

Environment

Bindings

This environment's bindings

The extended environment

foo 42
... ...

A variable binding is a mapping from a name to a value

The empty environment

PLC > Environments and Variable Bindings

A Scope introduces a new environment that extends the previous environment

// environment_example.c
int main(void) {

    int foo = 42;
    printf("foo: %d\n", foo);

    {
        char *foo = "Hello";
        printf("foo: %s\n", foo);
    }

    return 0;
}
foo 42
foo "Hello"

Creating a new variable binding with the same name as an existing variable is called variable shadowing

PLC > Functions

A Function is a mapping of input values to an output value

A function's type is denoted using the types of its inputs and output

public String foo(char little, long big) { ... }

Formally, foo: char x long -> String

In programming, a function is executed by passing arguments to it as input

In math, a function is said to be applied to its input

PLC > Functions

In many programming languages, a function is itself a value

A Value is a piece of data that 1) can belong to one or more types and 2) have zero or more operations performed on it

Remember:

Let foo: int -> String

1) The value "foo" belongs to type int -> String

2) We can think of the type int -> String as having an operation called apply defined on it.

The "apply" operator can be seen as () in many languages

foo(42) can be read as "the function foo applied to the input 42"

PLC > Functions

Given foo: int -> String

Expression Type
foo int -> String
42 int
foo(42) String

The type of a function applied to its input (e.g. foo(42)) is the type of the function's output

i.e. (int -> String) applied to int gives String

PLC > Functions

Function overloading means defining a new function type for an existing function name

Function applied to yields a
toString: int -> String int String
toString: Person -> String Person String
toString: String -> String String String

Not that it would be good practice or intuitive, but you could also define toString: String -> int

Functions definitions must be unique. They can have the same name or the same type, but not both.

e.g. given the functions above, we could not define another toString: int -> String

PLC > Operators

An operator can be thought of as a function

e.g. + : int x int -> int

10 + 12 can be thought of as +(10, 12)

Operator overloading is the same thing as function overloading:

+ : int x int -> int,

+ : String x String -> String

PLC > Operators

Let's denote the type of an array as numbers: int[]

[ ] can be thought of as the "index" operator.

'numbers[5]' means "go the the address of the pointer called numbers, then increment the address by 5 times the size of int and get the value stored at that address"

An attempt at defining the index operator (pseudocode)

fn []<T>(name: *T [ index: usize ] ) -> T {
    let offset = index * sizeof(T);
    return *(name + offset);
}

PLC > Closures

A Closure is a function and the environment that its body is evaluated in

foo:

bar console.log(`${bar}, ${baz}`);

const foo = (bar) => console.log(`${bar}, ${baz}`);

formal arguments

body

environment

PLC > Closures

const key = "AAAAAAAAAAAAAHHHHHRHRGRGRGRRR...";


const pollFunction = () => {


  return fetch("https://some.api.com/data", {
    Authorization: `Basic ${key}`,
  })
  .then(result => result.json())
  .then(result["answer"]);
};

setInterval(pollFunction, 5000);
key "..."
pollFunction

()

...

PLC > Homogeneous types (lists)

A List is a variable-size set of values where each value in it is of the same type

foos: [String] = [ "foo", "fooo", "foooo" ]

PLC > Heterogeneous types (tuples)

A Tuple is an ordered, fixed-size set of values

// Define a tuple called 'point'
let point:  (u32, u32)                 = (2, 4);
let person: (u32, String)              = (20, "Bob");
let nested: (u32, (u16, (u8, String))) = (0, (1, (2, "Hi")));

The type of a tuple is given by the types of its values

point: (int x int)

The empty tuple is a tuple of length 0. The type of the empty tuple '()' is called Unit and has exactly one value, also called unit.

PLC > Heterogeneous types (tuples)

Mathematically, a tuple models a Cartesian product.

Let point: (int x int)

int

int

(1,1)

(2,1)

(3,1)

(4,1)

(1,2)

(2,2)

(3,2)

(4,2)

(-4,2)

(-3,2)

(-2,2)

(-1,2)

(-4,1)

(-3,1)

(-2,1)

(-1,1)

(1,-1)

(2,-1)

(3,-1)

(4,-1)

(-4,-1)

(1,-2)

(2,-2)

(3,-2)

(4,-2)

(-3,-1)

(-2,-1)

(-1,-1)

(-4,-2)

(-3,-2)

(-2,-2)

(-1,-2)

PLC > Heterogeneous types (tuples)

Mathematically, a tuple models a Cartesian product.

Let point: (int x String)

String

int

(1,"a")

(2,"a")

(3,"a")

(4,"a")

(1,"b")

(2,"b")

(3,"b")

(4,"b")

(1,"c")

(2,"c")

(3,"c")

(4,"c")

(-4,"a")

(-3,"a")

(-2,"a")

(-1,"a")

(-4,"b")

(-3,"b")

(-2,"b")

(-1,"b")

(-4,"c")

(-3,"c")

(-2,"c")

(-1,"c")

(1,"d")

(2,"d")

(3,"d")

(4,"d")

(-4,"d")

(-3,"d")

(-2,"d")

(-1,"d")

PLC > Enums and Algebraic Data Types (ADTs)

An Enum is an enumeration of all possible choices of a value

enum Color {
    Red,
    Green,
    Blue,
}

In this example, the newly defined type is Color, and the newly defined values are Color::Red, Color::Green, and Color::Blue.

PLC > Enums and Algebraic Data Types (ADTs)

Enums in some languages can carry data. These are sometimes called sum types or tagged unions.

enum Option<T> {
    Some(T),
    None,
}

In the Option type, a value may either carry no data (None), or carry Some data of type T.

In order to distinguish between which variant a value of an enum type is, we need to use pattern matching.

PLC > Pattern Matching

Pattern Matching is a means of deconstructing a compound type into its component types.

enum Color {
    Red,
    Green,
    Blue,
}

let mystery_color: Color = Color::Blue;

match mystery_color {
    Color::Red => println!("Got the color red"),
    Color::Green => println!("Got the color green"),
    Color::Blue => println!("Got the color blue"),
}

When matching against enums with no data, the match statement works similarly to a switch statement.

PLC > Pattern Matching

Pattern matching generally requires a match statement to be exhaustive, or account for every variant case.

enum Color {
    Red,
    Green,
    Blue,
}

let mystery_color: Color = Color::Blue;

match mystery_color {
    Color::Red => println!("Got the color red"),
    Color::Green => println!("Got the color green"),
    // Color::Blue => println!("Got the color blue"),
}

The above example fails to compile because case Color::Blue is not handled.

PLC > Pattern Matching

When pattern matching enums with data, the pattern can create variable bindings that map to the contained values.

enum Option<T> {
    Some(T),
    None,
}

fn square_root(number: f32) -> Option<f32> { ... }

let number = -4.0;
let maybe_root = square_root(number);

match maybe_root {
    None => println!("Cannot take the square root of a negative"),
    Some(root) => println!("Got a square root of {}", root),
}

If the value of maybe_root is Some, the value inside it is bound to the name root for the duration of the match arm.

PLC > Algebraic Data Types

Proper enums (aka Tagged Unions or Sum Types) are known as Algebraic Data Types because the set of possible values of a given enum type is fixed.

When we know all of the possible values of a type, we can reason more strongly about the possible behavior of programs.

PLC > Algebraic Data Types

Consider the following ADT:

enum Color { Red, Green, Blue }

enum Row { A, B, C }

enum Column { 1, 2, 3 }

ColorPoint: (Row, Column, Color)

We can visualize the entire domain of values of the ColorPoint type.

PLC > Algebraic Data Types

enum Color { Red, Green, Blue }

enum Row { A, B, C }

enum Column { 1, 2, 3 }

ColorPoint: (Row, Column, Color)

1

2

3

A

B

C

Programming Language Concepts

By Nick Mosher

Programming Language Concepts

  • 297