Skip to content

Commit fb23d75

Browse files
committed
Flesh out the README
1 parent 7420794 commit fb23d75

File tree

1 file changed

+307
-17
lines changed

1 file changed

+307
-17
lines changed

README.md

Lines changed: 307 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,323 @@
22

33
An exploratory programming language.
44

5-
## Quick start
5+
## Quick Start
66

77
```sh
8-
git clone [email protected]:mkantor/please-prototype.git
9-
cd please-prototype
8+
git clone [email protected]:mkantor/please-lang-prototype.git
9+
cd please-lang-prototype
1010
npm install
1111
npm run build
1212
echo '{@runtime context => :context.program.start_time}' | ./please --output-format=json
1313
```
1414

15-
## What this repository is
15+
## What This Repository Is
1616

17-
**This implementation of Please is a proof of concept**. There are bugs and missing pieces, and
18-
language syntax/semantics may change backwards-incompatibly on the way to an official release.
19-
TypeScript was chosen because it's pretty good for rapid prototyping, but it's likely that another
20-
language would be used in a non-prototype implementation.
17+
**This implementation of Please is a proof of concept**. There are bugs and
18+
missing pieces, and language syntax/semantics may change backwards-incompatibly
19+
on the way to an official release. TypeScript was chosen because it's pretty
20+
good for rapid prototyping, but different languages may be used in non-prototype
21+
implementations.
2122

22-
## Current state
23+
### Current State
2324

24-
Enough pieces exist to write basic runnable programs. There is a type system, but it's not wired up
25-
in many places yet so mistakes often go unnoticed at compile time. The standard library is anemic
26-
and documentation is nonexistent.
25+
Enough pieces exist to write basic runnable programs. There is a type system,
26+
but it's not wired up in many places yet so mistakes often go unnoticed at
27+
compile time. The standard library is anemic and documentation is lacking.
2728

28-
The current runtime is an interpreter, but the plan is to eventually add one or more backends to
29-
allow building native executables.
29+
The current runtime is an interpreter, but the plan is to eventually add one or
30+
more backends to allow building native executables.
3031

31-
## What's next?
32+
### What's Next?
3233

