HomeVideos

Constraints Liberate, Liberties Constrain — Runar Bjarnason

Now Playing

Constraints Liberate, Liberties Constrain — Runar Bjarnason

Transcript

1284 segments

0:01

[Music]

0:10

[Music]

0:20

[Music]

0:25

I want to start kick off this conference

0:28

by by talking to you about this sort of

0:31

uh rather counter-intuitive thing

0:34

which is sort of this this tension that

0:37

I think exists between expressive power

0:40

on one hand and analytical power on the

0:43

other hand and as you sort of expand one

0:46

of those things you necessarily contract

0:49

the other

0:51

so as a as a wee young Runar I was

0:55

programming in Delphi on on Windows

0:58

anybody any Delphi programmers in here

1:00

yeah

1:02

all right

1:03

so don't you know why object Pascal

1:05

didn't take over the world I will never

1:07

know but um

1:09

so yeah I was you know programming in

1:11

Delphi on Windows

1:13

and uh I was working for a publisher of

1:17

poetry of all things and uh and this is

1:20

the first program I ever sold to anybody

1:24

um

1:25

and so one of the things that this thing

1:26

needs to do was to print out sales

1:28

reports for the publisher and I needed

1:30

to do that on this beautiful piece of

1:31

Machinery right here

1:33

now unfortunately at that time Delphi

1:36

did not have very good facilities for

1:38

printing so I did what any

1:41

you know any hacker would do I got out

1:43

the the star dot matrix printer

1:45

reference manual and I just took their

1:48

SQL tables and I turned it directly into

1:50

printer control codes over the parallel

1:53

port

1:54

now of course

1:55

once you've printed something on the

1:57

paper you know it's on the paper and

1:59

there's there's nothing you can do

2:01

so then the client comes back and says

2:02

hey these reports look great but you

2:05

know I got a new printer

2:06

so you know can I can I print this out

2:09

on my new printer and I had to say no

2:11

uh and then they asked well I'd like to

2:14

take you know this report over here and

2:16

this other report over here and then

2:18

sort of put them together side by side

2:19

on one sheet of paper can you do that

2:21

and I have to say

2:23

no

2:25

um so so about going directly to the

2:29

sort of full power of the parental

2:31

control code by like having complete

2:33

control over the printer you know I

2:35

I wasn't making making use of any kind

2:37

of abstraction that would allow me to

2:39

sort of you know abstract over the over

2:41

the printing so what I should have been

2:43

doing was to generate some kind of

2:45

intermediate form

2:47

that allows for composition you know

2:49

some kind of syntax tree or something

2:51

that I could then you know go and render

2:54

out to the to the printer as sort of the

2:56

last possible step

2:59

so that's some other point

3:01

uh I was working with strings that that

3:03

rather uh looked like this

3:05

so uh

3:07

some strings that contain some SQL right

3:09

so it's you know select some stuff from

3:11

some table where some condition is true

3:13

uh now the problem with with this is

3:15

that it's a string right so the the

3:17

structure of the SQL in here is sort of

3:19

lost inside of this string so if you

3:22

want to do something like add a column

3:24

or add a condition or something

3:26

uh that's actually really difficult to

3:29

do because you know you have to sort of

3:31

ReDiscover what it is that the structure

3:33

of the string actually is

3:35

or you know you have to do some horrible

3:37

hack which you know I've done a lot of

3:40

so again what I needed here was an

3:44

abstract representation some kind of

3:45

intermediate form that I can then

3:47

manipulate

3:48

and then at the last possible moment

3:50

when I'm really sure that I don't want

3:52

to manipulate it any further I compile

3:54

that to a string and then I send that to

3:58

uh to the database

4:00

right so maybe sort of what I was

4:03

discovering was this principle that any

4:05

problem can be solved by just adding

4:07

another layer of indirection

4:11

uh and so David wheeler here is a little

4:14

bit

4:15

uh tongue-in-cheek about this but it

4:18

makes a good point about abstraction and

4:20

composition

4:22

so as an illustration when uh when we're

4:26

working with explosions as you do you

4:30

want to work with a representation like

4:31

this right so you want to work with the

4:34

unexploded form right you want you want

4:36

to well be you know because it's

4:38

compositional for one thing like you can

4:40

take two blocks of of TNT in Minecraft

4:43

and and you can put them together right

4:47

to make a larger explosion

4:49

um and then you know just like when

4:52

we're working with side effects in our

4:53

in our programs you know we want to work

4:55

with them also in the sort of unexploded

4:56

form we do not want to work with them in

4:59

this form

5:00

right so so whenever I see side effects

5:02

in in code you know this is what I want

5:04

to think of it's just think of

5:06

explosions going off everywhere so so

5:09

yeah once you've sort of detonated your

5:10

Dynamite or your side effects uh

5:12

composition is is out the window

5:15

and when we're working with explosives

5:17

we really want you know that extra level

5:20

of indirection

5:23

so I think that this is a mistake that a

5:27

lot of people make I know that you know

5:29

I make this mistake a lot and it's a

5:31

mistake that's really easy to make

5:33

which is uh choosing to work in a

5:37

language that is sort of too large for

5:39

the task at hand it's insufficiently

5:41

abstract and insufficiently uh precise

5:46

and so we tend to optimize for for the

5:48

task at hand for the thing that we're

5:50

working on right now and uh and forget

5:53

to optimize for the task that's down the

5:55

line which is integrating our uh our

5:59

code into a larger system or a larger

6:02

conceptual framework

6:04

so go into a concrete representation of

6:07

what we're working on sort of too early

6:10

and this comes up in all kinds of

6:13

situations and not just with side

6:15

effects for example in this very simple

6:18

