diff --git a/book/src/syntax-and-semantics.md b/book/src/syntax-and-semantics.md index e142018b85..c9ff11d269 100644 --- a/book/src/syntax-and-semantics.md +++ b/book/src/syntax-and-semantics.md @@ -439,7 +439,7 @@ f { x = 1, y = 2, other = "abc" } // The record we pass can hold more fields tha [Row type]:./syntax-and-semantics.html#row-type -### Enumeration type +### Variant type ``` ( | ()* )* @@ -449,6 +449,45 @@ f { x = 1, y = 2, other = "abc" } // The record we pass can hold more fields tha Gluon also has a second way of grouping data which is the enumeration type which allows you to represent a value being one of several variants. In the example above is the representation of Gluon's standard `Result` type. It represents either the value having been successfully computed (`Ok t`) or that an error occurred (`Err e`). +#### Generalized algebraic data type + +Variants also have an alternate syntax which allows [Generalized Algebraic Data Type][GADT] (GADT) to be specified. + +``` +type ()* = ( | : ()* )* + +type Result e t = + | Err : e -> Result e t + | Ok : t -> Result e t +``` + +This encodes the same type as the variant in the previous (with the restriction that the variant must be specified as the top of the type definition). +While this simple example doesn't do anything that a normal variant could not define it is possible to encode much more information about what a variant contains through such as the canonical expression example. + +```f#,rust +type Expr a = + | Int : Int -> Expr Int + | Bool : Bool -> Expr Bool + | Add : Expr Int -> Expr Int -> Expr Int + | If : Expr Bool -> Expr a -> Expr a -> Expr a + +let eval e : Expr a -> a = + match e with + | Int x -> x + | Bool x -> x + | Add l r -> eval l + eval r + | If p t f -> if eval p then eval t else eval f + +let int : Int = eval (If (Bool True) (Add (Int 1) (Int 2)) (Int 0)) +let bool : Bool = eval (Bool True) +() +``` + +Through specifying a more specific type in the return type of a GADT variant we enforce that the variant can only contain that specific type +in the argument. We can then exploit it when matching to refine the argument. + +[GADT]:https://en.wikipedia.org/wiki/Generalized_algebraic_data_type + ### Alias type ``` diff --git a/examples/lisp/main.rs b/examples/lisp/main.rs index e52a3213e5..d5ee180738 100644 --- a/examples/lisp/main.rs +++ b/examples/lisp/main.rs @@ -6,18 +6,21 @@ extern crate gluon_codegen; use std::io::{self, BufRead}; -use gluon::vm::api::{FunctionRef, OpaqueValue}; -use gluon::{new_vm, Compiler, RootedThread}; +use gluon::{ + new_vm, + vm::api::{FunctionRef, OpaqueValue}, + Compiler, RootedThread, +}; #[derive(VmType)] #[gluon(vm_type = "examples.lisp.lisp.Expr")] -struct Expr; -type OpaqueExpr = OpaqueValue; +struct ExprMarker; +type Expr = OpaqueValue; #[derive(VmType)] #[gluon(vm_type = "examples.lisp.lisp.LispState")] -struct LispState; -type OpaqueLispState = OpaqueValue; +struct LispStateMarker; +type LispState = OpaqueValue; fn main() { if let Err(err) = main_() { @@ -31,14 +34,13 @@ fn main_() -> gluon::Result<()> { let thread = new_vm(); Compiler::new().load_file(&thread, "examples/lisp/lisp.glu")?; - let mut eval: FunctionRef< - fn(String, OpaqueLispState) -> Result<(OpaqueExpr, OpaqueLispState), String>, - > = thread.get_global("examples.lisp.lisp.eval_env_string")?; + let mut eval: FunctionRef Result<(Expr, LispState), String>> = + thread.get_global("examples.lisp.lisp.eval_env_string")?; - let mut show: FunctionRef String> = + let mut show: FunctionRef String> = thread.get_global("examples.lisp.lisp.show.show")?; - let mut env: OpaqueLispState = thread.get_global("examples.lisp.lisp.default_env")?; + let mut env: LispState = thread.get_global("examples.lisp.lisp.default_env")?; let stdin = io::stdin(); for line in stdin.lock().lines() { @@ -55,3 +57,31 @@ fn main_() -> gluon::Result<()> { } Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + use gluon::Error; + + fn eval_lisp(expr: &str) -> Result { + let thread = new_vm(); + Compiler::new().load_file(&thread, "examples/lisp/lisp.glu")?; + + let mut eval: FunctionRef Result<(Expr, LispState), String>> = + thread.get_global("examples.lisp.lisp.eval_env_string")?; + + let mut show: FunctionRef String> = + thread.get_global("examples.lisp.lisp.show.show")?; + + let env: LispState = thread.get_global("examples.lisp.lisp.default_env")?; + + let (msg, _) = eval.call(expr.to_string(), env.clone())??; + Ok(show.call(msg.clone())?) + } + + #[test] + fn basic() { + assert_eq!(eval_lisp("(+ 1 2 3)").unwrap(), "6"); + } +} diff --git a/repl/src/repl.glu b/repl/src/repl.glu index b7e8d77ffb..7ddeae9e98 100644 --- a/repl/src/repl.glu +++ b/repl/src/repl.glu @@ -21,7 +21,6 @@ let { flat_map, (>>=) } = import! std.monad let { foldl } = import! std.foldable let { (<>) } = import! std.prelude -let (++) = (<>) rec type ReplEffect r a = [| reader : Reader Repl, state : State Settings, lift : Lift IO | r |] a @@ -139,7 +138,7 @@ let commands : Commands = alias = "p", info = "Sets the prompt", action = \prompt -> - do _ = modify (\settings -> + seq modify (\settings -> // TODO Should be able infer this before the `..` splatting let settings : Settings = settings { prompt, @@ -150,9 +149,9 @@ let commands : Commands = { name = "color", alias = "c", - info = "Sets wheter to use color in the repl: auto, always, always-ansi, never", + info = "Sets whether to use color in the repl: auto, always, always-ansi, never", action = \color -> - do _ = + seq match repl_prim.parse_color color with | Ok color -> modify (\settings ->