diff --git a/ava.ebnf b/ava.ebnf index 0a5042f..4b2f7f5 100644 --- a/ava.ebnf +++ b/ava.ebnf @@ -52,10 +52,20 @@ k_object ::= 'o', 'b', 'j', 'e', 'c', 't'; k_let ::= 'l', 'e', 't'; k_mut ::= 'm', 'u', 't'; k_export ::= 'e', 'x', 'p', 'o', 'r', 't'; + +(* Starts an import definition. *) k_import ::= 'i', 'm', 'p', 'o', 'r', 't'; + +(* Denotes a namespace declaration. *) k_namespace ::= 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e'; + +(* Starts an infix function definition *) k_infix ::= 'i', 'n', 'f', 'i', 'x'; + +(* Starts a function definition. *) k_fn ::= 'f', 'n'; + +(* Ends any block definition. Must be followed by the keyword of the block. *) k_end ::= 'e', 'n', 'd'; (* Initiates a pattern match against some value. *) @@ -63,6 +73,7 @@ k_match ::= 'm', 'a', 't', 'c', 'h'; (* Initiates a pattern matching case. Applicable to functions and matches. *) k_case ::= 'c', 'a', 's', 'e'; +k_lambda ::= 'λ'; (* Used in if/else syntax. Starts such a block and used in 'else if'. *) k_if ::= 'i', 'f'; @@ -94,8 +105,8 @@ k_false ::= 'f', 'a', 'l', 's', 'e'; keyword ::= k_type | k_class | k_alias | k_const | k_enum | k_record | k_object | k_let | k_mut | k_export | k_import | k_namespace | k_infix | k_fn | k_end | k_match | k_case - | k_if | k_then | k_else | k_do | k_return | k_given - | k_private | k_true | k_false; + | k_lambda | k_if | k_then | k_else | k_do | k_return + | k_given | k_private | k_true | k_false; (* ============== *) (* Literal Values *) @@ -106,7 +117,7 @@ literal_integer ::= ['-'], number_integer; literal_float ::= ['-'], number_float; literal_char ::= "'", char_char, "'"; literal_string ::= '"', {string_char}, "'"; -empty_tuple ::= lparen, rparen; +empty_tuple ::= op_tuple, lparen, rparen; empty_list ::= lbracket, rbracket; literal ::= literal_bool | literal_integer @@ -124,6 +135,9 @@ literal ::= literal_bool runtime. *) hole ::= '?', '?', '?'; +(* Used for splat imports: import foo.bar.baz.* is an example. *) +op_import_splat ::= '*'; + (* Used to access members. Relevant for namespaces, records, enums, classes. *) op_member ::= '.'; @@ -134,7 +148,7 @@ op_comma ::= ','; op_type_union ::= '|'; (* List operator. Prepend LHS to RHS. Can be used in pattern matching. *) -op_list_prepend ::= ':'; +op_list_prepend ::= ':', '-'; (* Only valid adjacent to a name declaration. Explicitly declare the type that the name has. *) @@ -153,9 +167,22 @@ op_bind_do ::= '<', '-'; (* Indicates some type is constrained by some type class, ex F * :: Functor *) op_class_member ::= ':', ':'; -(* Indicates the start of a case, case => *) +(* Indicates the start of a pattern matching case, used in both forms: + + case => + λ => + + The λ form is preferred for functions, 'case' is preferred for 'match'. *) op_case ::= '=', '>'; +(* Declares a tuple, as in #(a, b, c) *) +op_tuple ::= '#'; + +operator ::= hole | op_import_splat | op_member | op_comma + | op_type_union | op_list_prepend | op_bind_type + | op_bind_value | op_fn_return | -> op_bind_do + | op_class_member | op_case | op_tuple; + (* ===== *) (* Names *) (* ===== *) diff --git a/functions.md b/functions.md index 5e91611..00c7ddf 100644 --- a/functions.md +++ b/functions.md @@ -24,6 +24,10 @@ instance Functor List case f [h : t] => f h : map f t end fn end instance + +-- Equivalent to F[*[*]] +given F (* *), C, B +fn example: F (C B) -> B ``` ``` diff --git a/lists.md b/lists.md index 71829ea..f8b3c2c 100644 --- a/lists.md +++ b/lists.md @@ -1,14 +1,15 @@ # Lists -Lists are a standard Ava type. Specifically, `List[A]` represents a linked list. +Lists are a standard Ava type. Specifically `[A]` represents a linked list. ``` -let x: List[Int32] := { 1, 2, 3 } -let y := prepend(x, 0) -let z := append(x, { 4, 5 }) -let w: Option[Int32] := head(x) -let tail: List[Int32] := tail(x) -let sz: List[Int32] := size(x) +let x: [Int32] := [1, 2, 3] +let y := prepend x 0 +let alt_y := 0 :- x +let z := append x [4, 5] +let w: Option[Int32] := head x +let tail: [Int32] := tail x +let sz: [Int32] := size x ``` ## Type Class Support @@ -24,21 +25,9 @@ The type `NonEmptyList` is a list which cannot be empty: ``` given A -record NonEmptyList: (head: A, tail: List[A]) +record NonEmptyList(head: A, tail: [A]) ``` ## Indexing Lists cannot be accessed by index. - -## Implementation - -``` -given A -enum List - object Nil - - given A - record List(head: A, tail: List[A]) -end enum -``` diff --git a/names.md b/names.md index b94c3bd..96267d2 100644 --- a/names.md +++ b/names.md @@ -12,7 +12,6 @@ Names are UTF-8 strings. The following values are _excluded_: - `"` - `,` - `\` -- `[` or `]` - `(` or `)` ## Convention diff --git a/records.md b/records.md index 2777877..0f28cdc 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 (x: String, y: Int32) +record Foo(x: String, y: Int32) ``` Record fields are both _ordered_ and _named_. @@ -23,7 +23,7 @@ Record fields are both _ordered_ and _named_. Records do _not_ need to be named. ``` -(x: String, y: String, z: String) +#(x: String, y: String, z: String) ``` ## Generic Records @@ -32,7 +32,7 @@ Records may have generically typed data, and accept a type constructor: ``` given A, B -record Foo (x: A, y: B) +record Foo(x: A, y: B) ``` ## Instantiating Records @@ -41,7 +41,10 @@ record Foo (x: A, y: B) record Foo (x: String, y: Int32) let foo1 := Foo("foo", 1) -let foo2 := Foo(x := "foo", y := 1) +let foo2 := Foo( + x := "foo", + y := 1 +) ``` ## Copying Data @@ -50,11 +53,11 @@ Copy syntax allows any record to be duplicated, with any fields explicitly overridden by some value: ``` -record Foo (x: String, y: Int32) +record Foo(x: String, y: Int32) let foo := Foo("foo", 1) -let bar := copy(foo) -let baz := copy(foo, ("y": 2)) +let bar := copy foo +let baz := copy foo #(y := 2) ``` ## Accessing Record Data @@ -62,7 +65,7 @@ let baz := copy(foo, ("y": 2)) The `.` operator is used to access individual fields on a record. ``` -record Foo (x: String, y: Int32) +record Foo(x: String, y: Int32) let foo := Foo("foo", 1) let bar := foo.x @@ -75,45 +78,43 @@ Tuple syntax is _not_ supported on records. Use the following record definition for this section: ``` -record Foo (x: String, y: Int32) +record Foo(x: String, y: Int32) ``` Tuples can be assigned from records. ``` let foo := Foo("foo", 1) -let some_tuple: (String, Int32) := foo +let some_tuple: #(String, Int32) := foo ``` Records can be assigned from tuples. ``` -let some_tuple := ("foo", 1) +let some_tuple := #("foo", 1) let foo: Foo := some_tuple ``` -Records can be cast to tuples by extracting the tuple metadata from the record. +Records can be cast to tuples if the types/ordering are identical. ``` let foo := Foo("foo", 1) -let bar: (String, Int32) := @foo.tuple -``` - -TODO: Metdata is up in the air. - -Records respect the tuple size metadata. - -``` -let size := @foo.size -``` - -Records provide field metadata. - -``` -let desc: RecordDescription := @foo.description +let bar: #(String, Int32) := foo ``` ## Destructuring Records Records can be _destructured_ via [pattern matching](pattern-matching.md) capabilities. + +``` +record Foo(x: String, y: Int32) + +let foo := Foo("foo", 1) + +match foo + case "foo" _ => "bar" + case _ 2 => "baz" + case _ _ => "legume" +end match +``` diff --git a/standard-type-classes.md b/standard-type-classes.md index 2b6121a..e1a0a8a 100644 --- a/standard-type-classes.md +++ b/standard-type-classes.md @@ -1,17 +1,14 @@ # Standard Type Classes ``` ---- Associative binary operation. ---- **Associative**: `combine(x, combine(y, z)) = combine(combine(x, y), z)` given A class Semigroup - fn combine(x: A, y: A): A + fn combine: A -> A -> A end class ---- **Empty Identity**: `combine(x, empty()) = combine(empty(), x) = x` given A :: Semigroup class Monoid - fn empty() => A + fn empty: A end class --- Type class for type constructors which can be mapped over. @@ -20,7 +17,7 @@ end class --- --- **Composition**: `fa -> f -> g = fa -> (f ∘ g)` --- **Identity**: `fa -> ((x) => x) = fa` -given F[*] +given F * class Functor --- Transform some wrapped data from one type to another, preserving the --- wrapper. @@ -29,105 +26,99 @@ class Functor --- @tparam B The type of output data. --- @param fa The functor input. --- @param f The function to transform data from `A` to `B`. - given [A, B] - fn map: (fa: F[A])(f: (A) => B) => F[B] + given A, B + fn map: F A -> (A -> B) -> F B end class --- Need Laws -given F[*] :: Functor +given F * :: Functor class Apply given A, B - fn ap: (ff: F[(A) => B])(fa: F[A]) => F[B] + fn ap: F (A -> B) -> F A -> F B end class --- Need Laws -given F[*] :: Apply +given F * :: Apply class Applicative given A - fn pure: (value: A) => F[A] + fn pure: A -> F A - fn unit: () => F[()] - pure(()) + fn unit: F () + pure () end fn end class --- Any Applicative satisfies Functor. -given F[*] :: Applicative -instance Functor[F] +given F * :: Applicative +instance Functor F given A, B - fn map: (fa: F[A])(f: (A) => B) => F[B] - ap(pure(f))(fa) + fn map: F A -> (A -> B) -> F B + fa f => ap (pure f) fa end fn end instance -given F[*] :: Apply +given F * :: Apply class FlatMap given A, B - fn fmap: (fa: F[A])(f: (A) => F[B]) => F[B] + fn fmap: F A -> (A -> F B) -> F B given A - fn flatten: (ffa: F[F[A]]) => F[A] - fmap(ffa)((fa) => fa) + fn flatten: F (F A) -> F A + ffa => fmap ffa (fa => fa) end fn end class -given F[*] :: FlatMap, Applicative +given F * :: FlatMap, Applicative class Monad end class ---- Any Monad satisfies Functor. ---- Note for type class spec, must account for "override" precedence -given F[*] :: Applicative -instance Functor[F] - fn map: (fa: F[A])(f: (A) => B) => F[B] - fmap(fa)((a) => pure(f(a))) +given F * :: Monad +instance Functor F + fn map: F A -> (A -> B) -> F B + λ fa f => fmap fa (λ a => pure (f a)) end fn end instance -given F[*, *] +given F * * class Bifunctor - def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D] - given A, B, C, D - fn bimap(fab: F[A, B])(f: (A) => C, g: (B) => D) => F[C, D] + fn bimap F A B -> (A -> C) -> (B -> D) -> F C D end class -given F[*] :: Functor +given F * :: Functor class CoFlatMap given A, B - fn cofmap: (fa: F[A])(f: (F[A]) => B) => F[B] + fn cofmap: F A -> (F A -> B) -> F B given A - fn coflatten: (fa: F[A]) => F[F[A]] - cofmap(fa)((fa) => fa) + fn coflatten: F A -> F (F A) + λ fa => cofmap fa (λ f => f) end fn end class -given F[*] :: CoFlatMap +given F * :: CoFlatMap class CoMonad given A - fn extract: (fa: F[A]) => A + fn extract: F A -> A end class given A class Show - fn show: (value: A) => String + fn show: A -> String end class given A, B class Eq - fn eq: (x: A, y: B) => Boolean + fn eq: A -> B -> Boolean - fn neq: (x: A, y: B) => Boolean - not(eq(x, y)) + fn neq: A -> B -> Boolean + λ x y => not (eq x y) end fn - infix =: (x: A, y: B) => Boolean - eq(x, y) + infix =: A -> B -> Boolean + λ x y => eq x y end infix - infix !=: (x: A, y: B) => Boolean - neq(x, y) + infix !=: A -> B -> Boolean + λ x y => neq x y end infix end class @@ -139,20 +130,22 @@ end enum given A class Compare - fn compare: (x: A, y: A) => Comparison + fn compare: A -> A -> Comparison end class -given A -instance Eq[A, A] - fn eq: (x: A, y: A) => Boolean - match compare(x, y) - case EqualTo => true - case _ => false +given A :: Compare +instance Eq A A + fn eq: A -> A -> Boolean + λ x y => + match compare x y + case EqualTo => true + case _ => false + end match end fn end instance given A class HashCode - fn hash_code(data: A) => Int32 + fn hash_code: A -> Int32 end class ``` diff --git a/tuples.md b/tuples.md index 4bcdecf..ba694e5 100644 --- a/tuples.md +++ b/tuples.md @@ -7,10 +7,10 @@ but records have named parameters. ## Syntax ``` -let x := () -let y := ("foo") -let z := ("foo", 1) -let w := ("foo", 1, true, 3.14) +let x := #() +let y := #("foo") +let z := #("foo", 1) +let w := #("foo", 1, true, 3.14) ``` ## Type of a Standard Tuple