geometry problem uh let's say we want to

6:20

find the area of the yellow square right

6:23

really simple we know that this red

6:26

triangle is a right triangle and we know

6:28

the two sides that they're both two

6:31

so you know we know our Pythagoras and

6:32

we just say like well you know 2 squared

6:34

plus 2 squared is 4 is 8 and then we

6:37

take the square root of eight and we get

6:38

something like 2.828 something right and

6:42

we you know obviously turn that into a

6:44

double and then we square that right

6:48

so when we square that we end up with

6:51

something like eight so not exactly

6:53

eight so so what has happened here

6:56

is is that you know we've sort of

6:58

detonated our our square root too early

7:00

you know we've lost Precision we've gone

7:03

to a representation uh that is sort of

7:05

insufficiently uh precise and uh and it

7:10

doesn't allow for this kind of

7:11

compositional reasoning right

7:14

so we've we've sort of created when we

7:16

take the square root and go to doubles

7:18

we created this barrier that this allows

7:20

further uh composition

7:23

and something has been lost here more

7:26

than just the the sort of precision of

7:27

the answer what has been lost is the

7:29

algebraic relationship between these

7:32

squares because the Pythagorean theorem

7:35

is really about the relationship between

7:37

three squares that all lie on a triangle

7:42

like this and the relationship is that

7:44

you know the area of the square that's

7:46

on the patent hypotenuse is the sum of

7:49

the areas of the other two squares

7:52

right so hopefully you can see the

7:54

analogy here between

7:55

this and and side effects so we're using

7:58

side effects is like working with you

8:00

know distances and doubles and using

8:02

algebraic data types and pure functions

8:05

is rather like working with you know the

8:06

algebraic relationships between uh

8:09

squares

8:11

so the moral of the story in in this

8:14

case is that you want to work

8:15

symbolically right you want you want to

8:17

manipulate symbols for as long as you

8:19

can and then sort of detonate to

8:22

decimals or to some kind of

8:23

representation that is uh

8:26

uh

8:27

larger or sort of uh like they could

8:30

mean more possible things as the last

8:33

possible step

8:35

so yeah this kind of problem of uh of

8:39

having concretized too early comes up in

8:41

situations that are much closer to our

8:43

hearts right as scholar programmers

8:45

for example the problem of binary

8:47

compatibility

8:49

so binary compatibility comes up because

8:51

of this kind of problem of having gone

8:54

to a binary representation sort of too

8:56

early

8:57

so when the scholar compiler goes in and

9:00

generates a binary from a Scala file you

9:05

know it's it creates this intermediate

9:08

form this kind of tree like the syntax

9:10

tree of the Scala

9:12

and then it goes and throws that away

9:14

right and just leaves the the binary for

9:17

you to then consume to construct further

9:20

code right

9:21

so then you write some more code and

9:23

that consumes the sort of the library

9:25

that you had just compiled before

9:27

but now what you're consuming is a

9:29

binary and the binary representation is

9:32

sort of strictly larger and less precise

9:34

than what you want right

9:38

so we've we've sort of you know done the

9:40

same thing as when we like took the

9:41

square root and went to doubles

9:44

um we have thrown away this abstract

9:46

relationship between uh entities in

9:50

Scala and so then when we try to combine

9:53

that with other things you know what

9:54

we're thinking about is combining trees

9:57

but what we're doing actually is trying

9:59

to recover uh sort of hypothetically the

10:04

idea of the tree from the binary which

10:06

may or may not represent that actual

10:08

tree because we may have done things

10:10

like oh gone to a different version of

10:12

that library or gone to a different

10:13

version of Scala C altogether

10:17

a partial solution to to this that

10:19

Martin odersky and others are working on

10:22

is this idea of shipping the abstract

10:25

syntax tree along with the bytecode so

10:28

and they're calling this tasty trees

10:29

which I think is kind of an awesome name

10:32

uh and so yeah the reason here is that

10:34

you know the byte code representation is

10:36

sort of too large and and it's a barrier

10:39

to composition and reasoning and so not

10:42

throwing away that absolute

10:43

representation uh helps or is going to

10:47

help enormously I

10:50

so the moral of the story here is that

10:52

you know we want to work with syntax

10:54

trees for as long as we can we want to

10:56

work symbolically and then we want to go

10:58

to binaries as late as possible we want

11:00

to compile

11:02

as the last possible step

11:04

because really uh compilation when you

11:07

really think about it is is an

11:09

optimization step like you really only

11:11

need to do it when you want the thing

11:12

you know to run quickly on the machine

11:15

and as we know parameter optimization is

11:18

the root of all evil

11:21

but uh more than that I want to say that

11:25

that's really just a special case of

11:27

something else that the the root of at

11:31

least sort of a moderate amount of evil

11:33

is uh

11:35

premature loss of precision or

11:38

compilation or folding or going too

11:40

early to a uh too concrete or two large

11:45

representation

11:47

so to a representation that is sort of

11:49

too unconstrained and too powerful

11:51

right

11:53

and this is uh sort of a classic

11:56

mistake or a classic error I think that

11:58

is really easy to to make and it's not

12:01

just something that beginners do I think

12:02

experts make this mistake all the time

12:05

uh for example if you open up any uh

12:08

calculus text you'll see that they go

12:10

directly for the most powerful number

12:13

system they could possibly think of like

12:15

everything that could possibly have uh

12:18

everything that they they might possibly

12:20

need

12:21

all right and it's easy to think I mean

12:23

it's interesting to to think about

12:25

uh what kind of structure we're missing

12:29

out on what kind of reasoning we could

12:30

possibly be doing sort of within

12:32

calculus

12:34

if we you know restricted ourselves to

12:38

