1 of 68

matt’s�~ languages of the week ~

all mistakes are his; sources are cited when possible

2 of 68

quick table of contents

  • JS gone wrong
  • Haskell
  • ML/OCaml
  • JS
  • Lua
  • Go
  • Rust
  • Lisp/Scheme/Racket
  • Python variants
  • Ruby + Sorbet
  • Cirq + Qiskit
  • Liquid Haskell + Idris

3 of 68

4 of 68

javascript gone wrong*

�(and how to infer language behaviour by example)

doing things a lil’ differently…

aside: matt has given variations of this talk a couple of times!

5 of 68

6 of 68

Haskell

We just learned about OCaml and functional programming (FP)!

Haskell is functional programming to the extreme.

  • Heavily used to experiment with new FP features:
    • lazy evaluation and call-by-need
    • type classes
    • monads, effects (ex monadic I/O)
  • Developed since 1987 - by committee!
  • “Reference implementation”: Glasgow Haskell Compiler (GHC)

We cover Haskell in Carey’s CS 131!

  • We met GHC lead developer Simon Peyton Jones!

The Haskell logo. �Haskell website.

7 of 68

Why Haskell?

Compared to OCaml, Haskell is a different approach to functional programming!

Haskell’s characteristics:

  • lazy evaluation (opposed to strict)
    • i.e. values are only evaluated when needed, not when they’re defined
    • what’s the tradeoff here?
  • pure (opposed to impure)
    • no side effects allowed!
    • tradeoff: theory vs practice
  • other complicated stuff
    • effects, monads, modules vs packages, portability, HKTs, typeclasses, generics

And … large performance differences!

And, it has influenced:

  • JavaScript!
  • “Reactive” programming
    • React
  • HDLs & DSLs
  • OCaml & ML!

8 of 68

Haskell code (and lazy eval)

-- Generate first 5 #s divisible by 29

-- between 1000 and infinity

div_by_29 = take 5 [x | x <- [1000..], (mod x 29) == 0]

-- Generate all numbers divisible by 29�-- between 1000 and infinity

div_by_29 = [x | x <- [1000..], (mod x 29) == 0]

9 of 68

10 of 68

ML & OCaml

We just learned about Haskell (and functional programming)!

ML is the influential functional programming language!

  • Direct influence to Miranda and thus Haskell
  • Popularized various functional paradigms, including:
    • type inference
    • pattern matching
    • currying
    • algebraic data types & parametric polymorphism
  • Developed in 1973 by Robin Milner

OCaml is one popular dialect of ML

  • Developed by Inria (~ French DARPA) in 1996
  • Heavily invested-in by Jane Street

11 of 68

Why ML & OCaml?

History and influence:

  • Functional programming is not “new”!
  • Languages often have lineages, dialects, and various implementations
  • Languages are often developed at companies and academia!

OCaml is widely used in PL tooling (compilers, interpreters), formal methods, analysis.

Major projects: Coq, various Facebook-related type retrofitters (Hack, Flow, pyre), Haxe.

We used to cover OCaml in 131!

Compared to Haskell, ML/OCaml are different approaches to functional programming!

Compared to Haskell:

  • strict by default (opposed to lazy)
    • i.e. arguments are always evaluated first
    • what’s the tradeoff here?
  • impure
    • allows imperative layers on top, “practical”
  • other complicated stuff
    • Polymorphic variants, type classes, module system, parallelism

And … large performance implications!

12 of 68

Quicksort in OCaml – similar to Haskell :)

let rec qsort = function

| [] -> []

| pivot :: rest ->

let is_less x = x < pivot in

let left, right = List.partition is_less rest in

qsort left @ [pivot] @ qsort right

13 of 68

14 of 68

JavaScript

Technically it’s actually ECMAScript” smh

A little about JS:

  • dynamically typed
  • no single standard implementation
    • Chrome vs Firefox vs Safari, Deno, Node, Bun…
  • JS was built in 10 days” (not entirely accurate)
  • traces lineage to Scheme (Lisp), Self (Smalltalk), Java

Matt’s most-used language (~200k sloc).

Why is JS interesting?

  • It powers the web! You will probably use it in your job.
  • Significant differences in implementation!
  • It is arguably the most mainstream functional language!

15 of 68

JavaScript

Technically it’s actually ECMAScript” smh

