Welcome to CS251: Principals of Programming Languages!
Agenda for today:
Introductions
High-level course goals.
Course expectations, learning outcomes.
A bit of our first functional programming language!
Name magnets/name tents
If you missed class, I will have more on Monday!
Professor introduction
I'm Alexa VanHattum–-you can call me "Alexa", "Prof. Alexa", or "Prof VanHattum".
This is my second year as an Assistant Professor at Wellesley. My research focus is the intersection of programming languages & computer systems, with a focus on applying lightweight formal methods to compilers for systems languages.
Before Wellesley, I spent time as a software engineer for Apple health, then completed grad school at Cornell.
Student introductions
(Names, pronouns, year, favorite, least favorite, or weirdest programming language).
This is a smaller elective, and we want to all get to know each other!
Go over the syllabus/website
Assignments
Help hours, Calendly
Zulip
Gradescope
Quiz and retake policy
Collaboration policy
Expectations
Goals
In this class, you will:
Gain a more accurate view on what programs actually mean.
Learn powerful programming language features—such as structural recursion, higher-order functions, and pattern matching—that will make you a stronger programmer across languages even beyond the two functional languages (Racket and OCaml) we will use.
Develop skills and strategies that will help you learn programming languages more quickly and effectively in the future.
Gain an appreciation for the expressive power of different programming language constructs.
Approach problem-solving through the lens of language design and program analysis.
Develop and apply skills for independent learning, critical thinking, and problem-solving as a self-reliant computer scientist.
Programming languages as a field of study
NOT just a tour of existing PLS ("PL Zoo")
Focus on the fundamentals:
Properties and paradigms important across different PLs.
Design, implementation, underlying principals.
Paradigms
Paradigms are specific models and shared features across different languages. These are usually not strict boundaries, but rather broad groupings.
The imperative paradigm
Most programming you've done up to this point is imperative. We can think of imperative code as being "step-by-step": you tell the computer what to do via a series of instructions. This comes from the lineage of assembly code, which is organized as a sequence of instructions grouped by jumping between blocks of code.
The primary means of processing data in imperative code is via loops: for, while, do while, etc.
Data in imperative languages is more closely aligned with its representation in memory: memory is a contiguous array of bytes. Types group the bytes into e.g. integers. Types further group basic data types like integers into composite data like arrays.
Examples of primarily imperative languages: C, Java, Python.
The functional paradigm
Functional programming languages are more declarative: they describe what a computation is, rather than how to do it step-by-step. This more closely aligned with pure mathematics.
The primary means of processing data in imperative code is via recursion, instead of loops. Functional languages avoid mutating data, and are thus less close of a representation to low-level program memory.
For example, you'd avoid the following pattern of mutation in most functional languages:
# This code is more imperative than functional
x = 3
x = 5
A primary feature of functional languages is that they treat functions as values. This is related to "first class functions" and "higher order functions", which we'll define more precisely over the coming weeks.
Imperative languages also often support some version of functions as values, even though functions are not necessarily "first class".
For example:
In C, you can pass around function pointers as a specific language feature.
In Python, how do you sort a list of objects based on one field of the object?
(Think, then click!)
You can use an _anonymous function_:
my_list.sort(key=lambda x: x.field)
lambda x: x.field is a function (with no name, and thus anonymous) that is passed as an optional argument to the sort function.
"Lambda" more broadly means an unnamed function. Later in the semester, we will briefly cover the lambda calculus.
Examples of primarily functional languages: Racket, OCaml, Lisp, Rust
Why study functional languages?
Why should we study these languages, when the most popular programming languages are primarily imperative?
Functional languages:
I would be personally sad if you only saw Python and a bit of Java in your Wellesley degree :(. Seeing more flavors of languages helps you pick up new languages in the future!
Are easier to analyze, because they are more like math.
They help you practice important skills like:
Avoiding mutation.
Recursion
These skills are important because (1) they are less error prone in many cases, (2) they can lead to performance improvements, e.g., by enabling parallel code, and (3) some data and tasks are much easier to use recursion on, but you have had less practice with it.
Mindset
Functional programming is a whole new way of doing things! I encourage you feel comfortable approaching learning Racket and OCaml as fun new games, distinct from the programming you've done prior to now. :)
What is a PL? Syntax + semantics
Syntax
A programming language’s syntax defines its form Syntax tells us:
What are the valid expressions in the PL?
How can we compose them to make bigger expressions?
Concrete syntax: style choices ({ } versus white space)
Semantics
A programming language’s semantics defines its meaning
Syntax tells us:
How do we evaluate expressions in the PL?
How will this program behave?
Dynamic semantics: what happens when a program runs?
What values does it produce?
What side-effects does it have?
Are expressions well-typed?
What is the scope of a variable?
Syntax vs semantics example
Good syntax, bad semantics:
colorless green ideas sleep furiously
vs.
Bad syntax, good semantics:
can anyway probably you this read
Which PL is best? How do we choose what PL to use?
Power versus expressiveness:
Computational power: what can be expressed in a given language?
Expressiveness: what is easy to express in a given language?
Computational power
All languages that are Turing-complete have equal computational power.
Tradeoffs
This class will help you think about picking the right language for the task.
Group exercise: mapping what you know about PLs
4 of the most common languages folks have used (Python, Java, Javascript, R)
What do people say about each language?
What are the pros and cons of each?
How do types work in the language?
Are these languages equally expressive?
Back to PL paradigms: types
Types tell us what operators are valid on what data.
Static means before the program runs.
Dynamic means as the program is running.
Broadly: A static type system finds type errors before the program runs. A dynamic type system only hits the error when the program actually reaches is. People tend to think of static as "stronger" and dynamic as "weaker" types.
Primarily imperative
Primarily functional
Dynamically typed
Python
🆕 Racket
Statically typed
Java
🆕 OCaml
Intro to Racket: your first (probably) functional language!
We will boil down programming to very basic pieces, then build up from there.
The Racket syntax is weird! I'm sorry!
Parenthesis for almost everything: ((()))
Basic values:
Booleans #t and #f Numbers 11.0 and 1/2
Expressions:
Operators: +, -, etc
Operators are prefix: they come before the arguments. Use parens to group.
(+12) ; evaluates to the value 3
Control Flow
So called "one-arm" ifs are not a thing. Instead, if always results in a value:
(if b #t #f)(if b
1
2)
Instead of a chain of if then else if then else, we use cond:
(cond ((= x 0) 5)
((= x 1) 6)
(else7))
Variable definitions
(definex1)
Binds the variable x to the value 1.
Functions
How do we define functions? More parens!
(define (add-onex)
(+ x1))
; Note: whitespace does not matter!; Same as:
(define (add-onex) (+ x1))
Group exercise:
Define our absolute value function in Racket.
Use: < or > , -
(define (abs x)
(if (<0 x)
x
(- x)))
; or
(define (abs x)
(if (< x 0)
(-0 x)
x))
; or many other variants...; Note: x alone does not need parens
Next time, we'll see more Racket in an actual IDE!
#L01
Welcome!
Welcome to CS251: Principals of Programming Languages!
Agenda for today:
Name magnets/name tents
If you missed class, I will have more on Monday!
Professor introduction
I'm Alexa VanHattum–-you can call me "Alexa", "Prof. Alexa", or "Prof VanHattum".
This is my second year as an Assistant Professor at Wellesley. My research focus is the intersection of programming languages & computer systems, with a focus on applying lightweight formal methods to compilers for systems languages.
Before Wellesley, I spent time as a software engineer for Apple health, then completed grad school at Cornell.
Student introductions
(Names, pronouns, year, favorite, least favorite, or weirdest programming language).
This is a smaller elective, and we want to all get to know each other!
Go over the syllabus/website
Goals
In this class, you will:
Programming languages as a field of study
Paradigms
Paradigms are specific models and shared features across different languages. These are usually not strict boundaries, but rather broad groupings.
The imperative paradigm
Most programming you've done up to this point is imperative.
We can think of imperative code as being "step-by-step": you tell the computer what to do via a series of instructions. This comes from the lineage of assembly code, which is organized as a sequence of instructions grouped by jumping between blocks of code.
The primary means of processing data in imperative code is via loops:
for
,while
,do while
, etc.Data in imperative languages is more closely aligned with its representation in memory: memory is a contiguous array of bytes. Types group the bytes into e.g. integers. Types further group basic data types like integers into composite data like arrays.
Examples of primarily imperative languages: C, Java, Python.
The functional paradigm
Functional programming languages are more declarative: they describe what a computation is, rather than how to do it step-by-step. This more closely aligned with pure mathematics.
The primary means of processing data in imperative code is via recursion, instead of loops. Functional languages avoid mutating data, and are thus less close of a representation to low-level program memory.
For example, you'd avoid the following pattern of mutation in most functional languages:
# This code is more imperative than functional x = 3 x = 5
A primary feature of functional languages is that they treat functions as values.
This is related to "first class functions" and "higher order functions", which we'll define more precisely over the coming weeks.
Imperative languages also often support some version of functions as values, even though functions are not necessarily "first class".
For example:
(Think, then click!)
You can use an _anonymous function_:
my_list.sort(key=lambda x: x.field)
lambda x: x.field
is a function (with no name, and thus anonymous) that is passed as an optional argument to thesort
function."Lambda" more broadly means an unnamed function. Later in the semester, we will briefly cover the lambda calculus.
Examples of primarily functional languages: Racket, OCaml, Lisp, Rust
Why study functional languages?
Why should we study these languages, when the most popular programming languages are primarily imperative?
Functional languages:
These skills are important because (1) they are less error prone in many cases, (2) they can lead to performance improvements, e.g., by enabling parallel code, and (3) some data and tasks are much easier to use recursion on, but you have had less practice with it.
Mindset
Functional programming is a whole new way of doing things! I encourage you feel comfortable approaching learning Racket and OCaml as fun new games, distinct from the programming you've done prior to now. :)
What is a PL? Syntax + semantics
Syntax
A programming language’s syntax defines its form
Syntax tells us:
Semantics
Syntax vs semantics example
Good syntax, bad semantics:
colorless green ideas sleep furiously
vs.
Bad syntax, good semantics:
can anyway probably you this read
Which PL is best? How do we choose what PL to use?
Power versus expressiveness:
Computational power
All languages that are Turing-complete have equal computational power.
Tradeoffs
This class will help you think about picking the right language for the task.
Group exercise: mapping what you know about PLs
4 of the most common languages folks have used
(Python, Java, Javascript, R)
Back to PL paradigms: types
Types tell us what operators are valid on what data.
Static means before the program runs.
Dynamic means as the program is running.
Broadly: A static type system finds type errors before the program runs. A dynamic type system only hits the error when the program actually reaches is. People tend to think of static as "stronger" and dynamic as "weaker" types.
Intro to Racket: your first (probably) functional language!
We will boil down programming to very basic pieces, then build up from there.
The Racket syntax is weird! I'm sorry!
Parenthesis for almost everything:
((()))
Basic values:
Booleans
#t
and#f
Numbers
1
1.0
and1/2
Expressions:
Operators:
+
,-
, etcOperators are prefix: they come before the arguments. Use parens to group.
(+ 1 2) ; evaluates to the value 3
Control Flow
So called "one-arm" ifs are not a thing. Instead,
if
always results in a value:(if b #t #f) (if b 1 2)
Instead of a chain of
if
thenelse if
thenelse
, we usecond
:(cond ((= x 0) 5) ((= x 1) 6) (else 7))
Variable definitions
(define x 1)
Binds the variable
x
to the value1
.Functions
How do we define functions? More parens!
(define (add-one x) (+ x 1)) ; Note: whitespace does not matter! ; Same as: (define (add-one x) (+ x 1))
Group exercise:
Define our absolute value function in Racket.
Use:
<
or>
,-
(define (abs x) (if (< 0 x) x (- x))) ; or (define (abs x) (if (< x 0) (- 0 x) x)) ; or many other variants... ; Note: x alone does not need parens
Next time, we'll see more Racket in an actual IDE!