maybe something slightly smaller than a

12:41

number system that includes you know

12:42

uncountable reels and non-computable

12:45

numbers and and things like that

12:47

and I want to say that this is sort of

12:49

the same phenomenon as doing extremely

12:52

type programming uh where you know like

12:55

with SQL where you know I have just a

12:57

string that represents whatever I'm

12:59

working on because you can always

13:00

represent anything as a string right so

13:02

why not just work with strings

13:04

well because you know then you have to

13:06

ReDiscover what is the the structure of

13:08

the of the string and I want to say that

13:11

reaching too early for things like

13:13

General recursion and reaching for

13:15

languages that have side effects are

13:17

also a species of this same kind of

13:20

thing

13:22

so because you know when we're doing

13:24

these things uh when we're when we're

13:26

coding you know we should ask ourselves

13:28

like do we really need General recursion

13:31

do we need really need to go to a string

13:32

here do I really need a side effect here

13:35

and what am I missing out on

13:37

well the fact is that the more

13:40

expressive something some language can

13:42

be the sort of larger it is the less we

13:45

can analyze what we have expressed in

13:49

that language

13:50

because the more kinds of things

13:52

something could potentially be

13:54

the less we can actually reason about

13:57

what it is like what it actually is

13:59

right

14:01

so you know as as the potential stuff

14:03

gets larger they you know this sort of

14:07

a number of things that could that we

14:09

could actually sort of predict about it

14:10

uh is is smaller

14:14

so my point is here that we want to

14:17

choose representations that are

14:19

sufficiently abstract uh or you know

14:21

sufficiently indirect

14:24

because abstraction buys you this

14:26

compositionality and and failing to be

14:29

abstract enough creates this barrier uh

14:32

so you detonate sort of too early but

14:35

abstraction also buys you precision and

14:38

uh now many people might think that

14:40

abstraction and precision are opposites

14:42

but they're not in fact I want to say

14:45

that abstraction is what makes Precision

14:48

possible

14:50

in the words of escrow Dykstra the

14:52

purpose of abstraction is not to be

14:54

vague but to create a new semantic level

14:57

in which one can be absolutely precise

15:01

and to demonstrate that in a very simple

15:03

way

15:04

here is a a signature that is not very

15:07

abstract

15:08

so

15:10

uh if you ask yourself how many

15:12

implementations exist of this type

15:16

like a lot right it's something like 4

15:19

billion to the

15:21

to the four billionth power

15:23

and and you know that's a huge number

15:25

and that's not even counting the ways

15:27

that you could do side effects and throw

15:29

exceptions and you know involve nulls

15:31

and absurdities and things like that in

15:33

in a program that meets this type

15:35

signature

15:37

but if we allow the uh the type to vary

15:42

that is if we abstract over the type so

15:45

this type signature you know is is sort

15:48

of the same as before that it's you know

15:49

it's a function from one type to another

15:51

except now the type

15:52

has been abstracted out

15:55

so now how many implementations exist

15:56

for this signature one right well

15:59

there's only one that actually you know

16:02

makes sense you know without side

16:03

effects and without nulls and so if we

16:06

don't appeal to to those kinds of things

16:07

but there's only one way that you could

16:09

take a value of type A for any type A

16:12

and then return

16:14

a value of type A for that same type A

16:16

you have to return the value that you

16:19

were given right

16:21

so here in allowing the type to vary

16:24

we've introduced a freedom to the caller

16:27

yeah

16:28

so they can choose any type

16:31

but this Freedom constrains the

16:33

implementation

16:34

yeah

16:36

and

16:37

that in turn introduces a Precision

16:40

because this type specifies precisely

16:44

what this function is

16:46

and this is very sort of

16:47

counterintuitive that you know making

16:49

something more abstract is making it

16:51

more precise that you know making

16:54

something freely vary then creates a

16:57

constraint

17:00

um but I want to I want to say that sort

17:03

of in general that

17:05

for you know if you introduce a freedom

17:07

at one level then that leads to

17:09

restriction sort of further down the

17:11

line at a different level

17:14

and this works the other way around that

17:15

if you constrain something sort of

17:18

Upstream then that leads to freedom and

17:20

power further down the line because now

17:24

you can you know once things once

17:26

something is very constrained you can

17:27

now reason more about what that could

17:29

possibly be

17:31

okay so let's take some examples for

17:34

example monads versus applicatives

17:38

to give you a quick sort of refresher on

17:41

on monads and applicatives so a functor

17:43

is just something that you can map over

17:46

right it's

17:47

some data type f for which there exists

17:50

a map right so you if you have a

17:52

function from A to B you can get a

17:54

function from F of a to f b for this

17:57

data type f for example if f is a list

18:00

then you know you can map over the list

18:01

with a function that takes all the

18:03

changes the elements to some other type

18:06

all right

18:08

um

18:09

now applicative functors are functor so

18:12

you can map over but they come with

18:14

these additional capabilities uh the

18:18

capability that you can take any value

18:20

and sort of put it inside of the context

18:24

for example you can with with a list you

18:26

can take an element and you can put it

18:28

inside of a single element list

18:30

and then the other capability of

18:32

applicative functors is that if you have

18:34

two of them if you have an F of a and an

18:36

F B sort of side by side you can put

18:39

them together

18:40

and you can do so

18:43

only if you have a function where you

18:46

can combine the elements right the the

18:48

A's and B's in those two f f a and F of

18:51

B if you can combine them into C's then

18:53

you can combine the F of a and F of B

18:55

into a combined F of C

18:58

all right so you can sort of sum them

19:00

yeah

19:02

or multiply them uh

19:04

and then an applicative functor that

19:07

comes with one additional capability is