A little about JS:

  • dynamically typed
  • no single standard implementation
    • Chrome vs Firefox vs Safari, Deno, Node, Bun
  • JS was built in 10 days” (not entirely accurate)
  • traces lineage to Scheme (Lisp), Self (Smalltalk), Java

Matt’s most-used language (~200k sloc).

Why is JS interesting?

  • It powers the web! You will probably use it in your job.
  • Significant differences in implementation!
  • It is arguably the most mainstream functional language!

16 of 68

FP in JavaScript (and React)

const listItems = products.map(product =>

<li key={product.id}>

{product.title}

</li>

);

17 of 68

FP in JavaScript (and React) – Map & Lambdas!

const listItems = products.map(product =>

<li key={product.id}>

{product.title}

</li>

);

18 of 68

FP in JavaScript

const dates = [

'2019/06/01', '2018/06/01', '2019/09/01', '2018/09/01'

].map(v => new Date(v));

const maxDate = dates.reduce(

(max, d) => d > max ? d : max, // ternary operator

dates[0]

);

19 of 68

FP in JavaScript – Map, Lambda, & Reduce

const dates = [

'2019/06/01', '2018/06/01', '2019/09/01', '2018/09/01'

].map(v => new Date(v));

const maxDate = dates.reduce(

(max, d) => d > max ? d : max, // ternary operator

dates[0]

);

20 of 68

21 of 68

Lua

Lua is a small, lightweight language designed to be embedded in other applications!

On one hand:

  • very small & simple syntax
  • language itself is feature-light
    • e.g. no classes, inheritance, namespaces, most stdlib methods
    • only composite data type is “Table”
  • features simple C API, runs on small bytecode VM

On the other hand:

  • easily extensible
    • significant focus on metaprogramming with tables
    • users implement OOP features themselves (and decide how they work!)
  • has many advanced “non-syntax” features:
    • e.g. first-class functions, closures, tail calls, GC, coroutines

22 of 68

chunk ::= {stat [`;´]} [laststat[`;´]]

block ::= chunk

stat ::= varlist1 `=´ explist1 |

functioncall |

do block end |

while exp do block end |

repeat block until exp |

if exp then block {elseif exp then block} [else block] end |

for Name `=´ exp `,´ exp [`,´ exp] do block end |

for namelist in explist1 do block end |

function funcname funcbody |

local function Name funcbody |

local namelist [`=´ explist1]

laststat ::= return [explist1] | break

funcname ::= Name {`.´ Name} [`:´ Name]

varlist1 ::= var {`,´ var}

var ::= Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name

namelist ::= Name {`,´ Name}

explist1 ::= {exp `,´} exp

exp ::= nil | false | true | Number | String | `...´ | function | � prefixexp | tableconstructor | exp binop exp | unop exp

prefixexp ::= var | functioncall | `(´ exp `)´

functioncall ::= prefixexp args | � prefixexp `:´ Name args

args ::= `(´ [explist1] `)´ | tableconstructor |

String

function ::= function funcbody

funcbody ::= `(´ [parlist1] `)´ block end