33-
I'm focused on squashing compiler bugs and establishing solid foundations to build atop. Along the
34-
way I've been slowly fleshing out the type system and standard library.
34+
I'm focused on squashing compiler bugs and establishing solid foundations to
35+
build atop. Along the way I've been slowly fleshing out the type system and
36+
standard library.
37+
38+
## Language Design
39+
40+
### Syntax
41+
42+
A Please program is composed of atoms, objects, lookups, and functions.
43+
44+
#### Atoms
45+
46+
Atoms are the raw textual portions of your source code. They're similar to
47+
strings from other programming languages, except there isn't a specific runtime
48+
data representation implied by the fact that a value is an atom (e.g. the atom
49+
`2` may be an integer in memory).
50+
51+
Bare words not containing any
52+
[reserved character sequences](./src/language/parsing/atom.ts#L54-L76) are
53+
atoms:
54+
55+
```
56+
Hello
57+
```
58+
59+
Atoms can be quoted:
60+
61+
```
62+
"Hello, World!"
63+
```
64+
65+
#### Objects
66+
67+
Objects are maps of key/value pairs ("properties"), where keys must be atoms:
68+
69+
```
70+
{ greeting: "Hello, World!" }
71+
```
72+
73+
Object properties without explicitly-written keys are automatically enumerated:
74+
75+
```
76+
{ Hello World } // is the same as { 0: Hello, 1: World }
77+
```
78+
79+
#### Lookups
80+
81+
Data can be referenced from other places in the program using lookups, like
82+
`:en` below:
83+
84+
```
85+
{
86+
en: "Hello, World!"
87+
zh: "世界您好!"
88+
hi: "हैलो वर्ल्ड!"
89+
es: "¡Hola, Mundo!"
90+
default: :en // runtime value is "Hello, World!"
91+
}
92+
```
93+
94+
You can drill down into the properties of looked-up values:
95+
96+
```
97+
{
98+
deeply: {
99+
nested: {
100+
greeting: "Hello, World!"
101+
}
102+
}
103+
greeting: :deeply.nested.greeting // or :{deeply nested greeting}
104+
}
105+
```
106+
107+
Lookups are lexically scoped:
108+
109+
```
110+
{
111+
greeting: "Hello, World!"
112+
scope: {
113+
greeting: "Hi, Moon!"
114+
a: :greeting
115+
}
116+
b: :greeting
117+
}
118+
```
119+
120+
Output:
121+
122+
```
123+
{
124+
greeting: "Hello, World!"
125+
scope: {
126+
greeting: "Hi, Moon!"
127+
a: "Hi, Moon!"
128+
}
129+
b: "Hello, World!"
130+
}
131+
```
132+
133+
#### Functions
134+
135+
Functions take exactly one parameter and their body is exactly one expression:
136+
137+
```
138+
a => "Hello, World!"
139+
```
140+
141+
Functions can be applied:
142+
143+
```
144+
(a => :a)("Hello, World!")
145+
```
146+
147+
#### Keywords
148+
149+
The functions and lookups shown above are syntax sugar for _keyword
150+
expressions_. Most of the interesting stuff that Please does involves evaluating
151+
keyword expressions.
152+
153+
Under the hood, keyword expressions are modeled as objects. For example, `:foo`
154+
desugars to `{@lookup query: foo}`. All such expressions have a key `0`
155+
referring to a value that is an `@`-prefixed atom (the keyword). Keywords
156+
include `@function`, `@lookup`, `@apply`, `@check`, and `@runtime`.
157+
158+
Currently only `@function`, `@lookup`, and `@apply` have syntax sugars.
159+
160+
### Semantics
161+
162+
Please is a functional programming language. Currently all functions are pure,
163+
with a sole exception: logging to stderr can happen from anywhere. The specific
164+
approach to modeling other runtime side effects is still to be decided.
165+
166+
Once desugared, a Please program is either an atom or an object. Please code is
167+
data in the same sense as in Lisp, though without a macro system there's not
168+
much you can do with this right now.
169+
170+
Before a Please program terminates, it prints the fully-resolved version of
171+
itself to standard output. That means Hello World can be as simple as this:
172+
173+
```
174+
"Hello, World!"
175+
```
176+
177+
`@runtime` expressions allow accessing runtime context (like command-line
178+
arguments). A `@runtime` expression is conceptually a bit like the `main`
179+
function from other programming languages, except there can be any number of
180+
`@runtime` expressions in a given program. Here's an example:
181+
182+
```
183+
{@runtime context => :context.program.start_time}
184+
```
185+
186+
Unsurprisingly, this program outputs the current time when run.
187+
188+
Code outside of `@runtime` expressions is evaluated at compile-time as much as
189+
possible. For example, this program compiles to the literal value `2` (no
190+
computation will occur at runtime):
191+
192+
```
193+
:integer.add(1)(1)
194+
```
195+
196+
There's currently no module system and all Please programs are single files, but
197+
that's only because this is a prototype.
198+
199+
### Layering
200+
201+
Please is a layered language. It can be thought of as a stack of three smaller
202+
languages:
203+
204+
- Layer 0 (`plz`) is the surface syntax. This is the language you as a human
205+
typically use to write programs.
206+
- Layer 1 (`plo`) is a desugared/normalized representation of the syntax tree.
207+
- Layer 2 (`plt`) is the result of applying semantic analysis, compile-time
208+
evaluation, and other reductions to the `plo` tree. The prototype
209+
implementation of the language runtime is a `plt` interpreter.
210+
211+
`plz` has a specific textual representation, but `plo` & `plt` could be encoded
212+
in any format in which hierarchial key/value pairs of strings are representable
213+
(currently only JSON is implemented, but YAML, TOML, HOCON, BSON, MessagePack,
214+
etc could be supported).
215+
216+
Take this example `plz` program:
217+
218+
```
219+
{
220+
language: Please
221+
message: :atom.prepend("Welcome to ")(:language)
222+
now: {@runtime context => :context.program.start_time}
223+
}
224+
```
225+
226+
It desugars to the following `plo` program:
227+
228+
```
229+
{
230+
language: Please
231+
message: {
232+
0: @apply
233+
function: {
234+
0: @apply
235+
function: {
236+
0: @lookup
237+
query: atom.prepend
238+
}
239+
argument: "Welcome to "
240+
}
241+
argument: {
242+
0: @lookup
243+
query: language
244+
}
245+
}
246+
now: {
247+
0: @runtime
248+
1: {
249+
0: @function
250+
parameter: context
251+
body: {
252+
0: @lookup
253+
query: context.program.start_time
254+
}
255+
}
256+
}
257+
}
258+
```
259+
260+
Which in turn compiles to the following `plt` program:
261+
262+
```
263+
{
264+
language: Please
265+
message: "Welcome to Please"
266+
now: {
267+
0: @runtime
268+
function: {
269+
0: @function
270+
parameter: context
271+
body: {
272+
0: @lookup
273+
query: context.program.start_time
274+
}
275+
}
276+
}
277+
}
278+
```
279+
280+
Which produces the following runtime output:
281+
282+
```
283+
{
284+
language: Please
285+
message: "Welcome to Please"
286+
now: "2025-01-27T16:06:55.802Z"
287+
}
288+
```
289+
290+
After an eventual stable release of Please, `plo` & `plt` will be versioned to
291+
ensure backwards compatibility.
292+
293+
Many compilers use intermediate representations (IRs) internally. Please's
294+
layers serve a similar purpose, though unlike some other IRs they are
295+
serializable, stable, and are designed to be human-readable (albeit verbose).
296+
297+
What is this good for? Use cases include:
298+
299+
- an eventual package manager which distributes lower-layer representations for
300+
efficiency
301+
- experimenting with alternative syntaxes while remaining compatible with the
302+
rest of the ecosystem
303+
- caching `plo` & `plt` artifacts to speed up recompiles
304+
- new optimizations can be applied to existing programs without compiling from
305+
scratch
306+
- using common serialization formats to encode `plo` & `plt` makes them easy to
307+
manipulate with a variety of tools (refactor your code with
308+
[`jq`](https://jqlang.github.io/jq/)!)
309+
310+
### Philosophy
311+
312+
The first-order goal of Please is to be pleasant to use.
313+
314+
It strives to:
315+
316+
- help you express your ideas clearly, concisely, and correctly
317+
- catch mistakes and oversights without being annoying or confusing
318+
- be useful across many different contexts & domains
319+
- be extensible to accommodate novel use cases
320+
- make it easy to pay off technical debt
321+
- emit programs that you have confidence in
322+
323+
The prototype implementation doesn't live up to these aspirations, but hopefully
324+
it approaches them over time.

0 commit comments

Comments
 (0)