From 218647484d3bbe3e1d1710c44870719a76bdabcf Mon Sep 17 00:00:00 2001 From: Pat Garrity Date: Sat, 27 Jan 2024 22:00:47 -0600 Subject: [PATCH] Continuing to toss ideas around and work on different parts of the base design. --- constants.md | 9 ++-- definitions.md | 2 +- enumerations.md | 31 +++++++++---- examples.md | 15 ++++++ functions.md | 107 ++++++++++++++++++++++++++++++++++--------- infix-operators.md | 33 +++++++++++++ names.md | 1 - records.md | 12 ++--- table-of-contents.md | 4 ++ tuples.md | 10 ++++ types.md | 27 +++++++---- 11 files changed, 197 insertions(+), 54 deletions(-) create mode 100644 examples.md create mode 100644 infix-operators.md diff --git a/constants.md b/constants.md index bdd0c8c..7d3ef40 100644 --- a/constants.md +++ b/constants.md @@ -5,19 +5,16 @@ _Constants_ are definitions which provide a name and explicit type for some constant. ``` -const weak_pi: Float64 is 3.14 -const my_name: String is "Pat" +const weak_pi: Float64 := 3.14 +const my_name: String := "Pat" ``` In general: ``` -const : is +const : := ``` -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: diff --git a/definitions.md b/definitions.md index 3474229..b84e758 100644 --- a/definitions.md +++ b/definitions.md @@ -9,7 +9,7 @@ class (and instance) definitions. All definitions adhere to the following syntax: ``` -[export] is +[export] ``` Each definition type ultimately controls the description and body. All diff --git a/enumerations.md b/enumerations.md index 4311d19..44bd92d 100644 --- a/enumerations.md +++ b/enumerations.md @@ -5,11 +5,17 @@ Enumerations are sum types. ## Example: The Option Type ``` -enum Option[A] is - record Some[A] is (value: A) +given A +enum Option + given A + record Some (value: A) object None +end enum -fn some[A]: (a: A) => Some[A] is a => Some(a) +given A +fn some: (a: A) => Some[A] + a => Some (a) +end fn ``` ## Example: The Either Type @@ -17,10 +23,19 @@ fn some[A]: (a: A) => Some[A] is a => Some(a) 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 } +given L, R +enum Either + given L + record Left (value: L) -fn left[L]: (left: L) => Either[L, Nothing] is l => Left { l } -fn right[R]: (right: R) => Either[Nothing, R] is r => Right { r } + given R + record Right (value: R) + +given L +fn left: (left: L) => Either := l => Left(l) +end fn + +given R +fn right: (right: R) => Either := Right(r) +end fn ``` diff --git a/examples.md b/examples.md new file mode 100644 index 0000000..1234d61 --- /dev/null +++ b/examples.md @@ -0,0 +1,15 @@ +# Examples + +## Hello, World! + +``` +namespace example; + +fn exit_success: () => ExitCode + exit(0) + +export fn main: (args: Array[String]) => Task[ExitCode] + println ("Hello, World!") -> exit_success + +-- map (println ("Hi")) (exit_success) +``` diff --git a/functions.md b/functions.md index 3e5565f..997d53c 100644 --- a/functions.md +++ b/functions.md @@ -1,34 +1,97 @@ # 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. +## Syntax ``` -fn example: () => Int32 is 42 -example -example() +[given T1, T2, ... TN] +fn : => := + +end fn +``` + +- `TN`: Type declaration (e.g. A :: Show) +- `name`: Follows the rules for [names](names.md) +- `input`: One or more named tuples ([records](records.md)). +- `output`: Some type. +- `body`: Function definition. + +Note that ` => ` is the type of a function. + +## Function Definitions + +Function definitions are a series of expressions, where the value of the last +expression is returned. + +``` +fn example: (x: Int32, y: Int32) => Int32 + let xx := x + 1 + let yy := y + 1 + xx * yy +end fn +``` + +## Calling Functions + +Calling a function involves passing that function the tuples it needs. + +``` +given A :: Show +fn example: (a: A) => String + show(a) +end fn + +fn calling_a_function: () => Task[()] + let x := 1 + let y := true + let z := "foo" + println(show(x) + show(y) + show(z)) +end fn +``` + +### Calling with Tuples + +``` +fn example: (x: Int32, y: Int32: z: Int32) => Int32 + x + y + z +end fn + +let inputs := (10, 20, 30) +example inputs ``` ``` -fn example: String => Int32 is str => length(str) -example "foo" -example("foo") +fn example: (x: Int32, y: Int32)(z: Int32, w: Int32) => Int32 + x + y + z + w +end fn + +let in1 := (1, 2) +let in2 := (3, 4) +example in1 in2 +example (1, 2) in2 +example in1 (3, 4) ``` -``` -fn example: { x: Int32, y: Int32 } => Int32 is (x, y) => x + y -example 1 2 -example(1, 2) -example { x: 1, y: 2 } -``` +## Generic Functions + +## Referencing Arguments + +`@args` refers to function arguments. + +- For a single parameter list, it refers to the record. +- For multiple parameter lists, it refers to a tuple containing the lists in + order. + +## Lazy Arguments + +The `lazy` keyword allows the expression providing the value to have evaluation +deferred until the value is _used_ in the function. If the value is not used, +the expression is never evaluated. ``` -fn map[F[*], A, B]: (F[A]) => (A => B) => F[B] is - (fa) (f) => fa f - -map(list)(x => x + 1) +fn foo: (lazy x: Int32) => Int32 + x * 2 +end fn ``` + +Note that this is equivalent to encoding a parameter as a synchronous effect and +evaluating it to value when needed. diff --git a/infix-operators.md b/infix-operators.md new file mode 100644 index 0000000..3687078 --- /dev/null +++ b/infix-operators.md @@ -0,0 +1,33 @@ +# Infix Operators + +Example of defining `->` to do `map` for any functor: + +``` +given F[*] :: Functor, A, B +infix -> (fa: F[A], f: (A) => B) => F[B] := +map (fa) (f) +``` + +``` +let xs: List[Int] := list(1, 2, 3) +let addOne := (x: Int32) => x + 1 + +-- [2, 3, 4] +let ys := xs -> addOne + +-- [4, 5, 6] +let zs := xs -> addOne -> addOne -> addOne +-- note, in order precedence: ((xs -> addOne) -> addOne) -> addOne +``` + +## Precedence + +In order precedence. + +## Using Parentheses to Group + +Infix operators for numeric operations are defined for tuples of size one. + +``` +(12 - 2) / 2 +``` diff --git a/names.md b/names.md index bfffa65..f0ef499 100644 --- a/names.md +++ b/names.md @@ -12,7 +12,6 @@ Names are UTF-8 strings. The following values are _excluded_: - `=` - `"` or `'` - `[` or `]` -- `{` or `}` - `(` or `)` ## Convention diff --git a/records.md b/records.md index f3cf0a8..dc71430 100644 --- a/records.md +++ b/records.md @@ -5,7 +5,7 @@ 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 Foo (x: String, y: Int32) ``` Record fields are both _ordered_ and _named_. @@ -31,13 +31,13 @@ Records do _not_ need to be named. Records may have generically typed data, and accept a type constructor: ``` -record Foo[A, B] is (x: A, y: B) +record Foo[A, B] (x: A, y: B) ``` ## Instantiating Records ``` -record Foo is (x: String, y: Int32) +record Foo (x: String, y: Int32) let foo1 := Foo("foo", 1) let foo2 := Foo(x := "foo", y := 1) @@ -49,7 +49,7 @@ Copy syntax allows any record to be duplicated, with any fields explicitly overridden by some value: ``` -record Foo is (x: String, y: Int32) +record Foo (x: String, y: Int32) let foo := Foo("foo", 1) let bar := copy(foo) @@ -61,7 +61,7 @@ let baz := copy(foo, ("y": 2)) The `.` operator is used to access individual fields on a record. ``` -record Foo is (x: String, y: Int32) +record Foo (x: String, y: Int32) let foo := Foo("foo", 1) let bar := foo.x @@ -78,7 +78,7 @@ let baz: Int32 := foo._2 Use the following record definition for this section: ``` -record Foo is (x: String, y: Int32) +record Foo (x: String, y: Int32) ``` Tuples can be assigned from records. diff --git a/table-of-contents.md b/table-of-contents.md index 252caa7..78d84b7 100644 --- a/table-of-contents.md +++ b/table-of-contents.md @@ -24,4 +24,8 @@ - [Holes](holes.md) - [Pattern Matching](pattern-matching.md) - [Recursion](recursion.md) +- [Infix Operators](infix-operators.md) - [Standard Library](standard-library.md) +- [Examples](examples.md) +- [Standard Types](standard-types.md) +- [Numeric Overflow](numeric-overflow.md) diff --git a/tuples.md b/tuples.md index f0f0e91..d44983b 100644 --- a/tuples.md +++ b/tuples.md @@ -11,6 +11,16 @@ let x := () let y := ("foo") let z := ("foo", 1) let w := ("foo", 1, true, 3.14) +let a: Int32 := unwrap(1) +``` + +Unwrapping a tuple of size one is just the identity function: + +``` +given A +fn unwrap: (a: A) => A + a +end fn ``` ## Type of a Standard Tuple diff --git a/types.md b/types.md index 765fa31..85ab67a 100644 --- a/types.md +++ b/types.md @@ -26,13 +26,14 @@ 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)] +given A, B +type TupleList := List[(A, B)] ``` Or in general terms: ``` -type is +type := ``` ### Type Constructors @@ -41,7 +42,8 @@ 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)] +given A, B +type TupleList := List[(A, B)] ``` The `TupleList` type requires two type arguments, `A` and `B`, to be constructed @@ -52,16 +54,21 @@ as a concrete type. For example, `TupleList[String, Int32]` is a concrete type. 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] +given A +type List is ??? -type class Functor[F[*]] is - fn map[A, B]: (F[A])((A) => B) => F[B] +given A, B +fn map_list: (List[A], (A) => B) => List[B] + +given F[*] +type class Functor is + given A, B + fn map: (F[A])((A) => B) => F instance Functor[List] is - fn map[A, B]: (List[A])((A) => B) => List[B] is - (list)(f) => map(list, f) + given A, B + fn map: (List[A])((A) => B) => [B] := + (list)(f) => map_list (list, f) ``` In this example, `Functor[F[*]]` is a type constructor which accepts a single