19:10

a Monet

19:12

so a Monet is is an applicative functor

19:14

that has this additional method join

19:17

where we can if we have a nested F if we

19:21

have an F of f of a then we can turn

19:23

that into an F of a for example uh I'm

19:27

gonna go go on with the lists if if you

19:29

have a nested list of lists then you can

19:31

concatenate all those lists into a

19:32

single list if you have you know a three

19:35

with threes you can graft all the trees

19:37

into the nodes of the tree and you have

19:38

a

19:39

sort of a flatter tree

19:41

if you have an option of option you know

19:44

you can collapse those things together

19:46

right

19:47

and this allows us to do

19:49

you know it allows us to write flat map

19:51

so flat map is is map then join

19:54

and then you know it allows us to use

19:57

four comprehensions and so it allows us

19:59

to assign to variables and then use

20:01

those variables later on in our sort of

20:03

nested in the inner context

20:07

but uh in doing so something is lost and

20:11

when we go from applicative functors to

20:14

monads we we gain this additional

20:17

capability of joining and flat mapping

20:20

but we lose something as well we lose

20:22

the ability to mechanically compose

20:24

these things

20:26

so if you have sort of two data types or

20:28

two languages or you know two

20:30

two applicative functors f and g

20:32

uh you can always mechanically compose

20:35

them into a composite functor FG

20:39

so for example you know if you have a

20:41

list of options you can you know map

20:44

over

20:45

both of those structures at the same

20:46

time and you know you can combine two

20:50

maps of options or list of options or

20:52

whatever and combine them together using

20:54

the applicative

20:55

uh and coming up with an applicative

20:58

instance for the composite is completely

21:00

mechanical and can be done once and for

21:02

all and the implementation is is really

21:04

really simple

21:05

all right it's just like you know for

21:07

the pure thing just lift the pure thing

21:09

and you know literally just compose the

21:11

appears and with map too uh it's

21:14

literally just composed the two twos

21:16

all right but if we want to do this with

21:19

monads this is not possible in a

21:21

mechanical way

21:23

composition of monads requires a

21:26

mechanism that is specific to the monads

21:29

that are being composed so we need to

21:31

know something extra we need some

21:33

additional information about the

21:35

individual monads in order to be able to

21:37

compose them

21:38

and this is why we need things like

21:39

Monet Transformers

21:41

uh I want to say that uh this this

21:45

applies to Monas that you want to

21:46

compose in this particular way you might

21:48

be able to you can use things like code

21:51

products and other things to compose

21:52

monads in other ways but if you want to

21:55

uh you know if you have two monads f and

21:57

g and you want the monad FG uh

22:01

then you may be able to get it but you

22:04

cannot get it in a completely mechanical

22:06

once and for all way

22:09

all right so this is an example of where

22:13

something is strictly more powerful than

22:15

another thing but then is less

22:17

compositional right right we lose some

22:20

kind of composition

22:21

so monads offer more

22:24

uh for the first order like we can we

22:27

can take a moment we can immediately do

22:28

more stuff

22:30

but amona can participate in fewer

22:33

applications right it can participate in

22:36

fewer compositions of things

22:39

yeah so I can I can participate in fewer

22:41

systems of solutions if

22:46

you want so again this is an example

22:47

where a constraint at one level we

22:50

constrain ourselves to applicative

22:51

functors and this creates a freedom or

22:54

power at a higher level in the at the

22:56

level of composing systems

23:00

all right another example of this is uh

23:03

actors versus Futures and I kind of

23:06

wanted to put in a little parenthesis

23:08

like fight right

23:11

um

23:12

so so Futures are more constrained than

23:15

actors in the sense that they have an

23:18

algebra uh so you can do sort of

23:22

algebraic reasoning with with futures

23:25

uh so for example if you have something

23:26

like a monadic future you can do uh

23:29

things like Fork you know you can take

23:30

an a you can create a future a uh you

23:32

can map you know you so if you have a

23:35

function from A to B and you have a

23:37

future of a you can extend that future

23:39

just by sort of appending that function

23:41

and say Hey you know once you're done

23:42

with this do this other thing as well

23:44

and then you can join that as you can

23:46

tell if you have you know a process that

23:49

spawns some sub processes you can ask

23:51

that process to sort of wait on its

23:52

child processes

23:54

and this all sort of behaves according

23:56

to the monad laws and so

23:59

that fact gives us the power to use this

24:02

with Magnetic libraries and to reason

24:04

about it as a monad right and so that

24:06

gives us this algebraic reasoning

24:10

now contrast that to uh you know a type

24:13

like this right so this is the type that

24:16

that actors are in akka at least are

24:19

built around

24:20

so this is you know a function from any

24:22

to unit

24:23

and so this is completely unconstrained

24:26

right

24:29

uh so you start with

24:31

you know anything you could you could

24:34

receive a value of any type whatsoever

24:37

uh you don't know up front what this

24:39

what this could be uh and then it just

24:42

returns a unit and even that is not

24:44

really true what it does is not return a

24:46

unit it actually goes and has a side

24:49

effect right so it'll you know the type

24:52

of this is even bigger than this right

24:55

right

24:56

um

24:56

so

24:58

so there's no algebraic uh theory about

25:01

this type right there's there's no way

25:03

we can sort of reason about this

25:04

algebraically

25:06

um

25:07

and

25:09

the the this is aggravated by the fact

25:12

that not only can the actor at any you

25:15

know can the actor be anything like you

25:17

know the thing that's behind the

25:19

signature any two unit could be anything

25:20

it can become something else like

25:23

between calls to to the function right

25:27

so if somebody has called become an

25:29

unbecome like between two calls of your

25:31

of your function it could be doing uh

