It is becoming increasingly important for applications to protect sensitive data. With current techniques, the programmer bears the burden of ensuring that the application’s behavior adheres to policies about where sensitive values may flow. Unfortunately, privacy policies are difficult to manage because their global nature requires coordinated reasoning and enforcement. To address this problem, we describe a programming model that makes the system responsible for ensuring adherence to privacy policies. The programming model has two components: 1) core programs describing functionality independent of privacy concerns and 2) declarative, decentralized policies controlling how sensitive values are disclosed. Each sensitive value encapsulates multiple views; policies describe which views are allowed based on the output context. The system is responsible for automatically ensuring that outputs are consistent with the policies. We have implemented this programming model in a new functional constraint language named Jeeves. In Jeeves, sensitive values are introduced as symbolic variables and policies correspond to constraints that are resolved at output channels. We have implemented Jeeves as a Scala library using an SMT solver as a model finder. In this talk I describe the Jeeves programming language and our experience using Jeeves to implement a conference management system.
DSPy a system for AI to Write Prompts and Do Fine Tuning
Jeeves Talk Spring 2012
1. Jeeves
A Language for
Enforcing Privacy
Jean Yang
with Kuat Yessenov
and Armando Solar-Lezama
2. My Biggest Fear
Spotify “Private
Session” tells
everyone what I
really listen to.
Jean Yang / Jeeves 2
3. More May Be at Stake…
I’m not home!
Jean Yang / Jeeves 3
4. Choose Your Adventure
Maintain
Verify the control;
applications. spend
Want
time and $.
applications to
adhere to
privacy settings. Introduce
Make it easier
abstraction;
to write the
spend less
applications.
time and $.
What’s hard?
Jean Yang / Jeeves 4
6. No Privacy Concerns
Whatever!
A
A
Secret getLocation
club Secret
club
Alice
def getLocation (user: User): Location = user.location
Jean Yang / Jeeves 6
7. Simple policy
Only my
friends can getLocation
see my
location. A
Owner Viewer
Secret
club A
Alice getLocation
Secret
club
Jean Yang / Jeeves 7
8. Finer-Grained Policies
Only
Only my
friends can
members Policy
know this
see my
exists.
interaction?
location. A
A
Secret getLocation
club
Diner
Alice Owner Viewer
Locations
Which policies Not a
member!
apply where?
Jean Yang / Jeeves 8
9. Jeeves Mission
Make it easier for the
programmer to preserve
confidentiality of user data.
Jean Yang / Jeeves 9
10. What’s Hard?
Data
Function Programmer check/filter
Functionality
and policy are
Scrubbed data
intertwined.
Function Programmer check/filter
Scrubbed data
Jean Yang / Jeeves 10
11. Our Solution
Separation of policies
Policy Data from functionality
Automatic
enforcement Function Programmer check/filter
Tagged data
Scrubbed data
Function Programmer check/filter
Scrubbed data
Jean Yang / Jeeves 11
12. Policy-Agnostic Programs
def getLocation (user: User) (viewer: User)
State of the Art
: Location = {
if (isFriends user viewer) {
if (canSee user.location viewer) {
user.location;
} else { scrub(user.location, “Work”); }
} else { undisclosedLocation; }
}
Jeeves
def getLocation (user: User): Location =
user.location
Jean Yang / Jeeves 12
13. Talk Outline
Jeeves How it Coding in
language works Jeeves
Jean Yang / Jeeves 13
14. Jeeves Language
1 Sensitive values
Policy Data 2 Policies
3 Automatic
contextual
Function
enforcement
Tagged data
Function
Scrubbed data
Jean Yang / Jeeves 14
15. Jeeves for Locations
|
Diner
Low confidentiality
Secret
club
High confidentiality
A A
Secret
Diner
club
Jean Yang / Jeeves 15
16. Using Jeeves
Sensitive Values
level a in { low, high }
val location: String = <“school” | “MIT”>a Level variable
Low component High component
Policies
policy a: context != alice low
Core Functionality
val msg: String = “Alice is at ” + location
Contextual Enforcement
print {alice} msg /* “Alice is at MIT” */
print {bob} msg /* “Alice is at school” */
Jean Yang / Jeeves 16
17. Like a Butler…
“[Jeeves] lets a programmer delegate
privacy responsibilities and concentrate on
the actual function of their code,
much like a party host might entrust their
butler with ensuring the needs of each
guest are met so they can spend more
time socialising.”
Jean Yang / Jeeves 17
18. Policy
Programmer is free to
focus on core functionality.
Jean Yang / Jeeves 18
19. Talk Outline
Jeeves How it Coding in
language works Jeeves
Jean Yang / Jeeves 19
20. How Jeeves Works
Constraints Symbolic values
Symbolic
evaluation
Function
Symbolic
expressions
Implicit
Function parameter
Solver
Concrete value
Jean Yang / Jeeves 20
21. Representing Sensitive
Values in Jeeves
Without Jeeves Jeeves
Name Location Name Location
Alice MIT Alice ?|MITa Policy
Bob Facebook Bob Facebook
Claire Google Claire ?|Googleb Policy
Jean Yang / Jeeves 21
22. Symbolic Evaluation for
Information Flow
Name Location Runtime Environment
Alice |a context != alice a = low
Bob Facebook … b = low
Claire |b
How many people Outputs computed from
are at Facebook?
sensitive values are symbolic
1 + ((x1 = Facebook) ? 1 : 0) & concretized under the policy
+ ((x2 = Facebook) ? 1 : 0) environment.
Jean Yang / Jeeves 22
23. Jeeves Non-Interference
Guarantee
Consider the sensitive value
L | H a Level variable
Low component High component
Given a fixed L, all executions where a must be
low produce equivalent outputs no matter the
value of H.
Jean Yang / Jeeves 23
26. Static Checks
Constraints Symbolic values
Symbolic
evaluation
Function
Symbolic values flow
only where expected. Symbolic
expressions
Evaluation does not Contexts are
introduce Function well-formed.
nontermination.
SMT solving
Concrete value Outputs are concrete.
Jean Yang / Jeeves 26
27. Stateful Policies
Only people policy a:
near me can
((distance
see my
A context.loc alice.loc) > radius )
location. low
Secret But Alice’s location is
club
changing…
Alice
Design Decision:
Delay policy evaluation until output.
Jean Yang / Jeeves 27
29. Circular Dependencies
Location unknown; policy a:
distance unknown; ((distance
a is low context.loc alice.loc) > radius )
low
| a
alice.loc =
Location is GPS coords;
Unknown GPS distance is 0;
location coordinates a is high
The policies permit
print {alice} alice.loc both solutions!
Jean Yang / Jeeves 29
30. Circular Dependencies
policy a:
((distance
context.loc alice.loc) > radius )
low
Design Decision:
| a
alice.loc =
Jeeves execution model
Unknown GPS chooses outcome that
location coordinates yields maximal
functionality.
print {alice} alice.loc Use default logic to
implement this…
Jean Yang / Jeeves 30
31. Jeeves System
Policies Data Well-formed values.
Jeeves Evaluation
runtime
Function produces well-
formed values.
Symbolic
expressions
Function
Output
Concrete value Guarantee: outputs shown
according to policies.
Jean Yang / Jeeves 31
32. FINALLY.. I CAN FOCUS ON
FUNCTIONALITY!
Jean Yang / Jeeves 32
33. Talk Outline
Jeeves How it Coding in
language works Jeeves
Jean Yang / Jeeves 33
34. Implementation
Overload operators to
create symbolic expressions.
Use an SMT
+
solver as a
SMT Solver model finder.
v 3
print
= Delay
policy Runtime evaluation of
Environment policies until
v 2 output.
Propagate policies.
Jean Yang / Jeeves 34
35. JeevesLib in One Slide
val aliceLoc = StringVal (“MIT")
val defaultLoc = StringVal (“school")}
val a = mkLevel () // Level variable
policy (a, !(CONTEXT === alice), LOW) // Policy
val location: Symbolic =
mkSensitive( a // Level variable
, aliceLoc // High-confidentiality value
, defaultLoc ) // Low-confidentiality value
print_concrete (alice, location) // “MIT”
print_concrete (bob, location) // “school”
Jean Yang / Jeeves 35
36. Conference
Management System
Reviewed
Papers Reviews Needs review
Accepted
Labels
The rest is just setting permissions…
Jean Yang / Jeeves 36
37. Conf. Management
Permissions
Program
Authors Reviewers committee
Submission Review Rebuttal Public
Jean Yang / Jeeves 37
38. JConf Backend
Paper Review
Title Policy Reviewer Policy
Context
Policy
Author Policy Content Policy
Viewer: User
Reviews Policy
User CStage: Stage
Tags Policy
… Role
Functionality
Core Program
•Search papers. Does not
need to ex. Submission
•Display papers.
•Add and remove tags. know about
•Assign and submit reviews. policies.
Jean Yang / Jeeves 38
39. Page as
rendered
for paper
SQL
reviewer
(Squeryl)
Authors not
visible to
reviewer
Page as Jeeves
rendered backend
for paper
author
Only author
Reviewer can edit
not visible to Scalatra
authors frontend
Jean Yang / Jeeves 39
40. Functionality vs. Policy
File Total LOC Policy LOC
ConfUser.scala 212 21
PaperRecord.scala 304 75
PaperReview.scala 116 32
ConfContext.scala 6 0
Jean Yang / Jeeves 40
41. Functionality vs. Policy
File Total LOC Policy LOC
ConfUser.scala 212 21
PaperRecord.scala 304 75
PaperReview.scala 116 32
ConfContext.scala 6 0
Backend + Squeryl 800 0
Jean Yang / Jeeves 41
42. Functionality vs. Policy
File Total LOC Policy LOC
ConfUser.scala 212 21
PaperRecord.scala 304 75
PaperReview.scala 116 32
ConfContext.scala 6 0
Backend + Squeryl 800 0
Frontend (Scalatra) 629 0
Frontend (SSP) 798 0
Jean Yang / Jeeves 42
43. Functionality vs. Policy
File Total LOC Policy LOC
ConfUser.scala 212 21
PaperRecord.scala 304 75
PaperReview.scala 116 32
ConfContext.scala 6 0
Backend + Squeryl 800 0
Frontend (Scalatra) 629 0
Frontend (SSP) 798 0
Total 2865 < 5% 128
Jean Yang / Jeeves 43
44. Future Directions
Language
l
Runtime
Integrity/robustness;
policy expression.
Compiler
Scaling: reduce role of solver;
optimize runtime for
anticipated use cases.
Push more work to compiler.
Jean Yang / Jeeves 44
45. Jeeves Team
Jean Yang Armando Kuat Yessenov
Solar-Lezama
Patrick Long,
Jesse Klimov
MIT PRIMES
Program
Jean Yang / Jeeves 45
46. Conclusions
The Jeeves language: Language and runtime to Early experience
separating automatically enforce using Jeeves:
functionality from privacy policies. conference
privacy policies. Implementation as management system.
embedded DSL in Scala.
Website: sites.google.com/site/jeevesprogramming
Google Code: code.google.com/p/scalasmt
Contact: jeanyang@mit.edu
Jean Yang / Jeeves 46
48. Language Restrictions
Constraints Symbolic values Primitives and objects.
No functions.
Symbolic
Arithmetic and
Boolean constraints
evaluation
Function
with conditionals &
implications. Symbolic
expressions
No functions,
quantifiers, or Function
theory of lists.
SMT solving
Concrete value
Jean Yang / Jeeves 48
49. Standard
Non-Interference
H2 Hn
L H1 Hn-1
a = low
Program Does not depend
on the H-value
Does not
depend on the
Output H-value
Jean Yang / Jeeves 49
50. Jeeves
Non-Interference
H2
H1 Hn
L Hn-1
a = low
Program Depends on the
H-value
Cannot distinguish
between H-values that
Output imply a = low
Jean Yang / Jeeves 50
51. Jeeves
Non-Interference
L H
a = low
Program
Program does not leak
information about H.
Output
Jean Yang / Jeeves 51
Editor's Notes
Name is Jean Yang.Going to talk about joint work with KuatYessenov and Armando Solar-Lezama at MIT about a language for automatically enforcing privacy policies.TRANSITION: To illustrate why privacy matters, let me tell you about my most active fear.
I would prefer that the world remain oblivious to the depth of my love for Nicki Minaj.This leaves me at the mercy of the Spotify programmers.TRANSITION: More than just our reputation may be at stake.
Sites like PleaseRobMe show that you may endanger your possessions and life by oversharing.TRANSITION: We want to make sure these applications are adhering to the privacy policies.
Verification is at odds with the way people are writing these web programs.Value rapid prototyping; fast turn-around. This is difficult to achieve if you are doing full verification.
In a social network with users, locations, and friendship relationships between users, what the getLocation function returns Let us look at what the
In the absence of privacy concerns, the implementation of getLocation is quite straightforward.If Alice does not care who sees her location, then getLocation can tell everyone that she is at the secret club.TRANSITION: A basic policy most systems allow their users is the ability to specify who can see their location and who can’t.
If Alice can have a policy that says “Only my friends can see my location,” the getLocation function now needs to know about the preferences of the location’s “owner” and also who is viewing the location.Someone who is not Alice’s friend should not be able to see her location.While someone who is her friend should be able to see.TRANSITION: There are often other sources of policies in a social network as well.
Let’s say locations can now specify who can see their labels. Maybe the secret club wants only members to know it exists.The getLocation function now needs to know about not just policy of the location’s owner, but also of the location itself.The programmer must now reason not just about which policies must be enforced but also how they manifest, especially given policy interactions at different program points.For instance, the jester, who is not a member, may only be able to see an anonymized version of the secret club.TRANSITION: This reasoning can be quite difficult to get right.
Our goal is to help the programmer preserve confidentiality of user data.It is not that the programmer is malicious and wants to leak data, but even well-intentioned programmers find this difficult.Do this by pushing more responsibility for enforcing policies to the language runtime.TRANSITION: To motivate our solution, let us look at what is hard and how this can be automated.
We want functions that filter data based on the policies attached. For functions in a program to produce filtered data, the programmer is responsible for placing checks and filters across the program.Writing and maintaining the code can become quite complex because the functionality and policies are intertwined.TRANSITION: Our solution involves separating policies from functionality and automating enforcement across the program.
We have created a language, Jeeves, that allows the programmer to designate values as sensitive and provide declarative policies controlling how they are displayed.These policies are separate from the core program.The runtime system automatically enforces the policies by tracking the flow of sensitive values through the system.The runtime is responsible for producing filtered outputs consistent with the policies.TRANSITION: We have implemented this model in the Jeeves programming language.
Jeeves supports policy-agnostic implementation of core program functionality.In state-of-the-art programming languages, programmers must reason about policy and functionality together.For instance, a function that gets the location of a user needs to be concerned with the output channel, the different policies, and the different supported views of the locations.In Jeeves, the goal is to shield the programmer from having to worry about privacy when implementing core functionality.In Jeeves, the programmer can implement the getLocation function as if there were no policies.** QUESTIONS? **TRANSITION: In this talk, I will describe how we make this possible.
I will first describe Jeeves at a high level and what it looks like to have this separation between functionality and policy.Then I will describe how the Jeeves runtime works and the privacy guarantee that Jeeves provides.I will also describe our implementation of Jeeves as an embedded domain-specific library in Scala.I will then describe our early experience using Jeeves to build prototype systems.Specifically, we have used Jeeves to build a conference management system end-to-end.TRANSITION: Let us now look at the key elements of the Jeeves language.
[IMPORTANT TO REMEMBER THE INTRO TO THIS SLIDE!]The main ideas in Jeeves are sensitive values, policies, and automatic contextual enforcement.Sensitive values represent data for which different versions should be shown depending on who is seeing it.Policies describe which version of the values should be shown in different output contexts.Jeeves allows the programmer to implement the core program functionality in a manner agnostic to the policies.** That is, the program may need to make accommodations for the fact that sensitive values may be flowing through, but it does not need to know what the specific policies are.The Jeeves runtime automatically enforces the policies by keeping track of where the sensitive values.And the runtime is responsible for producing the appropriate output given the policies and the context under which the value should be shown.TRANSITION: Jeeves policies allow a sensitive value flowing through the *same program* to be shown differently depending on who is seeing the result.
Let’s go back to the location example to see how this works.In Jeeves, a sensitive value is a pair of a low-confidentiality and high-confidentiality value.The version of the Secret Club that is shown to non-members is considered the low-confidentiality version, while the fully disclosed identity of the location is the high-confidentiality version.This sensitive value can flow through a program that is agnostic to which version of the value should be shown to whom.The runtime will use the policies to determine what the jester can see versus what the guard can see.For instance, that the Jester sees the anonymized location while the guard can see the full location.TRANSITION: Let us now look more specifically at Jeeves syntax.
In Jeeves, the programmer associates sensitive values with level variables and introduces declarative policies describing the behavior of the level variables.To create a sensitive value, the programmer provides the low-confidentiality component, the high-confidentiality component, and a level variable that determines which component should be displayed.Level variables are special variables that can take on the values low and high.The programmer introduces policies that determine what the values of the level variables should be.This policy on level variable a that says that if the context viewer is not the user alice then the level is low.Here, context is a special variable referring to a parameter passed in at the output channel.The Jeeves runtime collects these policies to determine which views of sensitive values should be shown.The Jeeves programmer can use sensitive values alongside normal program values in the core program.For instance, here we create a string “msg” that is the concatenation of the sensitive location with the string “Alice is at.”The Jeeves runtime is responsible for tracking the policies associated with sensitive values to produce the appropriate outputs.Effectful computations such as print require a context to determine what the output should be.Printing in the context of user alice should yield the string “Alice is at MIT,” while printing on the context of user bob should yield the string “Alice is at school.”TRANSITION: The Jeeves programmer can rely on the runtime to ensure that outputs are produced according to the policies.
The Jeeves runtime tracks the policies in order to produce outputs that adhere to them.This allows the programmer to focus on the core functionality without having to reason about policies and policy interactions at the same time.** QUESTIONS??TRANSITION: Now let us look at how the Jeeves runtime automatically produces outputs adhering to the policies.
TRANSITION: The Jeeves runtime uses symbolic evaluation to track the flow of sensitive values and produce appropriate outputs.
Sensitive values are symbolic values.A symbolic value is a placeholder that can participate in operations as any other program expression but whose value is determined later.Jeeves policies correspond to logical constraints over program values.To track the flow of sensitive values, the Jeeves runtime symbolically evaluates programs.The context is an implicit parameter corresponding to the output channel.Given a possibly symbolic expression and a context value, the runtime produces concrete values through constraint solving.TRANSITION: The runtime guarantees that as long as the programmer correctly attaches policies to sensitive values, all outputs are shown according to the policies.
Say we have a list of people and their locations.The way to do this without Jeeves is to store this list concretely.The programmer is responsible for making sure that each person’s privacy preferences are taken into account when the program produces outputs.With Jeeves, the programmer can create sensitive values for data and explicitly attach the policies to the data. TRANSITION: The runtime keeps track of the data-policy associations for the rest of the program and makes sure that all derived values adhere to the policies.
First of all, the runtime environment keeps track of all of the policies.To evaluate a query such as “How many people are at Facebook?” the runtime produces a symbolic expression that depends on the symbolic values of Alice’s and Bob’s locations, which depend on the policies describing who can see their locations.The runtime uses the constraint environment to produce an concrete output.Because all outputs computed from sensitive values are symbolic and all symbolic values are concretized under the policy environment, we have the guarantee that all outputs adhere to the privacy policies.TRANSITION: We have a more formal statement of this guarantee that is a variation on the traditional non-interference guarantee in computer security.
We have the following non-interference guarantee for Jeeves.Given a fixed value for the low component, all executions where the level variable must be low produce equivalent outputs no matter what the value of H is.** QUESTIONS? **TRANSITION: This guarantee is nice and all, but it only applies to programs that produce outputs.
After we started programming with the naïve Jeeves model, our experience informed various design decisions about how and when constraints should be resolved.
Besides having rules checking that contexts are well-formed and outputs are concrete, we also keep track of what values may be symbolic to ensure symbolic values only flow where expected. For instance, the rules check that conditionals cannot produce symbolic functions.We also have rules that ensure that symbolic evaluation of recursive functions does not introduce nontermination. We do this by restricting applications of recursive functions under potentially symbolic conditionals.TRANSITION: In addition, the design of our execution strategy addresses some more subtle issues that arise when one uses declarative policies to control output of sensitive values.
An example of an issue that arose is that of stateful policies.…TRANSITION: A more subtle issue arises when there are circular dependencies between policies and program values.
Make point that dealt with this in principled way.Maximal functionality in all cases.TRANSITION: Put this back together…
** QUESTIONS?TRANSITION: This relieves programmer burden of having to reason about policy and functionality together.
This allows the programmer to focus on program functionality, resting assured that the Jeeves system is correctly enforcing the policies.TRANSITION: This all sounds nice, but does it really work?
TRANSITION:
We have implemented Jeeves as an embedded domain-specific language in Scala.Scala’s overloading support made it quite nice to embed Jeeves.
TRANSITION: We have used the Jeeves implementation to build an end-to-end conference management system. Besides being the one system reviewers are sure to understand, conference management systems have become the standard benchmark for computer security papers because it is so tricky to get the permissions right.
Their core functionality, however, simply consists of storing papers, reviews, and labels such as “reviewed,” “needs review,” and “accepted.”TRANSITION: The rest is just setting permisisons.
TRANSITION: We demonstrate with our Jeeves conference management system that Jeeves supports a separation of policy from functionality.
Policies are associated with fields of papers, reviews, and users.TRANSITION: We have integrated this backend with an end-to-end system where we use Jeeves policies for both confidentiality and integrity.
Here we show the same paper as rendered for the paper reviewer and paper author.The paper details information includes title, authors, file, tags, and reviews.There is also optionally a link to edit the paper.According to the permissions…The authors are not visible to the reviewer,The reviewer is not visible to the authors,And only the author can edit the paper.What I’m showing here is the rendering of our end-to-end conference management system which interacts with SQL to store the policies persistently through the Squeryl interface.We define marshalling functions that attach Jeeves policies to objects upon retrieval from the database.We use Jeeves to show the appropriate value in the frontend.We use the ScalatraScala web framework to call functions that concretize values in the context of the current session user and conference stage.TRANSITION: We demonstrate that Jeeves supports a separation between policy and functionality. Additionally, only a fraction of code base is dedicated to specifying and enforcing policies.
In terms of the Jeeves language, we would like to formalize the guarantees Jeeves provides for integrity and robustness.Integrity policies describe who can modify values or cause certain effects.Robustness means that only those who have access can cause high-confidentiality values to be shown.It would also be interesting to examine our policy language and how to make that more usable.Right now Jeeves policy enforcement occurs almost entirely at run time.To make the approach more scalable it will be useful to look at what we can push to compile time.This future direction relates to the direction of scaling the runtime: reducing the role of the solver and optimizing for the use cases we anticipate.TRANSITION: I am also curious to talk to all of you to help inform the problems we tackle next.
Benefits of using Jeeves:Develop functionality separately from privacy policies.When testing policies, focus on sensitive data.System automatically handles policy interactions.It is exciting that language and solver technologies are at a point where we can even consider this sort of separation of global concerns from core functionality.I am excited about a future in which we can continue to make programmers’ lives easier by teasing out concerns such as privacy and automating how we handle them.
We have designed the Jeeves language so that evaluation produces expressions and constraints we can expect to handle.The programmer can make sensitive values from primitives and objects but not functions.The constraint language is restricted to arithmetic and Boolean constraints with conditions and implications.No functions, no quantifiers, no theory of lists.TRANSITION: We have static type rules that ensure execution does not create values that cannot be handled.
Consider some fixed value for the low component L.If the level variable a does not depend on the value of the high component H, then we have the standard non-interference guarantee that the output cannot depend on the value of H.TRANSITION: Now we have the case where the level variable a *can* depend on the high values. We will see an example of how this can happen in a few slides.
There are two situations if the value of level variable a can depend on the value of the high component.There is a set of high values that imply a is high and a set of high values that imply a must be low.We have the guarantee that if a must be low, then it is not possible to distinguish which of the high values we have.That is, if we have two high values for which a must be low, output will be equivalent for them.Please see our paper the full statement of this theorem and the restrictions of how it implies.For instance, our formalism is concerned only with independent outputs.A more important point is that this theorem holds only if we can evaluate programs to produce outputs.TRANSITION: Forunately, we have designed our language, type system, and evaluation strategy to help programmers write programs that can produce outputs.
There are two situations if the value of level variable a can depend on the value of the high component.There is a set of high values that imply a is high and a set of high values that imply a must be low.We have the guarantee that if a must be low, then it is not possible to distinguish which of the high values we have.That is, if we have two high values for which a must be low, output will be equivalent for them.Please see our paper the full statement of this theorem and the restrictions of how it implies.For instance, our formalism is concerned only with independent outputs.A more important point is that this theorem holds only if we can evaluate programs to produce outputs.TRANSITION: Forunately, we have designed our language, type system, and evaluation strategy to help programmers write programs that can produce outputs.