parlist1 ::= namelist [`,´ `...´] | `...´

tableconstructor ::= `{´ [fieldlist] `}´

fieldlist ::= field {fieldsep field} [fieldsep]

field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp

fieldsep ::= `,´ | `;´

binop ::= `+´ | `-´ | `*´ | `/´ | `^´ | `%´ | `..´ |

`<´ | `<=´ | `>´ | `>=´ | `==´ | `~=´ |

and | or

unop ::= `-´ | not | `#´

the entire Lua syntax! (in modified BNF)

23 of 68

compared to…

Similar in length to the CSS Grammar!

24 of 68

local default_fcompval = function( value ) return value end

local fcompf = function( a,b ) return a < b end

local fcompr = function( a,b ) return a > b end

function table.binsearch( tbl,value,fcompval,reversed )

local fcompval = fcompval or default_fcompval

local fcomp = reversed and fcompr or fcompf

local iStart,iEnd,iMid = 1,#tbl,0

while iStart <= iEnd do

iMid = math.floor( (iStart+iEnd)/2 )

local value2 = fcompval( tbl[iMid] )

if value == value2 then

local tfound,num = { iMid,iMid },iMid - 1

while value == fcompval( tbl[num] ) do

tfound[1],num = num,num - 1

end

num = iMid + 1

while value == fcompval( tbl[num] ) do

tfound[2],num = num,num + 1

end

return tfound

elseif fcomp( value,value2 ) then

iEnd = iMid - 1

else

iStart = iMid + 1

end

end

end

array binary search in Lua

Small exercise: yes, you know nothing about Lua, but can you guess…

  • dynamic or static typing?
  • any functional features?
  • is there a dangling-else problem?

25 of 68

who uses Lua?

Lua is now* primarily used:

  • as an extension scripting language
    • on top of tooling: Redis, Nginx, Apache, Neovim, LuaTeX, pandoc
    • in applications: Photoshop & Lightroom, DaVinci Resolve
  • embedded in video games (often for scripts)
    • Roblox, World of Warcraft, Garry’s Mod, DOTA 2
    • ~ matt lore ~ – I learned Lua through a Minecraft mod called ComputerCraft

Question for you: why do you think these are the prevalent uses?

*for some time, it was also used in AI & operating systems work, but imo those died out

26 of 68

so, why Lua?

Takeaway: spectrum on how lightweight language should be; no “right” answer!

  • lightweight: Lua, ~ OCaml
  • Mid-tier (large variance): C++, JavaScript, Ruby, ~Rust
  • strong: Python, Java, ~ PHP

Other dimensions:

  • core language versus standard library
  • strength of package/module system
  • reliance on third-party ecosystems

27 of 68

28 of 68

Go

“We hated C++ so much, we made a new language…”

  • made by a team at Google because they really, really, really hated C++
  • first-class primitives for parallelism
  • “simple enough to hold in your head”

Why Go?

  • we’ve learned about weird C++ behavior!
  • we met Robert Griesemer!!
  • super related to many course concepts: GC, first-class functions, concurrency, …

29 of 68

go’s defer keyword

// Contents returns the file's contents as a string.

func Contents(filename string) (string, error) {

f, err := os.Open(filename)

if err != nil {

return "", err

}

defer f.Close() // f.Close will run when we're finished.

var result []byte

buf := make([]byte, 100)

for {

n, err := f.Read(buf[0:])

result = append(result, buf[0:n]...) // append is discussed later.

if err != nil {

if err == io.EOF { break }

return "", err // f will be closed if we return here.

}

}

return string(result), nil // f will be closed if we return here.

}

30 of 68

goroutines with go (go by example)

$ go run goroutines.go

direct : 0

direct : 1

direct : 2

goroutine : 0

going

goroutine : 1

goroutine : 2

done

func f(from string) {

for i := 0; i < 3; i++ {

fmt.Println(from, ":", i)

}

}

func main() {

f("direct")

go f("goroutine")

go func(msg string) {

fmt.Println(msg)

}("going")

time.Sleep(time.Second)

fmt.Println("done")

}

31 of 68

Go is designed to avoid “footguns”.

It stops you from doing things like…

Having undeclared variables!

$ go run prog.go

./prog.go:6:2: a declared and not used

Go build failed.

func main() {

a := "hi"

}

32 of 68

opinionated languages

Go is part of a larger trend of opinionated / “restrictive” languages:

  • like Rust and Python, there is a “correct” style & formatting
  • furthermore, Go does not have: unions, assertions, exceptions, inheritance, overloading, implicit type coercion

Many languages restrict you from doing things that you can do in C and C++.

Core argument: what we’ve learned in class from C & C++’s weak typing, and similar arguments to memory safety, threads, and more!

33 of 68

34 of 68

Rust

A better C++ (for systems programming)?

Some neat things about Rust:

  • compile-time memory safety
  • first-class functional programming!!
  • super, super good tooling (rust-analyzer)

Also has a super vibrant community, and is well-loved: Rust is the most-loved language 7 years running in the SO Dev Survey!

35 of 68

Rust & Memory Safety

Rust does not allow (and catches at compile time):

  • dangling pointers / use-after-free / double-free
  • data races in memory
  • null-related issues (null doesn’t exist!)
  • and much, much more!

What’s the secret sauce?

36 of 68

First: let’s review our memory model. What’s probably going on?

let s1 = String::from("hello");

let s2 = s1;

// ... some stuff happens

println!("{}, world!", s1);

37 of 68

In Rust (and most langs): a shared reference!

let s1 = String::from("hello");

let s2 = s1;

// ... some stuff happens

println!("{}, world!", s1);

38 of 68

Okay, so … what could go wrong?

let s1 = String::from("hello");

let s2 = s1;

// ... some stuff happens

println!("{}, world!", s1);

39 of 68

Yikes!!

let s1 = String::from("hello");

let s2 = s1;

delete(s2);

println!("{}, world!", s1);

This is really bad! We’re inadvertently also affecting s1!

Then, this becomes a use-after-free!

40 of 68

Rust & Memory Safety

Rust does not allow (and catches at compile time):

  • dangling pointers / use-after-free / double-free
  • data races in memory
  • null-related issues (null doesn’t exist!)
  • and much, much more!

What’s the secret sauce? Lifetimes, bindings, and references!

41 of 68

Rust’s solution!

let s1 = String::from("hello");

let s2 = s1;

// ... some stuff happens

println!("{}, world!", s1);

Here, s1’s lifetime ends.

42 of 68

Then, this becomes a compile-time error in Rust.

$ cargo run

Compiling ownership v0.1.0 (file:///projects/ownership)

error[E0382]: borrow of moved value: `s1`

--> src/main.rs:5:28

|

2 | let s1 = String::from("hello");

| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait

3 | let s2 = s1;

| -- value moved here

4 |

5 | println!("{}, world!", s1);

| ^^ value borrowed here after move

|

= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0382`.

error: could not compile `ownership` due to previous error

43 of 68

“but matt”, you say,

“that seems really inconvenient…”

and you’d be right!

44 of 68

there are, of course, still ways to work with strings in Rust.

bigger picture: like with functional programming, Rust adds restrictions to your code. The tradeoff?

Memory Safety!

(and it turns out, this tradeoff is often worth it!)

45 of 68

46 of 68

Lisp & Scheme & Racket

Lisp is the second oldest high-level PL!

  • functional programming & heavy emphasis on lists
    • List Processing
  • lots and lots of parentheses :))))))))))))))))))))))))))
  • some related terms: lambda calculus, s-expressions

Used to be heavily used for AI research & NLP.

Huge influences in:

  • functional programming
  • metaprogramming
  • and so much more…

47 of 68

Lisp & Scheme & Racket

A Lisp dialect made by Guy Steele* and Gerald Sussman in the “Lambda papers”.

Defining features:

  • simple, limited syntax
    • why is this good? remember Lua?
  • “new” features: tail call optimization, lexical scope
  • “first-class continuations”

Bringing that all together: metaprogramming is easy!

no logo???

48 of 68

Lisp & Scheme & Racket

A Lisp dialect that’s also a Scheme “descendant”

  • technically not a super/subset of Scheme
  • Racket can run all Scheme programs (of a certain version)
  • online, people use them interchangeably – confusing!!

Racket has some defining features:

  • huge macro support
  • large community & tooling (for a Lisp dialect)
  • and many other things (that you probably won’t use)

Often used: creating & teaching PLs!

49 of 68

50 of 68

Python variants

(okay, it’s not one language)

Jython

Python on JVM

PyPy

JIT Python

CircuitPython

For microcontrollers!

Cython

Compiled Python!

51 of 68

Why talk about variants?

Common misconceptions:

  • only one implementation of a language
  • languages are compiled/interpreted
  • runtimes are strictly a property of a language

Case study, PyPy:

  • “On average, PyPy is 4.8 times faster than CPython
  • Stackless Python: erase call stack on function calls!!!
  • lack of compat with some C FFI / extensions

And, the failure of certain separations!

  • Libraries rely on features not in the spec, but part of CPython!
  • Big issue with Python 2.x -> Python 3.x!

52 of 68

here are alternative runtimes for other languages!

53 of 68

54 of 68

Ruby

A general-purpose, dynamic, and “fun” lang!

Shares many similarities with Python:

  • dynamically-typed, usable for scripting
  • general-purpose, supports many paradigms

But, it has some differences:

  • first-class OOP & FP: �everything is an object & expression!
  • more metaprogramming & introspection
  • largely popular in web development:�Ruby on Rails, Jekyll, …

Powers: Stripe, Shopify, GitHub, AirBnB, Coinbase, …

55 of 68

Ruby (and Sorbet!)

Sorbet is a static + runtime type system for Ruby.

  • autogenerates types for library files with reflection & introspection (a form of metaprogramming!)
  • implements IDE-level type support!!
  • mainly developed at Stripe and Shopify

Sorbet guesses types for 100k+ LoC libraries with only code examples, without manual intervention!

(disclaimer: matt did some OSS Sorbet work at CZI :’)))))

56 of 68

sig {params(name: String).returns(Integer)}

def main(name)

puts "Hello, #{name}!"

name.length

end

main("Sorbet") # ok!

main() # error: Not enough arguments provided

man("") # error: Method `man` does not exist

Sorbet catches type errors statically - in your editor!

Developers write this annotation

57 of 68

58 of 68

Cirq & Qiskit

A new frontier of programming languages*!

Both OSS Python SDKs/DSLs built by companies:

  • Cirq: Google QuantumAI
  • Qiskit: IBM Quantum

Core problem: representing quantum objects (qubits, gates, circuits) in a non-quantum PL!

If you’re interested, take Palsberg’s class!

*matt’s opinion: quantum programming is far too limited by (physical) engineering problems. “we still can’t factor 21”. but, it’s neat!

59 of 68

import cirq

# Pick a qubit.

qubit = cirq.GridQubit(0, 0)

# Create a circuit

circuit = cirq.Circuit(

cirq.X(qubit)**0.5, # Square root of NOT.

cirq.measure(qubit, key='m') # Measurement.

)

print("Circuit:")

print(circuit)

# Simulate the circuit several times.

simulator = cirq.Simulator()

result = simulator.run(circuit, repetitions=20)

print("Results:")

print(result)

import qiskit

# create circuit with Qiskit quantum circuit libraries

quantum_circuit = qiskit.circuit.library.QuantumVolume(5)

quantum_circuit.measure_all()

quantum_circuit.draw()

# select simulator backend

from qiskit import BasicAer

backend = BasicAer.get_backend('qasm_simulator')

# prepare your circuit to run on the simulator

optimized_circuit = qiskit.transpile(quantum_circuit, backend)

optimized_circuit.draw()

# run on simulator

job = backend.run(optimized_circuit)

result = job.result()

print(result.get_counts())

60 of 68

61 of 68

type-level programming (LiquidHaskell & Idris)

Two main languages in this field:

  • Idris: 2007
  • Liquid Haskell: 2008

Why “type-level programming”?

  • supercharging parametric polymorphism
  • interesting notion: “first-class types”
  • new developments in Haskell :) (and PL!)

62 of 68

... but what is dependent typing?

Let’s start with refinement types, like what’s in LiquidHaskell:

Q: What does it looks like this does?

A: 🤔

addPos : { a : Int | a > 0 } -> { b : Int | b > 0 } -> { n : Int | n > a & n > b }

addPos a b = a + b

63 of 68

... but what is dependent typing?

Let’s start with refinement types, like what’s in LiquidHaskell:

Q: What does it looks like this does?

A: this encodes three “refinements”:

  • a > 0, i.e. a is positive
  • b > 0, i.e. b is positive
  • n > a, n > b, i.e. the sum is larger than the inputs

And, the compiler will check these for you!

addPos : { a : Int | a > 0 } -> { b : Int | b > 0 } -> { n : Int | n > a & n > b }

addPos a b = a + b

64 of 68

... but what is dependent typing?

Why do we care?

Think about functions that are only defined on parts of their type’s domain:

  • factorial (only positive integers out of all integers)
  • arcsin, arcos (|x| <= 1 out of all numbers/floats)
  • inverse of a matrix (invertible)

Instead of magic values (like -1), we can embed this in the type system!

65 of 68

... but what is dependent typing?

Dependent typing is a true application of type-level programming; types can depend on values (and be used like any other value)!

Here’s the classic Idris example: lists where the length is part of the type!

data Vect : Nat -> Type -> Type where

Nil : Vect 0 a

(::) : (x : a) -> (xs : Vect n a) -> Vect (n + 1) a

66 of 68

... but what is dependent typing?

Here’s the classic Idris example: lists where the length is part of the type!

Q: why would we want this?

A: ?

data Vect : Nat -> Type -> Type where

Nil : Vect 0 a

(::) : (x : a) -> (xs : Vect n a) -> Vect (n + 1) a

67 of 68

... but what is dependent typing?

Here’s the classic Idris example: lists where the length is part of the type!

Q: why would we want this?

A: (among many other things) we can implement true math vectors!

data Vect : Nat -> Type -> Type where

Nil : Vect 0 a

(::) : (x : a) -> (xs : Vect n a) -> Vect (n + 1) a

total

pairAdd : Num a => Vect n a -> Vect n a -> Vect n a

pairAdd Nil Nil = Nil

pairAdd (x :: xs) (y :: ys) = x + y :: pairAdd xs ys

68 of 68

if you found that idea interesting,�good dependent typing is a “holy grail” of PL�– and an active research area :)