25:34

strictly different things

25:36

so uh I was given I was given a talk uh

25:40

at one point about you know Scala

25:43

concurrency and somebody said well why

25:45

would you not want to do this right well

25:47

you know if I if I can have this power

25:50

you know obviously you know AKA actors

25:52

are are more powerful than Futures why

25:55

would I ever want to use Futures because

25:56

I can do strictly more with Arc actors

25:59

well precisely because you can do more

26:03

with AKA actors you should want to

26:06

rather use futures

26:08

because uh you know they're more

26:11

constrained and that makes because

26:13

they're very unconstrained that makes

26:14

our actors hard to to reason about

26:17

right so not only can it be anything at

26:19

all it can become something else between

26:21

calls so to take an extreme example

26:25

um

26:25

or sort of actors are sort of the the

26:28

extreme example right they're really

26:30

really powerful right you can do

26:33

you know it's it's unconstrained side

26:34

effects right you can do anything you

26:36

want when somebody calls your uh your

26:40

actor

26:40

but uh precisely because they're so

26:44

powerful a reason about programs is

26:46

severely limited right because you know

26:49

like the right honorable John Dahlberg

26:51

Lord Acton once said uh Power tends to

26:54

corrupt an absolute power corrupts

26:57

absolutely

26:58

now he was obviously talking about

27:00

political power but in in the case of

27:02

side effects uh what is corrupted is our

27:06

ability to analyze and compose

27:11

uh for example if you consider

27:13

this simple kind of equation right

27:16

so I'm going to stop picking on actors

27:18

for now but if you consider this kind of

27:21

equation uh

27:23

where we want to say that oh if you have

27:25

a list called x's and you map over that

27:27

by subtracting some number n and then

27:29

you map over it again and you want to

27:30

add another number n

27:33

um

27:34

shouldn't that be the same list that you

27:36

started with yeah

27:37

make sense

27:39

but what if n has a side effect

27:41

right so if n has a side effect and this

27:44

is clearly not going to be the same

27:46

right

27:47

n could throw an exception it could be

27:50

null so X's could actually just be you

27:53

know a list and then you know this other

27:55

expression could be a throwing an all

27:57

pointer exception

27:59

uh

28:00

so in the presence of exceptions and

28:02

side effects and general recursion

28:05

we don't actually know this to be true

28:07

like we don't know that mapping over a

28:10

list twice and sort of doing inverses or

28:13

something that should or should

28:14

obviously work we don't know that it

28:17

does

28:18

but if we restrict ourselves to Total

28:21

pure functions we actually can do this

28:24

sort of reasoning

28:25

all right

28:26

so this is

28:28

an example of where a constraint at one

28:31

level affords us improved reasoning at a

28:33

higher level so uh

28:36

you don't want to pick on actors and

28:38

side effects too much you know I have a

28:39

I have sort of a larger a larger point

28:43

but but the the cost of side effects

28:45

which is just one of these examples

28:47

is that we lose uh the ability to reason

28:50

in this way we lose you know

28:53

compositionality we at least elude the

28:55

ability to reason uh modularly modularly

28:58

about concurrency and about

29:00

parametricity and other things like that

29:02

right so our programs can do strictly

29:04

more things if we have side effects but

29:06

we can conclude strictly fewer things

29:08

about them

29:10

all right so let's go now to another

29:12

example of this principle outside of

29:14

programming so enough about side effects

29:17

um I want to talk about where this comes

29:19

up in information security

29:22

so in information security there's this

29:24

principle called the principle of least

29:26

privilege which says states that a user

29:29

a program or component should have

29:32

exactly as much Authority as necessary

29:35

but no more

29:36

right so no why no more

29:39

well because uh if you minimize the

29:42

Privileges that a particular component

29:45

can have then you maximize uh the the

29:50

ability of composing that component with

29:52

other things right

29:54

because a composite system requires the

29:56

largest privileges of all of its compo

29:58

of its components it requires the max

30:00

right so if you have a composite

30:03

component you just find the one that

30:04

needs the most privileges you know the

30:07

sum of all the Privileges is what is

30:09

required by the composite

30:11

but the fewer the Privileges a component

30:14

needs

30:15

the easier it is to deploy in a large

30:18

environment and then modularity is

30:20

maximized as well by minimizing

30:22

privileges because uh we know that the

30:25

interaction of that component is limited

30:27

and we know we can reason sort of

30:29

mechanically about where that

30:31

interaction actually happens and so we

30:33

have overall sort of better

30:36

guarantees about system stability and uh

30:39

and it's also easier to test our limited

30:41

components in isolation

30:44

all right so another example of where

30:45

the more A system can do the lesser we

30:48

can predict what it will do yeah

30:52

and this kind of relationship and it

30:56

comes up in all kinds of things for

30:59

example you know with applicatives and

31:01

monads with side effects versus

31:05

algebraic reasoning uh and in

31:08

context-free versus regular grammars for

31:10

example if we have two regular grammars

31:12

we can we can

31:15

mechanically check whether they are

31:17

equal we can check whether they describe

31:19

the same set of strings but if we go to

31:22

context-free grammars which are strictly

31:24

more powerful we cannot conclude this

31:26

right there's no known algorithm that

31:29

will tell us whether to concrete context

31:32

suite grammars are in general

31:34

the same

31:36

euclidean versus Cartesian geometry

31:38

right so if you start you know if you

31:40

start with euclidean geometry like all

31:42

space is isotropic right all the

31:45

directions are the same space is the

31:47

same everywhere

31:48

all the dimensions are compatible all

31:51

curves if they look like they intersect

31:52

they actually do

31:54

but if we constrain ourselves to

31:56

