First dump of madness.

This commit is contained in:
Pat Garrity 2024-01-24 22:52:03 -06:00
commit c7aa5e19a9
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
26 changed files with 674 additions and 0 deletions

12
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,12 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: fix-byte-order-marker
- id: mixed-line-ending
args: ['--fix=lf']
description: Enforces using only 'LF' line endings.
- id: trailing-whitespace

13
README.md Normal file
View file

@ -0,0 +1,13 @@
# The Ava Programming Language
This repository is currently a research project. The project is to explore ideas
in programming language design by specifying _Ava_.
To start reading, please use the [Table of Contents](./table-of-contents.md)
## What is Ava?
Ava is a programming language research project that is in the design and
specification phase. There is no grammar, parser, compiler, or way to use any
Ava code. Ava is a way to get ideas out of my head, challenged, and further
explored.

30
constants.md Normal file
View file

@ -0,0 +1,30 @@
# Constants
_Constants_ are definitions which provide a name and explicit type for some
[value](values.md). The `const` keyword (definition type) is used to define a
constant.
```
const weak_pi: Float64 is 3.14
const my_name: String is "Pat"
```
In general:
```
const <name>: <type> is <value>
```
Since a constant is categorized as a [definition](general-syntax.md#definitions)
is uses the `is` keyword rather than the `:=` operator.
## Restrictions
Constants are subject to the following restrictions:
- Constants MUST refer to a literal value.
- Constants MUST have an explicit type.
- Constants MAY refer to a record instance.
- Constants CANNOT require a type constructor.
- Constants CANNOT refer to functions.
- Constants CANNOT refer to expressions.

29
definitions.md Normal file
View file

@ -0,0 +1,29 @@
# Definitions
_Definitions_ are Ava constructs that may live at the top level within some
namespace. [Functions](functions.md) in particular may _also_ live within type
class (and instance) definitions.
## Syntax
All definitions adhere to the following syntax:
```
[export] <definition type> <name> <description> is <body>
```
Each definition type ultimately controls the description and body. All
definition names adhere to standard [Name](names.md) rules.
## Supported Definition Types
TODO: This whole document... can go away? Need inventory of keywords.
- [`const`](constants.md)
- [`record`](records.md)
- [`type`](types.md)
- [`alias`](type-aliases.md)
- [`class`](type-classes.md)
- [`instance`](type-classes.md#instances)
- [`enum`](enumerations.md)
- [`fn`](functions.md)

8
effects.md Normal file
View file

@ -0,0 +1,8 @@
# Effects
## The Effect Type
The type `IO[E, A]` represents an effect which will produce `A` when executed,
and may fail with an error of type `E`. There exists a type
`type Task[A] = IO[Nothing, A]` that represents effects that cannot fail with an
error.

26
enumerations.md Normal file
View file

@ -0,0 +1,26 @@
# Enumerations
Enumerations are sum types.
## Example: The Option Type
```
enum Option[A] is
record Some[A] is (value: A)
object None
fn some[A]: (a: A) => Some[A] is a => Some(a)
```
## Example: The Either Type
TODO: Describe import behavior! Does importing Either import Left/Right?
```
enum Either[L, R] is
record Left[L] is { value: L }
record Right[R] is { value: R }
fn left[L]: (left: L) => Either[L, Nothing] is l => Left { l }
fn right[R]: (right: R) => Either[Nothing, R] is r => Right { r }
```

45
expressions.md Normal file
View file

@ -0,0 +1,45 @@
# Expressions
_Expressions_ are the basis of programming in Ava. An expression is some code
that will produce a _value_ when evaluated.
## Syntax
Most non-[Definition](definitions.md) code is considered an expression.
Expressions include:
- Literals
- Variables
- Function Calls
- Control Expressions
In general, using a Literal or a Variable for an expression wraps that value in
a `pure` function that returns the value when evaluated.
## Control Expressions
### If Then
The `if/then` expression expects an expression that returns a _Boolean Value_.
That expression is evaluated. If it evaluates to `true`, the `then` expression
is evaluated. Otherwise the `else` expression is evaluated.
```
if <boolean expr> then <expr> else <expr>
```
### Do Yield
TODO: This is incomplete
The `do/yield` expression allows for imperative composition of monadic
expressions.
```
do
x <- foo()
y <- bar()
_ <- baz()
yield
x + y
```

34
functions.md Normal file
View file

@ -0,0 +1,34 @@
# Functions
TODO: Scala syntax? Haskell syntax? Implications? How do we specify names for
parameters? Do we want to do that? How do function arguments, tuples, and
records all relate to one another? Are they all just the same? How do we talk
about curried functions? Is that supported? Is there some other mechanism?
TODO: This is all 100% up in the air right now.
```
fn example: () => Int32 is 42
example
example()
```
```
fn example: String => Int32 is str => length(str)
example "foo"
example("foo")
```
```
fn example: { x: Int32, y: Int32 } => Int32 is (x, y) => x + y
example 1 2
example(1, 2)
example { x: 1, y: 2 }
```
```
fn map[F[*], A, B]: (F[A]) => (A => B) => F[B] is
(fa) (f) => fa f
map(list)(x => x + 1)
```

58
general-syntax.md Normal file
View file

@ -0,0 +1,58 @@
# General Syntax
## Type Binding
Some name may be bound to some type by using the `:` (colon) syntax.
```
name: Type
```
Type bindings are used for all such occurrences. This includes:
- [Defining Variables](variables.md)
- [Defining Records](records.md)
- [Defining Functions](functions.md)
## Value Binding
Some name may be bound to some value by using the `:=` syntax.
```
name := value
name: Type := value
```
Value bindings are used for all such occurrences. This includes:
- [Defining Variables](variables.md)
- [Instantiating Records](records.md)
## Comments
Comments are lines where the first non-whitespace characters are `--`. This was
selected for ease of typing paired with low visual noise.
```
-- this is a comment
-- this is another comment
let x := "foo" -- this will not compile, comments cannot be mixed with code
```
Ava does not support multi-line comments.
## Code Documentation
Code documentation is written using comments that both directly-precede certain
definitions and have 3 `-` characters:
```
--- This function does some foo as well as some bar.
--- @param x Documentation for the `x` parameter.
fn foo_bar: (x: String) => Int32
```
- Code documentation respects Markdown.
- TODO: Link syntax.
- TODO: Supported definitions (example for each)
- TODO: Parameter documentation

4
holes.md Normal file
View file

@ -0,0 +1,4 @@
# Holes
`???` is a hole that resolves to any type, used for development, cannot be used
at runtime at all (it blows up).

1
memory-management.md Normal file
View file

@ -0,0 +1 @@
# Memory Management

23
names.md Normal file
View file

@ -0,0 +1,23 @@
# Names
_Names_ are user-defined strings that name defined items. All names (variable
names, function names, etc.) follow the same rules.
Names are UTF-8 strings. The following values are _excluded_:
- Any reserved keyword
- Any whitespace character
- `.`
- `:`
- `=`
- `"` or `'`
- `[` or `]`
- `{` or `}`
- `(` or `)`
## Convention
- Variables use `lower_snake_case`
- Functions use `lower_snake_case`
- Generic Types use `UpperCamelCase` but prefer single letters such as `A`.
- Data Types, aliases, etc. use `UpperCamelCase`

76
namespaces.md Normal file
View file

@ -0,0 +1,76 @@
# Namespaces
_Namespaces_ are the primary organizational unit of Ava. Every Ava file _must_
define a namespace. The namespace is ALWAYS the first line of the file.
## Syntax
Each namespace is an ordered, delimited, list of [names](names.md). Each name is
separated by the `.` character.
```
<name>[.<name>]*
```
- At least one name MUST be specified.
- CANNOT contain more than one `.` character in a row.
- CANNOT begin with `.` character.
- CANNOT end with `.` character.
- CANNOT use a [Reserved Name](#reserved-names).
### Examples
- `foo`
- `foo.bar.baz`
- `language.v0`
- `collections.v0`
## Reserved Names
Any namespaces provided with a particular Ava distribution are considered
_reserved_. This means that, for example, the following namespaces cannot be
used:
- `language`
- `collections`
## Language Namespace
The `language` namespace is automatically imported into every Ava file.
## Exports
Each namespace MAY _export_ defintions using the `export` keyword. If some
definition is exported, it may be [imported](#imports) into another namespace.
Definitions are NOT exported by default -- it is an opt-in process. As noted in
the [Definitions](definitions.md) documentation, the `export` keyword may be
used at the beginning of _any_ definition.
## Imports
Imports immediately follow the namespace definition in each file. Each file may
have zero or more imports. Imports bring definitions from another namespace into
scope.
### Syntax
Each import is a single, fully-qualified name. Imported names MAY be mapped to
some alternative name. An import may refer to a specific namespace or it may
refer to some specific definition within a namespace.
TODO: Account for type classes. How can we easily import instances? One option
is to NOT do this. Ever. Just resolve EVERY possible `instance` during
compilation and make them available within the scope of the program. Global.
Very strict one-instance-per-thing side-effect, but could be useful.
```
import <namespace>[.<definition name>] [as <name>]
```
### Examples
```
import collections.NonEmptyList as NEL
import collections.Queue
import collections
```

29
pattern-matching.md Normal file
View file

@ -0,0 +1,29 @@
# Pattern Matching
TODO: This needs just more description and examples and pressure testing.
Ava supports pattern matching and value destructuring.
## The Match Expression
```
match <expr>
case <pattern> => <expr>
case <pattern> => <expr>
...
```
## The Default Case
The _default case_, `_`, is used to catch all unmatched cases. This can be used
to only match a partial set of possible values:
TODO: WIP gotta figure out functions.
```
fn example: String => Int32 is
str => match str
case "foo" => 1
case "bar" => 2
case _ => 3
```

101
records.md Normal file
View file

@ -0,0 +1,101 @@
# Records
Records may be defined. Each record contains one or more named fields. Note that
records are just [tuples](tuples.md) with named fields. In many ways, the two
can be interchanged.
```
record Foo is (x: String, y: Int32)
```
Record fields are both _ordered_ and _named_.
- [Anonymous Records](#anonymous-records)
- [Generic Records](#generic-records)
- [Instantiating Records](#instantiating-records)
- [Copying Data](#copying-data)
- [Accessing Record Data](#accessing-record-data)
- [Tuple Interactions](#tuple-interactions)
- [Destructuring Records](#destructuring-records)
## Anonymous Records
Records do _not_ need to be named.
```
(x: String, y: String, z: String)
```
## Generic Records
Records may have generically typed data, and accept a type constructor:
```
record Foo[A, B] is (x: A, y: B)
```
## Instantiating Records
```
record Foo is (x: String, y: Int32)
let foo1 := Foo("foo", 1)
let foo2 := Foo(x := "foo", y := 1)
```
## Copying Data
Copy syntax allows any record to be duplicated, with any fields explicitly
overridden by some value:
```
record Foo is (x: String, y: Int32)
let foo := Foo("foo", 1)
let bar := copy(foo)
let baz := copy(foo, ("y": 2))
```
## Accessing Record Data
The `.` operator is used to access individual fields on a record.
```
record Foo is (x: String, y: Int32)
let foo := Foo("foo", 1)
let bar := foo.x
```
Note that because records are just tuples, tuple syntax continues to work:
```
let baz: Int32 := foo._2
```
## Tuple Interactions
Use the following record definition for this section:
```
record Foo is (x: String, y: Int32)
```
Tuples can be assigned from records.
```
let foo := Foo("foo", 1)
let some_tuple: (String, Int32) := foo
```
Records can be assigned from tuples.
```
let some_tuple := ("foo", 1)
let foo: Foo := some_tuple
```
## Destructuring Records
Records can be _destructured_ via [pattern matching](pattern-matching.md)
capabilities. This can take two possible forms.

3
recursion.md Normal file
View file

@ -0,0 +1,3 @@
# Recursion
TCO is supported.

5
standard-library.md Normal file
View file

@ -0,0 +1,5 @@
# Standard Library
Things like supporting different standard functions, type class list, etc.
What about collections?

1
strings.md Normal file
View file

@ -0,0 +1 @@
# Strings

27
table-of-contents.md Normal file
View file

@ -0,0 +1,27 @@
# Table of Contents
- [Names](names.md)
- [Namespaces](namespaces.md)
- [Exports](namespaces.md#exports)
- [Imports](namespaces.md#imports)
- [Definitions](definitions.md)
- [General Syntax](general-syntax.md)
- [Expressions](expressions.md)
- [Values](values.md)
- [Constants](constants.md)
- [Variables](variables.md)
- [Effects](effects.md)
- [Records](records.md)
- [Types](types.md)
- [Type Aliases](type-aliases.md)
- [Type Classes](type-classes.md)
- [Enumerations](enumerations.md)
- [Type Unions](type-unions.md)
- [Functions](functions.md)
- [Strings](strings.md)
- [Tuples](tuples.md)
- [Memory Management](memory-management.md)
- [Holes](holes.md)
- [Pattern Matching](pattern-matching.md)
- [Recursion](recursion.md)
- [Standard Library](standard-library.md)

61
tuples.md Normal file
View file

@ -0,0 +1,61 @@
# Tuples
Tuples are heterogeneous, fixed-size, collections of values. Tuples contain 0 to
`N` values, where `N` is unbounded. Tuples are similar to [Records](records.md),
but records have named parameters.
## Syntax
```
let x := ()
let y := ("foo")
let z := ("foo", 1)
let w := ("foo", 1, true, 3.14)
```
## Type of a Standard Tuple
Consider the tuple `("foo", 1, true, 3.14)`. It has type
`(String, Int32, Boolean, Float64)`.
## The Empty Tuple
The type `()` has a single possible value, `()`. This is the empty tuple. It is
typically used as a token to indicate side-effects with no other useful output.
## Accessing Tuple Members
Each tuple member is _indexed_ and can be directly accessed via a property of
that index:
```
let w := ("foo", 1, true)
let x := w._1
let y := w._2
let z := w._3
```
## Destructuring Tuples
Tuples can be _destructured_ via [pattern matching](pattern-matching.md)
capabilities. This can take two possible forms.
### Destructured Binding
Tuples may be destructured at the point of binding.
```
let w := ("foo", 1, true)
let (x, y, z) := w
```
### Destructured Match Case
```
let w := ("foo", 1, true)
let z :=
match w
case ("foo", _, x) => x
case _ => false
```

1
type-aliases.md Normal file
View file

@ -0,0 +1 @@
# Type Aliases

1
type-classes.md Normal file
View file

@ -0,0 +1 @@
# Type Classes

8
type-unions.md Normal file
View file

@ -0,0 +1,8 @@
# Type Unions
TODO: Explore. What syntax is better? `|` seems better.
```
type Foo = String | Int32
type Bar = Boolean or String or Int32
```

69
types.md Normal file
View file

@ -0,0 +1,69 @@
# Types
- [The Ava Type System](#the-ava-type-system)
- [The Nothing Type](#the-nothing-type)
- [Type Definitions](#type-definitions)
- [Type Constructors](#type-constructors)
- [Higher Kinded Types](#higher-kinded-types)
## The Ava Type System
Ava is based on a static type system with support for higher-kinded types. Ava
does not support inheritence. Ava _does_ support sum types
([Enumerations](enumerations.md)) and _does_ support
[type classes](type-classes.md).
## The Nothing Type
The special type, `Nothing`, cannot be instantiated. It can be used to describe
cases that cannot be expressed. For example, `Either[Nothing, String]` cannot
ever be a `Left`. `Nothing` can be used to satisfy any type requirement. For
example, `Either[Nothing, String]` satisfies `Either[Int32, String]`.
## Type Definitions
Types may directly, at the top level, be defined in terms of some
[type constructor](#type-constructors):
```
type TupleList[A, B] is List[(A, B)]
```
Or in general terms:
```
type <type constructor> is <type definition>
```
### Type Constructors
Type constructors essentially allow for types to be defined in terms of some set
of type inputs. Consider the prior example:
```
type TupleList[A, B] is List[(A, B)]
```
The `TupleList` type requires two type arguments, `A` and `B`, to be constructed
as a concrete type. For example, `TupleList[String, Int32]` is a concrete type.
## Higher Kinded Types
Type constructors in Ava may be higher order (e.g. not order 1) functions:
```
// Assume this exists
type List[A] is ???
fn map[A, B]: (List[A], (A) => B) => List[B]
type class Functor[F[*]] is
fn map[A, B]: (F[A])((A) => B) => F[B]
instance Functor[List] is
fn map[A, B]: (List[A])((A) => B) => List[B] is
(list)(f) => map(list, f)
```
In this example, `Functor[F[*]]` is a type constructor which accepts a single
type argument. That type argument is a type constructor that accepts a single
concrete (order 0) type argument.

1
values.md Normal file
View file

@ -0,0 +1 @@
# Values

8
variables.md Normal file
View file

@ -0,0 +1,8 @@
# Variables
- [Immutable Values](#immutable-values)
- [Mutable Values](#mutable-values)
## Variable Names
Please refer to [Names](names.md).