updated readme with more examples

This commit is contained in:
Andrew Segavac
2019-11-13 00:05:04 -07:00
parent 09216aa45d
commit 3f0f8ff239

114
README.md
View File

@@ -1,17 +1,18 @@
# Boring Lang
# Boring Lang Proposal
**NOTE: This repo is a work in progress as I learn compiler writing, I may abandon this.**
The Boring Programming Language (Boring-Lang) is an attempt to create an easy, productive, general purpose programming language that makes as few interesting choices as possible while still being in line with modern concepts in programming languages.
The language:
The language (wish list):
* is compiled with a run-time (llvm for convenience + c compatibility)
* is garbage collected
* uses async-await for all IO, with a built-in multi-core scheduler
* supports algebraic data types (Result type for errors, Maybe type for nullables)
* supports algebraic data types (Result type for errors, Maybe/Optional type for nullables)
* supports parametric polymorphism
* uses struct+traits, rather than classes or stuct+interfaces
* has a rich standard library (http server, actor model)
* is immutable by default
It's basically a middle-ground of Rust, Golang, Swift, and Typescript.
@@ -27,18 +28,111 @@ struct ExampleResponse {
email: Str
}
async func handle(req http.Request, resp http.Response) {
async func handle(req: http.Request, resp: mut http.Response) {
log.info("request: ", req.body)
response_data := ExampleResponse{id: 4, name: "Steven", email: "swerbenjagermanjensen@example.com"}
resp.write(json.encode<ExampleResponse>(response_data))
resp.set_status(200)
let response_data := ExampleResponse{id: 4, name: "Steven", email: "swerbenjagermanjensen@example.com"}
await resp.set_status(200)
await resp.write(json.encode<ExampleResponse>(response_data))
}
async func main(args: Array<Str>) int {
router := http.Router("").add_route("/myroute", handle)
async func main(args: Array<Str>) Int32 {
let router := http.Router("").add_route("/myroute", handle)
http_server := http.Server("localhost", 8080, router)
err := await http_server.server_forever()
let err := await http_server.server_forever()
await log.info("error serving: ", err)
return 1
}
```
## Mutability
All variables are immutable by default, to make them mutable use the `mut` keyword. Once a variable becomes immutable it cannot become mutable again. If you need it to become mutable, either implement the `copy` trait, or simply create a new one with the same data.
```
let mut foo := Dict<String, Int32>{
'eggs': 12,
'bananas': 2,
}
// fine
foo.insert('grapes', 2)
let bar = foo // bar is not mutable
bar.insert('apples', 4) // fails with compiler error
let mut baz = bar.copy()
baz.insert('apples', 4) // fine
```
Methods on a struct must specify if they mutate the struct.
```
impl Dict<Key: Hashable, Value> {
func insert(self: mut Dict, key: Key, value: Value) {
// mutate self here
}
func get(self: Dict, key: Key) {
// no need for `mut`
}
}
```
## Context
Context is an exceptionally useful feature in golang, but a common complaint is that:
1. Because it works as an arbitrary map, it can be used to pass arguments into a function that aren't explicitly stated.
2. It is used for both passing context parameters and cancellation, two fundamentally different tasks that have no reason to be in the same object.
The boring standard library solves this by using parametric polymorphism. Context is by default an empty object passed through the chain, and each function/set of context parameters is an additional trait condition applied at compile time.
```
pub func tracing_middleware<Ctx: Tracing>(handler: func(http.Request, mut http.Response)){
return async func(ctx: Ctx, req: http.Request, resp: mut http.Response) {
with tracing.NewSpan(ctx, 'request_duration') {
await handler(ctx, req, resp)
}
}
}
pub func auth_middleware<Ctx: Auth>(handler: func(http.Request, mut http.Response), scope: Str){
return async func(ctx: Ctx, req: http.Request, resp: mut http.Response) {
if ctx.has_scope(scope) {
await handler(ctx, req, resp)
}
await resp.set_status(403)
await resp.write('missing scope')
}
}
pub func cancel_middleware<Ctx: Cancel>(handler: func(http.Request, mut http.Response)){
return async func(ctx: Ctx, req: http.Request, resp: mut http.Response) {
if !(await ctx.is_cancelled()) { // check cancel token
await handler(ctx, req, resp)
}
await resp.set_status(400)
await resp.write('cancelled')
}
}
```
for the above examples, you would pass a context type that implements all three traits.
## Monadic function modifiers
Boring uses function modifiers to implement functionality like `async/await` and `coroutines`. These function by rewriting the AST prior to compilation. The table below describes the modifiers currently available.
|Type|Change To Return Type|Introduces to Scope|
|---|---|---|
|async|`Promise<ReturnType>`|await|
|coroutine|`FirstReturnType,func(Next,Params)...`|yield|
|errors<ErrorType>|`Result<ReturnType,ErrorType>`|?|
## Import System
Similar to python, folders/files represent the `.` seperated import path though relative imports are *not* supported. Exported values must be marked with `pub`. All imports take the form:
```
import package.path as local_name
```