coordinates if we constrain ourselves

31:58

you know to the sort of Cartesian plane

32:01

um

32:02

then uh you know we gain algebraic

32:06

reasoning we gain the ability to reason

32:07

about geometry algebraically and we can

32:10

reach for things like you know linear

32:13

algebra and category Theory and other

32:15

things to inform our our reasoning so

32:17

another an example of where a constraint

32:19

creates a freedom or power

32:22

and you know things like roadways uh you

32:25

know the fact that you can't just like

32:27

derive uh you know or the fact that you

32:30

choose not to drive you know from your

32:32

hotel directly to the conference venue

32:34

just like over the hills right it and

32:37

you drive on the road it gets you to the

32:39

conference more safely and more quickly

32:42

and and uh and it's compositional

32:45

because you know you can get a lot more

32:47

people you can get a whole bunch of

32:48

people uh very safely along the road

32:51

uh and very quickly and you know things

32:54

like commodity components like

32:56

if you have uh you're building some kind

32:58

of service and you want to be able to

33:00

reason about you know you want to buy

33:02

like a Linux server that it has like

33:04

these specific sets of components and so

33:06

they're all interchangeable the system

33:09

is compositional it's like you know

33:11

uh going to your wardrobe and you only

33:13

have one set of clothes like you know

33:15

lots of lots of instances of the same

33:17

set of clothes you know you can the

33:19

reason compositionally about your

33:20

wardrobe

33:21

and you know you never have to worry

33:23

like when you get up in the morning like

33:24

oh what should I what should I wear

33:26

right you just like put something on and

33:28

you don't have to worry about it and you

33:30

know other things like you know like

33:31

putting together Legos uh they they fit

33:33

together in a very

33:35

obvious way uh rather than like building

33:37

something out of like actual concrete

33:39

and steel uh and you know

33:41

Tony Hawk pro skater versus actual

33:43

skateboarding and things like that like

33:46

obviously Tony Hawk Pro Skater is easier

33:48

to do than actual skateboarding because

33:50

you're sort of on Rails right

33:53

so all of these things have in common

33:56

that a restriction at one semantic level

33:59

translates to freedom and power at

34:02

another semantic level

34:04

all right so if you make something

34:07

maximally general for the first hour

34:10

application you reach for the most power

34:12

that you can when you're building you

34:14

know the immediate thing then you

34:17

minimize your possibilities for

34:18

composition and for

34:20

reasoning about higher order

34:22

applications

34:25

and it's it's important to go not to go

34:28

too powerful too quickly because if you

34:31

constrain yourself to a small language

34:33

you can always embed that language in a

34:36

larger one later on right you can always

34:38

take your small language and then just

34:39

translate it to a larger one that fits

34:41

both what you were saying and also a

34:42

whole bunch of other stuff that you that

34:44

you might want to say in the future

34:46

but this does not work the other way

34:48

around right you cannot

34:50

uh you know take a a large abstraction

34:54

and sort of fit it into a smaller one

34:56

right uh when you when you do that you

34:58

get what people call a leaky abstraction

35:00

right you you have there's certainly

35:02

more stuff that you need to be able to

35:03

say than you can fit in the uh in the

35:06

language that you're that you're

35:07

actually using

35:09

right I mean it's like when you detonate

35:12

your your Dynamite right it's really

35:14

difficult to to then take the explosion

35:17

and sort of put it back in yeah

35:19

it might take a while

35:22

so another way of putting this is that

35:25

the more capable your syntax is

35:28

the fewer sort of logical semantics you

35:31

might be able to choose for that syntax

35:34

in the words of uh William lavier

35:38

syntax and semantics are a joint

35:41

right

35:43

um so what does that mean what what does

35:46

it mean for syntax and semantics to be a

35:48

joint well it means you know something

35:50

very similar to what what uh we've just

35:52

been talking about

35:54

so let's uh you know let's do a little

35:56

game where uh let's consider a category

36:00

C of all the concrete things in this

36:04

room uh or actually of con of groupings

36:07

of concrete things in this room right so

36:09

for example an element in the category C

36:12

might be like those two seats over there

36:15

or you know Dean and miles and one other

36:18

person or something right so

36:22

you know so so these are concrete

36:24

groupings of things this is the category

36:25

C

36:26

and

36:27

we want to say that there's a partial

36:29

order for this uh

36:32

for this category that is there's an

36:34

arrow from A to B

36:36

uh when B is a subset of a

36:39

right so so when B is a smaller or it's

36:43

contained in the grouping a you know for

36:46

example you know

36:48

those two seats over there are contained

36:50

in all in the group of all the seeds

36:52

right

36:53

uh and then we have this other category

36:56

which is ways of describing those things

36:58

you know for example we might say uh

37:01

like seat or chair or something

37:05

um and that you know would be a way of

37:07

describing some objects in this room

37:11

uh or people right uh that might be

37:14

another way of describing some objects

37:16

in this room

37:18

yeah and so and then we have a partial

37:19

order for this category as well that is

37:21

we want to say that there's an arrow

37:23

from A to B from the concept or the the

37:26

description a to the description B

37:29

when B is a more specific description

37:31

than a for example uh you know if you

37:35

have something like

37:37

uh

37:38

thing or let's say furniture and then

37:42

that is strictly larger a less specific

37:45

thing than saying like you know

37:47

Auditorium seat right

37:50

okay

37:51

simple enough so now we have a functor

37:54

from C to D which is a mapping that

37:58

takes a concrete group of things in this

38:01

room

38:01

and then finds the most specific

38:04

description in our Arsenal that that

38:06

fits that group of concrete things right

38:09

so for example you know if we have those

38:12

two seats over there we might want to

38:15

uh map that to like the most specific

38:17

description with my which might be it

38:19

might be seat or we might have something

38:21

like a red seat right it depends on on

38:24

what our descriptions uh contain but the

38:27

important thing is that F will find the

38:29

most specific description that we have

38:31

available for that group of copied

38:33

things and then there's a functor that

38:35

goes the other way which given a

38:37

description

38:38

um

38:39

D or a description in D it will find the

38:43

most generous group or the most capable

38:46

group

38:48

of elements that all match that

38:49

description right so for example if I

38:51

say seat and then I apply G to that I

38:55

want to get like all the seats in here

38:57

yeah

39:00

so

39:01

uh you might say that g sort of sort of

39:04

or F it will create a generalization for

39:08

something and then G will forget that

39:11

generalization and go to the sort of the

39:13

canonical example of that generalization

39:17

so this obeys this kind of relationship

39:20

where

39:21

if we have some grouping a

39:25

some group of concrete things a and then

39:28

we find the best available description

39:31

of that grouping so that would be F of a

39:34

and then we want to go back

39:37

to concretes we will end up as something

39:39

that may be larger that is the best

39:42

available description of some concrete

39:45

group May apply to a larger concrete

39:48

thing right so for example those two

39:50

seats over there I might you know go to

39:53

seat but then when I go back from seat

39:56

to the room I will get all the seats

39:59

right

40:00

so that's the the lower one uh and and

40:03

the upper one goes the other way where

40:06

if I get the most generous group

40:08

described by some concept B

40:11

that group May in fact be described

40:14

by a more specific there may be a better

40:17

fit for describing that right

40:20

that larger

40:23

I mean that concrete grouping okay

40:26

so

40:27

there's this sort of relationship that

40:28

goes in both ways so the important thing

40:31

here is that as we go from

40:33

smaller concrete groupings to larger

40:37

ones we get strictly more objects right

40:40

that is the the set is more capable

40:43

yeah so

40:45

but the number of possible

40:46

characterizations of that set gets

40:50

smaller

40:51

that is uh we can sort of reason less

40:53

about that set

40:56

uh where the the most specific

40:59

characterization will actually get less

41:01

specific as we grow the set of concrete

41:04

things that we want to describe right so

41:07

for example

41:08

you know like a a group of two seats uh

41:11

that might be best to characterized as

41:13

seat right but if we go from a group of

41:16

two seats two you know two seats and and

41:19

you know miles

41:21

um you know though you know that group

41:23

is more capable right like two seats and

41:26

miles are more capable than just two

41:28

seats

41:29

you know we would like to think anyway

41:32

so we can do strictly more things with

41:34

those right but they're harder to

41:37

summarize right what because what do

41:39

miles and two seats have in common right

41:41

that might be it might be difficult to

41:43

come up with sort of a very precise

41:46

uh generalization of that

41:49

right

41:52

uh and also if we find you know some

41:55

generalization and then we go back to

41:57

the room we may not find miles in two

41:59

seats again we might find a group that

42:01

is much much larger than that because

42:02

that concept will apply to a number of

42:04

number of other things yeah so this kind

42:07

of thing this kind of a relationship is

42:10

uh is called the galwa connection

42:13

uh after uh Everest gawa a French

42:16

mathematician

42:18

and there's a specific kind of a

42:20

junction

42:21

we were talking about how syntax and

42:23

semantics are a joint

42:25

so you can see that these two categories

42:28

c and d right where we have we have Maps

42:30

between them they're sort of mirror

42:32

images of each other in a sense

42:34

so there's a one-to-one correspondence

42:36

between two partial orders uh in a very

42:40

precise sense

42:42

uh and uh the the sort of relationship

42:46

that we can uh what what the

42:49

relationship that we were describing

42:50

previously what it sort of means is that

42:53

the most efficient description of any

42:56

concrete group a

42:59

uh will be less specific than any

43:02

description B precisely when

43:05

uh this is that green thing so precisely

43:08

when

43:09

a is smaller than the largest group

43:11

largest grouping described by B right

43:18

so uh we can go so instead of instead of

43:22

considering just functors between

43:23

categories of you know sets of objects

43:25

and and descriptions we can generalize

43:28

this to any category and since we can

43:30

generalize to any category that's just

43:32

pick the Scala category right so we can

43:34

say the same kind of thing about

43:37

at Junctions in Scala right so f and g

43:40

are then just ordinary Scala functors

43:42

and then there's a one to one

43:44

correspondence between these arrows

43:47

right if that is

43:49

you can if you could go from F of a to B

43:52

then you can go from a to G of B and

43:55

vice versa right

43:58

so you know that's all very sort of

44:00

abstract but let's uh

44:03

let's say that in Scala code so we can

44:06

write a trait like this a junction

44:08

uh where we can give an example of an

44:11

adjunction between two Scala functors so

44:14

f and g are functors

44:15

and then we can say that well we have to

44:17

implement two methods one of them takes

44:19

arrows from F of a to B and gives you an

44:23

arrow from a to G of B and the other one

44:25

goes the other way that is it takes

44:26

hours from a to G of B and it gives you

44:28

an F of

44:29

F of a m

44:31

and the sort of canonical example of

44:34

this in Scala

44:35

is the adjunction between uh sort of the

44:39

Tuple functor and the and

44:41

arrow and the reader functor

44:44

right so

44:46

uh well between producers and consumers

44:49

you could say

44:50

so here

44:52

uh you can see that this Junction is

44:54

witnessed by currying and uncurrying

44:56

right so F in the in the sort of in the

45:01

abstract is now in the concrete a pair

45:03

with r right and G is uh now in the

45:08

concrete a function that receives R

45:10

right

45:12

so then we can we can show that this

45:15

relationship holds because we if we have

45:17

an arrow from A and R to B then we can

45:20

construct an arrow from a to R to B

45:24

right and that is just Curry

45:26

and then if we can go the other way and

45:28

that is just uncurring

45:31

so what I want to draw your attention to

45:33

is that these two types pair with r and

45:36

function with r they sort of fit

45:38

together right you can feed one from the

45:40

other

45:42

and as the producer the pair becomes

45:44

more capable the corresponding consumer

45:47

has to become more capable as well

45:50

yeah

45:51

so a freedom in the producer becomes a

45:55

constraint on the consumer

45:57

and if we make the producer less capable

46:00

it becomes compatible with strictly more

46:03

consumers that is a constraint becomes a

46:07

freedom

46:08

and this is important because most

46:10

software that we write

46:12

are consumers most of the software that

46:14

we well at least I hope that most of the

46:15

software that we write is going to be

46:18

integrating some systems it's going to

46:20

be abstracting over something that has

46:21

been you know some kind of concrete

46:23

stuff or or something right

46:25

so most software that we write is going

46:28

to consume some things so most of the

46:30

software we write we have not yet

46:31

written and we're going to consume the

46:33

software we're writing today in that

46:35

software

46:36

so it's very important I think to

46:38

constrain for the first order

46:39

application to allow ourselves a freedom

46:42

and power at the higher order

46:46

um and so yeah we could tie this into

46:47

into variance right so the Tuple

46:50

uh

46:51

type has R in the covariant position but

46:54

then it's uh in the contravariant

46:57

position in the function type right so

46:59

as R grows in in one uh it contracts in

47:03

the other

47:05

okay

47:06

so the the other sort of canonical uh

47:10

a junction we can talk about is the

47:12

junction between free and forgetful

47:14

functors so free as in free monads and

47:18

free monoids so a list is a is a free

47:21

monoid

47:23

and that a junction if we take lists for

47:25

for example that a junction is witnessed

47:28

by the fact uh

47:30

well so the left side of the adjunction

47:32

the free side is witnessed by the

47:34

constructors of the list data type and

47:36

the other side is witnessed by fold left

47:38

and fold right

47:40

so it's saying that there is uh an

47:43

adjunction really between data types and

47:46

then let's be doing the constructors of

47:48

the data type and then the algebras that

47:50

we can use to deconstruct those data

47:52

types right so the producers of the

47:54

thing of the data and the consumers of

47:56

the data yeah

47:59

so I want to summarize here and say

48:03

that

48:05

when we you know when we're writing

48:07

software we want to reach for the least

48:09

powerful and most precise abstraction

48:11

that we can the thing that will allow us

48:13

to say exactly what we mean and no more

48:17

and then you know we want to want to

48:19

sort of build that and then we want to

48:21

detonate that as late as possible and

48:23

turn it into a sort of more powerful

48:25

larger language and sort of Let It

48:27

Escape right at the last possible moment

48:30

because once it's out we can't put it

48:32

back in right we're now in a larger

48:34

language

48:35

and I want to say that this this kind of

48:37

premature loss of precision or loss of

48:40

uh loss of abstraction

48:43

is uh you know is the root of all evil

48:45

in the in the Donald knuth sense that it

48:48

it creates a lot of problems for us down

48:50

the line that we then have to go uh and

48:53

fix like it's a creator of technical

48:55

debt

48:58

so yeah a constraint at one level leads

49:01

to freedom and power at another level

49:03

and for that reason we want to plan

49:05

ahead when we are when we're building

49:08

something we want to up

49:10

you know know that we are going to want

49:12

to integrate this into a larger system

49:13

so plan for that plan for algebraic

49:16

properties and compositionality and

49:18

don't always just reach for the for the

49:21

most uh expressive thing that you could

49:24

the thing that will do everything that

49:25

you might possibly need because then you

49:27

know you might paint yourself into a

49:28

into a corner

49:31

uh and that's all I have thank you

49:34

[Applause]

49:43

now do I have time for questions

49:47

one okay

49:50

okay

49:53

what is the question

49:56

uh can you just answer that because we

49:57

have

49:58

oh

50:00

we we I we may have to abandon this

50:03

already because

50:04

so people set the Wi-Fi

50:09

okay has anyone anyone's found that

50:12

okay

50:13

so uh in future shout out questions if

50:16

if speakers can repeat the question

50:18

that's good for the video okay uh only

50:21

one question Daniel do you have a

50:22

question oh yes you do okay

50:26

I'm curious if we

50:38

bought if there's any

50:40

oh the question is whether I thought of

50:42

whether there's a correlation between

50:44

this and scholomization that is the

50:46

relationship between existential and

50:49

Universal quantifiers uh the answer is I

50:51

haven't thought of that so lots of other

50:53

people have thought of that and there

50:54

definitely is an adjunction between

50:56

existentials and universals so that is

50:59

exactly and it's it's in fact one of the

51:01

canonical examples of this kind of

51:03

relationship

51:06

thank you very much

51:10

[Applause]

51:12

[Music]

Interactive Summary

The video discusses the trade-off between the expressive power of a programming language and its analytical power, arguing that over-abstracting or over-specifying too early leads to a loss of compositionality and precision. The speaker advocates for using the least powerful, most abstract representation possible—such as algebraic structures and syntax trees—and delaying compilation or "detonation" to concrete, less constrained forms until the last possible step. He illustrates this with various examples, including the composition of monads versus applicatives, the use of actors versus futures, and the concept of Galois connections in mathematics, concluding that restrictions at one level of abstraction create freedom and better reasoning capabilities at higher levels.

Suggested questions

4 ready-made prompts