From fb35db5153161d1ee393ae29bc9adfc1db553458 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 6 Oct 2019 16:45:25 +0200 Subject: [PATCH 1/6] feat: Allow attributes to be specified on fields inside types --- base/src/ast.rs | 34 ++++++++++++++++++++------------ base/src/types/mod.rs | 14 ++++++------- base/src/types/pretty_print.rs | 6 +++--- check/src/metadata.rs | 12 ++++++------ check/src/typecheck/error.rs | 4 ++-- check/src/unify_type.rs | 4 ++-- check/tests/metadata.rs | 36 +++++++++++++++++++++++++++++++++- parser/src/grammar.lalrpop | 14 ++++++------- 8 files changed, 83 insertions(+), 41 deletions(-) diff --git a/base/src/ast.rs b/base/src/ast.rs index 886b3b0342..f1241570f0 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -69,7 +69,7 @@ impl<'a, T: ?Sized + IdentEnv> IdentEnv for &'a mut T { #[derive(Clone, Eq, PartialEq, Debug)] struct InnerAstType { - comment: Option, + metadata: Option, typ: Spanned>, BytePos>, } @@ -101,7 +101,10 @@ impl> fmt::Display for AstType { impl From>, BytePos>> for AstType { fn from(typ: Spanned>, BytePos>) -> Self { AstType { - _typ: Box::new(InnerAstType { comment: None, typ }), + _typ: Box::new(InnerAstType { + metadata: None, + typ, + }), } } } @@ -139,34 +142,39 @@ where } } -pub trait Commented { - fn comment(&self) -> Option<&Comment>; -} +pub trait HasMetadata { + fn metadata(&self) -> Option<&Metadata>; -impl Commented for AstType { fn comment(&self) -> Option<&Comment> { - self._typ.comment.as_ref() + self.metadata() + .and_then(|metadata| metadata.comment.as_ref()) + } +} + +impl HasMetadata for AstType { + fn metadata(&self) -> Option<&Metadata> { + self._typ.metadata.as_ref() } } impl AstType { - pub fn with_comment(comment: T, typ: Spanned>, BytePos>) -> Self + pub fn with_metadata(metadata: T, typ: Spanned>, BytePos>) -> Self where - T: Into>, + T: Into>, { AstType { _typ: Box::new(InnerAstType { - comment: comment.into(), + metadata: metadata.into(), typ, }), } } - pub fn set_comment(&mut self, comment: T) + pub fn set_metadata(&mut self, metadata: T) where - T: Into>, + T: Into>, { - self._typ.comment = comment.into(); + self._typ.metadata = metadata.into(); } pub fn into_inner(self) -> Type { diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 611039acea..1da2311de1 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -19,11 +19,11 @@ use smallvec::SmallVec; use itertools::Itertools; use crate::{ - ast::{Commented, EmptyEnv, IdentEnv}, + ast::{EmptyEnv, HasMetadata, IdentEnv}, fnv::FnvMap, kind::{ArcKind, Kind, KindCache, KindEnv}, merge::{merge, merge_collect}, - metadata::Comment, + metadata::Metadata, pos::{BytePos, HasSpan, Span}, source::Source, symbol::{Name, Symbol, SymbolRef}, @@ -1434,8 +1434,8 @@ impl HasSpan for ArcType { } } -impl Commented for ArcType { - fn comment(&self) -> Option<&Comment> { +impl HasMetadata for ArcType { + fn metadata(&self) -> Option<&Metadata> { None } } @@ -1598,7 +1598,7 @@ pub trait TypeExt: Deref::Id, Self>> + Clone + S where Self::Id: AsRef + 'a, A: Clone, - Self: Commented + HasSpan, + Self: HasMetadata + HasSpan, { top(self).pretty(&Printer::new(arena, &())) } @@ -2312,7 +2312,7 @@ const INDENT: usize = 4; impl<'a, I, T> DisplayType<'a, T> where - T: Deref> + HasSpan + Commented + 'a, + T: Deref> + HasSpan + HasMetadata + 'a, I: AsRef + 'a, { pub fn pretty(&self, printer: &Printer<'a, I, A>) -> DocBuilder<'a, Arena<'a, A>, A> @@ -2705,7 +2705,7 @@ pub fn pretty_print<'a, I, T, A>( ) -> DocBuilder<'a, Arena<'a, A>, A> where I: AsRef + 'a, - T: Deref> + HasSpan + Commented, + T: Deref> + HasSpan + HasMetadata, A: Clone, { dt(Prec::Top, typ).pretty(printer) diff --git a/base/src/types/pretty_print.rs b/base/src/types/pretty_print.rs index 69cdf9a9ec..06d4fa7bd6 100644 --- a/base/src/types/pretty_print.rs +++ b/base/src/types/pretty_print.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use pretty::{Arena, Doc, DocAllocator, DocBuilder}; -use crate::ast::{is_operator_char, Commented}; +use crate::ast::{is_operator_char, HasMetadata}; use crate::metadata::{Comment, CommentType}; use crate::pos::{BytePos, HasSpan, Span}; use crate::source::Source; @@ -129,7 +129,7 @@ impl<'a, I, T, A> TypeFormatter<'a, I, T, A> { pub fn pretty(&self, arena: &'a Arena<'a, A>) -> DocBuilder<'a, Arena<'a, A>, A> where - T: Deref> + HasSpan + Commented + 'a, + T: Deref> + HasSpan + HasMetadata + 'a, I: AsRef, A: Clone, { @@ -156,7 +156,7 @@ impl<'a, I, T, A> TypeFormatter<'a, I, T, A> { impl<'a, I, T> fmt::Display for TypeFormatter<'a, I, T, ()> where - T: Deref> + HasSpan + Commented + 'a, + T: Deref> + HasSpan + HasMetadata + 'a, I: AsRef, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/check/src/metadata.rs b/check/src/metadata.rs index 2ef20e94f2..feac4d2e05 100644 --- a/check/src/metadata.rs +++ b/check/src/metadata.rs @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, sync::Arc}; use crate::base::ast::Visitor; use crate::base::ast::{ - self, Argument, AstType, Commented, Expr, Pattern, SpannedExpr, SpannedPattern, ValueBinding, + self, Argument, AstType, Expr, HasMetadata, Pattern, SpannedExpr, SpannedPattern, ValueBinding, }; use crate::base::fnv::FnvMap; use crate::base::metadata::{Metadata, MetadataEnv}; @@ -199,7 +199,7 @@ pub fn metadata( } for field in types { if let Some(m) = metadata.get_module(field.name.value.as_ref()) { - // FIXME Shouldn't need to insert this metadata twice + // TODO Shouldn't need to insert this metadata twice if let Some(type_field) = typ .type_field_iter() .find(|type_field| type_field.name.name_eq(&field.name.value)) @@ -356,7 +356,7 @@ pub fn metadata( ); if metadata.has_data() { - // FIXME Shouldn't need to insert this metadata twice + // TODO Shouldn't need to insert this metadata twice self.stack_var(bind.alias.value.name.clone(), metadata.clone()); self.stack_var(bind.name.value.clone(), metadata); } @@ -383,10 +383,10 @@ pub fn metadata( let module: BTreeMap<_, _> = row_iter(typ) .filter_map(|field| { let field_metadata = Self::metadata_of_type(&field.typ); - let field_metadata = match field.typ.comment() { - Some(comment) => { + let field_metadata = match field.typ.metadata() { + Some(other_metadata) => { let mut metadata = field_metadata.unwrap_or_default(); - metadata.comment = Some(comment.clone()); + metadata.merge_with_ref(other_metadata); Some(metadata) } None => field_metadata, diff --git a/check/src/typecheck/error.rs b/check/src/typecheck/error.rs index 654f24f578..ec5fd0ca3f 100644 --- a/check/src/typecheck/error.rs +++ b/check/src/typecheck/error.rs @@ -86,7 +86,7 @@ where I: fmt::Display + AsRef + Clone, T: TypeExt + fmt::Display - + ast::Commented + + ast::HasMetadata + pos::HasSpan + for<'a> ToDoc<'a, Arena<'a, ()>, (), ()>, { @@ -217,7 +217,7 @@ where I: fmt::Display + AsRef + Clone, T: TypeExt + fmt::Display - + ast::Commented + + ast::HasMetadata + pos::HasSpan + for<'a> ToDoc<'a, Arena<'a, ()>, (), ()>, { diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 0604f96f41..3d4de50ed0 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -127,7 +127,7 @@ impl From for TypeError { impl fmt::Display for TypeError where I: fmt::Display + AsRef, - T: TypeExt + ast::Commented + pos::HasSpan, + T: TypeExt + ast::HasMetadata + pos::HasSpan, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let filter = self.make_filter(); @@ -176,7 +176,7 @@ where impl TypeError where I: fmt::Display + AsRef, - T: TypeExt + ast::Commented + pos::HasSpan, + T: TypeExt + ast::HasMetadata + pos::HasSpan, { pub fn make_filter<'a>(&'a self) -> Box Filter + 'a> { match *self { diff --git a/check/tests/metadata.rs b/check/tests/metadata.rs index c269f1a74c..d1619f082f 100644 --- a/check/tests/metadata.rs +++ b/check/tests/metadata.rs @@ -337,7 +337,7 @@ let x ?test : [Test a] -> a = test.x } #[test] -fn propagate_metadata_through_implicits() { +fn propagate_metadata_through_implicits1() { let _ = env_logger::try_init(); let text = r#" @@ -369,3 +369,37 @@ let x ?test : [Test a] -> Test (Wrap a) = { x = Wrap test.x } }) ); } + +#[test] +fn propagate_metadata_through_implicits2() { + let _ = env_logger::try_init(); + + let text = r#" +type Test a = { + #[attribute] + x : a, +} + +type Wrap a = | Wrap a + +let x ?test : [Test a] -> a = test.x +{ x } +"#; + let (mut expr, result) = support::typecheck_expr(text); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + + let metadata = metadata(&MockEnv, &mut expr); + assert_eq!( + metadata.module.get("x").map(|m| &**m), + Some(&Metadata { + definition: metadata.module.get("x").and_then(|m| m.definition.clone()), + attributes: vec![Attribute { + name: "attribute".into(), + arguments: None, + }], + args: vec![Argument::implicit(intern("test@9_8"))], + ..Metadata::default() + }) + ); +} diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index d01ea5544a..9570d5e630 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -218,7 +218,7 @@ TypeParam: Generic = { }; RecordField: Either>>, Field>> = { - > >)> => { let span = id.span; Either::Left(Field::new( id.value.clone(), @@ -227,22 +227,22 @@ RecordField: Either>>, Field>> = args.into_iter() .map(|id| Generic::new(id, type_cache.kind_cache.hole())) .collect(), - AstType::with_comment(comment, alias), + AstType::with_metadata(metadata, alias), )), )) }, - > => { + > => { let span = id.span; Either::Left(Field::new( id.value.clone(), Alias::new( id.value, Vec::new(), - AstType::with_comment(comment, pos::spanned(span, Type::Hole)), + AstType::with_metadata(metadata, pos::spanned(span, Type::Hole)), ), )) }, - ":" > => { + ":" > => { if env.string(&id).starts_with(char::is_uppercase) { errors.push(::lalrpop_util::ParseError::User { error: pos::spanned(typ.span, format!("Defining a kind for a type in this location is not supported yet").into()), @@ -252,13 +252,13 @@ RecordField: Either>>, Field>> = Alias::new( id, Vec::new(), - AstType::with_comment(comment, typ), + AstType::with_metadata(metadata, typ), ), )) } else { Either::Right(Field::new( id, - AstType::with_comment(comment, typ), + AstType::with_metadata(metadata, typ), )) } }, From dc770831ad7902ca617cb5d2a179ff92af17ada4 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 6 Oct 2019 16:47:21 +0200 Subject: [PATCH 2/6] doc: Improve std.alternative docs --- std/alternative.glu | 10 ++++++++-- std/effect/alt.glu | 9 +++++---- std/json/de.glu | 3 ++- std/list.glu | 2 +- std/option.glu | 2 +- std/parser.glu | 3 ++- std/statet.glu | 4 ++-- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/std/alternative.glu b/std/alternative.glu index 8338669578..6abb809f42 100644 --- a/std/alternative.glu +++ b/std/alternative.glu @@ -5,15 +5,21 @@ let { Applicative } = import! std.applicative #[implicit] type Alternative f = { applicative : Applicative f, + /// The identify of `or` + empty : forall a . f a, + /// An associative binary operation. + /// + /// Evaluates to the first argument if it is not `empty`, otherwise evaluates to the second argument. or : forall a . f a -> f a -> f a, - empty : forall a . f a } let empty ?alt : [Alternative f] -> f a = alt.empty let or ?alt : [Alternative f] -> f a -> f a -> f a = alt.or -/// Alias of `or` +/// An associative binary operation. Alias of `or`. +/// +/// Evaluates to the first argument if it is not `empty`, otherwise evaluates to the second argument. #[infix(left, 3)] let (<|>) : [Alternative f] -> f a -> f a -> f a = or diff --git a/std/effect/alt.glu b/std/effect/alt.glu index e783901451..4c3fd15f99 100644 --- a/std/effect/alt.glu +++ b/std/effect/alt.glu @@ -31,17 +31,18 @@ let run_alt_inner transform fail eff_1 eff_2 : (a -> b) -> (() -> Eff [| | s |] /// /// ``` /// let { assert_eq, ? } = import! std.test -/// let alt = import! std.effect.alt +/// let alt @ { ? } = import! std.effect.alt /// let state = import! std.effect.state /// let { (*>) } = import! std.applicative +/// let { empty } = import! std.alternative /// let { Eff, run_pure, ? } = import! std.effect /// /// let incr = state.modify (\x -> x + 1) /// /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt incr incr))) (1) /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> incr) incr))) (2) -/// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> alt.empty *> incr) incr))) (2) -/// assert_eq (run_pure (state.exec_state 0 (alt.run_alt alt.empty incr))) (1) +/// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> empty *> incr) incr))) (2) +/// assert_eq (run_pure (state.exec_state 0 (alt.run_alt empty incr))) (1) /// /// ``` let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | r |] (Option a) = @@ -50,8 +51,8 @@ let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a let alternative : Alternative (Eff [| alt : Alt | r |]) = { applicative = (import! std.effect).applicative, + empty = empty, or = \l r -> run_alt_inner id (\_ -> empty) l r, - empty = empty } { diff --git a/std/json/de.glu b/std/json/de.glu index 6613ef6b92..fb7b9701ce 100644 --- a/std/json/de.glu +++ b/std/json/de.glu @@ -48,11 +48,12 @@ let applicative : Applicative (Deserializer i) = { let alternative : Alternative (Deserializer i) = { applicative, + empty = deserializer (\stream -> Err (error_msg "empty")), + or = \l r -> deserializer (\stream -> match deserializer l stream with | Ok a -> Ok a | Err _ -> deserializer r stream), - empty = deserializer (\stream -> Err (error_msg "empty")), } let monad : Monad (Deserializer i) = { diff --git a/std/list.glu b/std/list.glu index 31734ecd7f..3885755294 100644 --- a/std/list.glu +++ b/std/list.glu @@ -113,8 +113,8 @@ let some ?alt x : [Alternative f] -> f a -> f (List a) = let alternative : Alternative List = { applicative = applicative, - or = semigroup.append, empty = Nil, + or = semigroup.append, } let monad : Monad List = diff --git a/std/option.glu b/std/option.glu index d695b1a191..5d07260bee 100644 --- a/std/option.glu +++ b/std/option.glu @@ -104,11 +104,11 @@ let applicative : Applicative Option = { let alternative : Alternative Option = { applicative = applicative, + empty = None, or = \x y -> match x with | Some _ -> x | None -> y, - empty = None, } let monad : Monad Option = { diff --git a/std/parser.glu b/std/parser.glu index fd9efa2260..7e7d08eea1 100644 --- a/std/parser.glu +++ b/std/parser.glu @@ -54,11 +54,12 @@ let { (*>), (<*), wrap } = import! std.applicative let alternative : Alternative Parser = { applicative, + empty = parser (\stream -> Err { position = stream.start, message = "empty" }), + or = \l r -> parser (\stream -> match parser l stream with | Ok a -> Ok a | Err _ -> parser r stream), - empty = parser (\stream -> Err { position = stream.start, message = "empty" }), } let { (<|>) } = import! std.alternative diff --git a/std/statet.glu b/std/statet.glu index 8f8a0f4be8..42c823dee8 100644 --- a/std/statet.glu +++ b/std/statet.glu @@ -48,10 +48,10 @@ let transformer : Transformer (StateT s) = { /* monad, */ wrap_monad } let alternative : [Monad m] -> [Alternative m] -> Alternative (StateT s m) = - let stor sra srb = or << sra <*> srb let stempty = transformer.wrap_monad empty + let stor sra srb = or << sra <*> srb - { applicative, or = stor, empty = stempty } + { applicative, empty = stempty, or = stor } let put value : [Monad m] -> s -> StateT s m () = \state -> wrap { value = (), state = value } From 71fbd71bd7653101f6971b0bb236856e421168c9 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 6 Oct 2019 17:00:00 +0200 Subject: [PATCH 3/6] doc: Render attributes on types and values --- base/src/metadata.rs | 12 +++++++++++- doc/src/doc/module.html | 4 ++-- doc/src/lib.rs | 15 +++++++++++++++ doc/tests/doc.rs | 1 + 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/base/src/metadata.rs b/base/src/metadata.rs index c0127dd55d..9ae07b6714 100644 --- a/base/src/metadata.rs +++ b/base/src/metadata.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, sync::Arc}; +use std::{collections::BTreeMap, fmt, sync::Arc}; use crate::{ ast::Argument, @@ -42,6 +42,16 @@ pub struct Attribute { pub arguments: Option, } +impl fmt::Display for Attribute { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#[{}", self.name)?; + if let Some(arguments) = &self.arguments { + write!(f, "({})", arguments)?; + } + write!(f, "]") + } +} + #[derive(Clone, Debug, Default, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] pub struct Metadata { diff --git a/doc/src/doc/module.html b/doc/src/doc/module.html index 526d9ac78d..912bd26b99 100644 --- a/doc/src/doc/module.html +++ b/doc/src/doc/module.html @@ -63,7 +63,7 @@

-
type {{name}}{{#each args}} {{name}}{{/each}} = {{{type~}}}
+                            
{{attributes}}type {{name}}{{#each args}} {{name}}{{/each}} = {{{type~}}}
                             

@@ -88,7 +88,7 @@

-
let {{name}}
+                        
{{attributes}}let {{name}}
                             {{~#each args~}}
                                 {{~#if implicit}} ?{{name}}{{else}} {{name}}{{~/if~}}
                             {{~/each}} : {{{type~}}}
diff --git a/doc/src/lib.rs b/doc/src/lib.rs
index a58cadf672..f3a0c9f4d8 100644
--- a/doc/src/lib.rs
+++ b/doc/src/lib.rs
@@ -69,6 +69,7 @@ pub struct Field {
     pub args: Vec,
     #[serde(rename = "type")]
     pub typ: String,
+    pub attributes: String,
     pub comment: String,
     pub definition_line: Option,
 }
@@ -168,11 +169,16 @@ pub fn record(
             .type_field_iter()
             .filter(|field| !hidden(meta, field.name.as_ref()))
             .map(|field| {
+                let attributes;
                 let comment;
                 let definition_line;
 
                 match meta.module.get(AsRef::::as_ref(&field.name)) {
                     Some(meta) => {
+                        attributes = meta
+                            .attributes()
+                            .format_with("", |x, f| f(&format_args!("{}\n", x)))
+                            .to_string();
                         comment = meta
                             .comment
                             .as_ref()
@@ -182,6 +188,7 @@ pub fn record(
                         definition_line = None; // FIXME line_number(meta);
                     }
                     None => {
+                        attributes = "".to_string();
                         comment = "".to_string();
                         definition_line = None;
                     }
@@ -199,6 +206,7 @@ pub fn record(
                         })
                         .collect(),
                     typ: print_type(current_module, &field.typ.unresolved_type().remove_forall()),
+                    attributes,
                     comment,
                     definition_line,
                 }
@@ -210,11 +218,16 @@ pub fn record(
             .filter(|field| !hidden(meta, field.name.as_ref()))
             .map(|field| {
                 let args;
+                let attributes;
                 let comment;
                 let definition_line;
 
                 match meta.module.get(AsRef::::as_ref(&field.name)) {
                     Some(meta) => {
+                        attributes = meta
+                            .attributes()
+                            .format_with("", |x, f| f(&format_args!("{}\n", x)))
+                            .to_string();
                         args = meta
                             .args
                             .iter()
@@ -233,6 +246,7 @@ pub fn record(
                     }
                     _ => {
                         args = Vec::new();
+                        attributes = "".to_string();
                         comment = "".to_string();
                         definition_line = None;
                     }
@@ -242,6 +256,7 @@ pub fn record(
                     name: field.name.definition_name().to_string(),
                     args,
                     typ: print_type(current_module, &field.typ),
+                    attributes,
                     comment,
                     definition_line,
                 }
diff --git a/doc/tests/doc.rs b/doc/tests/doc.rs
index 7109389700..eac845febd 100644
--- a/doc/tests/doc.rs
+++ b/doc/tests/doc.rs
@@ -45,6 +45,7 @@ let test x = x
                     name: "x".to_string(),
                 }],
                 typ: handlebars::html_escape("forall a . a -> a"),
+                attributes: "".to_string(),
                 comment: "This is the test function".to_string(),
                 definition_line: None,
             }],

From 1183d218542d33d01f89994af9704859b39f760b Mon Sep 17 00:00:00 2001
From: Markus Westerlind 
Date: Sun, 6 Oct 2019 19:37:58 +0200
Subject: [PATCH 4/6] doc: Flesh out the std lib docs more

---
 std/alternative.glu              |  1 +
 std/assert.glu                   |  1 +
 std/effect.glu                   |  6 ++++++
 std/effect/alt.glu               | 25 ++++++++++++++-----------
 std/effect/error.glu             |  7 +++++++
 std/effect/lift.glu              |  5 +++++
 std/effect/reader.glu            |  6 ++++++
 std/effect/st.glu                | 23 +++++++++++++++++------
 std/effect/state.glu             | 10 ++++++++++
 std/effect/writer.glu            |  6 ++++++
 std/free.glu                     | 11 -----------
 std/http.glu                     |  2 ++
 std/identity.glu                 |  1 +
 std/{json/value.glu => json.glu} |  0
 std/json/de.glu                  |  2 +-
 std/json/ser.glu                 |  2 +-
 std/lazyt.glu                    |  1 +
 std/transformer.glu              |  1 +
 vm/src/api/json.rs               |  2 +-
 19 files changed, 81 insertions(+), 31 deletions(-)
 delete mode 100644 std/free.glu
 rename std/{json/value.glu => json.glu} (100%)

diff --git a/std/alternative.glu b/std/alternative.glu
index 6abb809f42..d1b6a43eb0 100644
--- a/std/alternative.glu
+++ b/std/alternative.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! Implementation of the `Alternative` type
 let { Applicative } = import! std.applicative
 
 /// A monoid on applicative functors.
diff --git a/std/assert.glu b/std/assert.glu
index e44ead4089..c608011d26 100644
--- a/std/assert.glu
+++ b/std/assert.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! Assertion functions.
 let { error } = import! std.prim
 
 let assert_msg x s = if x then () else error s
diff --git a/std/effect.glu b/std/effect.glu
index 18ee64ccbd..2c12d5dba7 100644
--- a/std/effect.glu
+++ b/std/effect.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! Composable effect types
 let { error } = import! std.prim
 let option = import! std.option
 let { Result, ? } = import! std.result
@@ -11,8 +12,10 @@ let { Applicative, wrap } = import! std.applicative
 let { Monad } = import! std.monad
 
 rec
+/// An effectful function `a -> b`
 type Arr r a b = a -> Eff r b
 
+/// The `Eff` monad provides composable effect via a `Row` of effects (`r`) and produces a value of type `a`.
 type Eff r a =
     | Pure a
     | Impure : forall x . r x -> Arr r x a -> Eff r a 
@@ -48,6 +51,9 @@ type OpenVariant r a = .. r
 #[doc(hidden)]
 let inject_rest x : forall e . OpenVariant r a -> [| | r |] a = convert_effect! x
 
+/// Extracts the value of type `a` from an effectful computation. Can only be done once all other
+/// effects have been eliminated from the row (leaving `[| |]` as the empty effect). See each
+/// individual effects module on how to eliminate the effect.
 let run_pure eff : Eff [| |] a -> a =
     match eff with
     | Pure v -> v
diff --git a/std/effect/alt.glu b/std/effect/alt.glu
index 4c3fd15f99..172bb0b6f0 100644
--- a/std/effect/alt.glu
+++ b/std/effect/alt.glu
@@ -1,9 +1,12 @@
+//! Implementation of the `Alt` effect
+
 let { Eff, inject_rest, ? } = import! std.effect
 let { map } = import! std.functor
 let { wrap } = import! std.applicative
 let { Alternative } = import! std.alternative
 let { (<<), id } = import! std.function
 
+/// The `Alt` effect lets `Eff` implement `Alternative`
 type Alt r a =
     | Empty
     .. r
@@ -12,9 +15,6 @@ let extract_alt x : forall s . [| alt : Alt | r |] a -> Alt r a = convert_varian
 
 let send_alt f : Alt r a -> Eff [| alt : Alt | r |] a = Impure (convert_effect! alt f) Pure
 
-let empty : forall s . Eff [| alt : Alt | r |] s =
-    send_alt Empty
-
 let run_alt_inner transform fail eff_1 eff_2 : (a -> b) -> (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a -> Eff [| | s |] b =
     let loop next ve : (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> _ =
         match ve with
@@ -28,7 +28,17 @@ let run_alt_inner transform fail eff_1 eff_2 : (a -> b) -> (() -> Eff [| | s |]
     let loop_2 _ = loop fail eff_2
     loop loop_2 eff_1
 
-///
+let empty : forall s . Eff [| alt : Alt | r |] s =
+    send_alt Empty
+
+let alternative : Alternative (Eff [| alt : Alt | r |]) = {
+    applicative = (import! std.effect).applicative,
+    empty = empty,
+    or = \l r -> run_alt_inner id (\_ -> empty) l r,
+}
+
+/// Eliminates the `Alt` effect returning `None` if the `Alt` effect is `empty`, otherwise returns `Some a` with the value
+/// 
 /// ```
 /// let { assert_eq, ? } = import! std.test
 /// let alt @ { ? } = import! std.effect.alt
@@ -49,17 +59,10 @@ let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a -> Eff [| alt : Alt | r |] a
     let fail _ = wrap None
     run_alt_inner Some fail eff_1 eff_2
 
-let alternative : Alternative (Eff [| alt : Alt | r |]) = {
-    applicative = (import! std.effect).applicative,
-    empty = empty,
-    or = \l r -> run_alt_inner id (\_ -> empty) l r,
-}
-
 {
     Alt,
 
     alternative,
 
-    empty,
     run_alt,
 }
diff --git a/std/effect/error.glu b/std/effect/error.glu
index 6fb453a045..efaab40a44 100644
--- a/std/effect/error.glu
+++ b/std/effect/error.glu
@@ -1,8 +1,11 @@
+//! Implementation of the `Error` effect
+
 let { Eff, inject_rest, ? } = import! std.effect
 let { Result } = import! std.result
 let { (<<) } = import! std.function
 let { wrap } = import! std.applicative
 
+/// The `Error` effects adds "exceptions" to the `Eff` monad
 type Error e r a =
     | Error : e -> Error e r a
     .. r
@@ -11,13 +14,16 @@ let send_error f : Error e r a -> Eff [| error : Error e | r |] a = Impure (conv
 
 let extract_error x : forall e . [| error : Error e | r |] a -> Error e r a = convert_variant! x
 
+/// Throws the error `e`
 let throw e : e -> Eff [| error : Error e | r |] a = send_error (Error e)
 
+/// Moves a `Result` into the `Eff` monad
 let ok_or_throw r : Result e t -> Eff [| error : Error e | r |] t =
     match r with
     | Ok t -> wrap t
     | Err e -> throw e
 
+/// Eliminates the `Error` effect and returns a `Result`
 let run_error eff : forall e . Eff [| error : Error e | r |] a -> Eff [| | r |] (Result e a) =
     let loop ve : Eff [| error : Error e | r |] a -> Eff [| | r |] (Result e a) =
         match ve with
@@ -30,6 +36,7 @@ let run_error eff : forall e . Eff [| error : Error e | r |] a -> Eff [| | r |]
                 Impure (inject_rest rest) (loop << f)
     loop eff
 
+/// Catches an "exception", allowing the effect to continue executing
 let catch eff handler : forall e . Eff [| error : Error e | r |] a -> (e -> Eff [| error : Error e | r |] a) -> Eff [| error : Error e | r |] a =
     let loop ve : Eff [| error : Error e | r |] a -> Eff [| error : Error e | r |] a =
         match ve with
diff --git a/std/effect/lift.glu b/std/effect/lift.glu
index 498737cce2..d3a52e7de1 100644
--- a/std/effect/lift.glu
+++ b/std/effect/lift.glu
@@ -1,18 +1,23 @@
 //@NO-IMPLICIT-PRELUDE
+//! Implementation of the `Lift` effect
 let { error } = import! std.prim
 let { Eff, inject_rest } = import! std.effect
 let { wrap } = import! std.applicative
 let { Monad, flat_map } = import! std.monad
 let { (<<) } = import! std.function
 
+/// The `Lift` effect allows a regular monad (usually `IO`) to be used in an `Eff` monad
 type Lift m r a = | Lift (m a) .. r
 
 let send_lift f : Lift m r a -> Eff [| lift : Lift m | r |] a = Impure (convert_effect! lift f) Pure
 
 let extract_state x : forall m . [| lift : Lift m | r |] a -> Lift m r a = convert_variant! x
 
+/// "Lifts" a monadic action into the `Eff` monad. Since monads do not compose this can only be
+// done once and `run_lift` must be the outermost effect eliminator
 let lift m : m a -> Eff [| lift : Lift m | r |] a = send_lift (Lift m)
 
+/// Eliminates the lifted monad `m`. Can only be used once all other effects have been eliminated
 let run_lift eff : [Monad m] -> Eff [| lift : Lift m |] a -> m a =
     let loop ve : Eff [| lift : Lift m |] a -> m a =
         match ve with
diff --git a/std/effect/reader.glu b/std/effect/reader.glu
index bb42ab1258..748f671233 100644
--- a/std/effect/reader.glu
+++ b/std/effect/reader.glu
@@ -1,8 +1,10 @@
+//! Implementation of the `Reader` effect
 let { Eff, inject_rest, ? } = import! std.effect
 let { map } = import! std.functor
 let { wrap } = import! std.applicative
 let { (<<) } = import! std.function
 
+/// The `Reader` effects provides a shared, immutable environment for the effectful functions using it
 type Reader s r a =
     | Ask : Reader s r s
     .. r
@@ -11,12 +13,15 @@ let extract_reader x : forall s . [| reader : Reader s | r |] a -> Reader s r a
 
 let send_reader f : Reader s r a -> Eff [| reader : Reader s | r |] a = Impure (convert_effect! reader f) Pure
 
+/// Retrieve the value from the environment
 let ask : forall s . Eff [| reader : Reader s | r |] s =
     send_reader Ask
 
+/// Retrieve the value from the environment while applying `f` to it
 let asks f : forall s . (s -> a) -> Eff [| reader : Reader s | r |] a =
     map f ask
 
+/// Runs a computation in a modified environment.
 let local f eff : forall s . (s -> s) -> Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a =
     do s = asks f
     let s : s = s // FIXME Remove after this does not affect inference
@@ -31,6 +36,7 @@ let local f eff : forall s . (s -> s) -> Eff [| reader : Reader s | r |] a -> Ef
                 Impure (inject_rest rest) (loop << f)
     loop eff
 
+/// Eliminates the `Reader` effect
 let run_reader s eff : forall s . s -> Eff [| reader : Reader s | r |] a -> Eff [| | r |] a =
     let loop reader ve : s -> Eff [| reader : Reader s | r |] a -> Eff [| | r |] a =
         match ve with
diff --git a/std/effect/st.glu b/std/effect/st.glu
index 0a181e84c7..46b32d6d24 100644
--- a/std/effect/st.glu
+++ b/std/effect/st.glu
@@ -1,3 +1,5 @@
+//! Implementation of the `st.State` effect
+
 let { Eff, inject_rest, ? } = import! std.effect
 let { map } = import! std.functor
 let { wrap } = import! std.applicative
@@ -5,6 +7,9 @@ let { (<<) } = import! std.function
 let { Reference, ref, (<-), load } = import! std.reference
 
 type STRef s a = { __ref : Reference a }
+
+/// The `State` effect enables the use of mutable state. By branding the state with `s` the mutable
+/// values are prevented from escaping the monad.
 type State s r a =
     | New : forall b . b -> State s r (STRef s b)
     | Read : STRef s a -> State s r a
@@ -17,10 +22,16 @@ let extract_state x : forall s . [| st : State s | r |] a -> State s r a = conve
 #[inline(never)]
 let send_state f : forall s . State s r a -> Eff [| st : State s | r |] a = Impure (convert_effect! st f) Pure
 
-let new_ST_ref a : forall s . a -> Eff [| st : State s | r |] (STRef s a) = send_state (New a)
-let read_ST_ref ref : forall s . STRef s a -> Eff [| st : State s | r |]  a =  send_state (Read ref)
-let write_ST_ref a ref : forall s . a -> STRef s a -> Eff [| st : State s | r |] () = send_state (Write a ref)
+/// Creates a new mutable reference that contains `a`.
+let new_ref a : forall s . a -> Eff [| st : State s | r |] (STRef s a) = send_state (New a)
+
+/// Reads the values stored in the reference.
+let read_ref ref : forall s . STRef s a -> Eff [| st : State s | r |]  a =  send_state (Read ref)
+
+/// Writes a new value into the reference.
+let write_ref a ref : forall s . a -> STRef s a -> Eff [| st : State s | r |] () = send_state (Write a ref)
 
+/// Eliminates the `State` effect
 let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a =
     let loop ve : forall s . Eff [| st : State s | r |] a -> Eff [| | r |] a =
         match ve with
@@ -43,8 +54,8 @@ let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a
 {
     State,
 
-    new_ST_ref,
-    read_ST_ref,
-    write_ST_ref,
+    new_ref,
+    read_ref,
+    write_ref,
     run_state,
 }
diff --git a/std/effect/state.glu b/std/effect/state.glu
index cffdb81b0f..f0c6ad5247 100644
--- a/std/effect/state.glu
+++ b/std/effect/state.glu
@@ -1,8 +1,11 @@
+//! Implementation of the `State` effect
+
 let { Eff, inject_rest, ? } = import! std.effect
 let { map } = import! std.functor
 let { wrap } = import! std.applicative
 let { (<<) } = import! std.function
 
+/// The `State` effect provides an updatable state
 type State s r a =
     | Get : State s r s
     | Put : s -> State s r ()
@@ -12,19 +15,24 @@ let extract_state x : forall s . [| state : State s | r |] a -> State s r a = co
 
 let send_state f : State s r a -> Eff [| state : State s | r |] a = Impure (convert_effect! state f) Pure
 
+/// Retreive the current value.
 let get : forall s . Eff [| state : State s | r |] s =
     send_state Get
 
+/// Retreive the current value and applied `f` to it.
 let gets f : forall s . (s -> a) -> Eff [| state : State s | r |] a =
     map f get
 
+/// Store `s` as the new value of the state.
 let put s : s -> Eff [| state : State s | r |] () =
     send_state (Put s)
 
+/// Update the state by applying `f`.
 let modify f : (s -> s) -> Eff [| state : State s | r |] () =
     do s = get
     put (f s)
 
+/// Eliminate the `State` effect and return the state and the computed value
 let run_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] { state : s, value : a} =
     let loop state ve : s -> Eff [| state : State s | r |] a -> Eff [| | r |] { state : s, value : a } =
         match ve with
@@ -39,9 +47,11 @@ let run_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [|
                 Impure (inject_rest rest) (loop state << f)
     loop s eff
 
+/// Eliminate the `State` effect and return the state
 let exec_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] s =
     map (\r -> r.state) (run_state s eff)
 
+/// Eliminate the `State` effect and return the computed value
 let eval_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] a =
     map (\r -> r.value) (run_state s eff)
 
diff --git a/std/effect/writer.glu b/std/effect/writer.glu
index 799e8225ee..4ddfe7b140 100644
--- a/std/effect/writer.glu
+++ b/std/effect/writer.glu
@@ -1,9 +1,12 @@
+//! Implementation of the `Writer` effect
+
 let { Eff, inject_rest, ? } = import! std.effect
 let monoid @ { Monoid } = import! std.monoid
 let { (<>) } = import! std.semigroup
 let { wrap } = import! std.applicative
 let { (<<) } = import! std.function
 
+/// The `Writer` effect allows the computations to output values of type `s`
 type Writer s r a =
     | Tell : s -> Writer s r ()
     .. r
@@ -14,9 +17,12 @@ let extract_writer x : forall s . [| writer : Writer s | r |] a -> Writer s r a
 #[inline(never)]
 let send_writer f : Writer s r a -> Eff [| writer : Writer s | r |] a = Impure (convert_effect! writer f) Pure
 
+/// Outputs `s`
 let tell s : forall s . s -> Eff [| writer : Writer s | r |] () =
     send_writer (Tell s)
 
+/// Eliminates `Writer`, returning the output and computed value. Each output through `tell` are
+/// joined via its `Monoid` instance
 let run_writer eff : forall s . [Monoid s] -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] { value : a, writer : s } =
     let loop writer ve : s -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] _ =
         match ve with
diff --git a/std/free.glu b/std/free.glu
deleted file mode 100644
index b65fa6dd91..0000000000
--- a/std/free.glu
+++ /dev/null
@@ -1,11 +0,0 @@
-let { List } = import! std.list
-
-type Val = | Val
-
-rec
-type Free f a = | Free (FreeView f Val Val) (List (ExpF f))
-type FreeView f a b = | Return a | Bind (f b) (b -> Free f a)
-type ExpF f = Val -> Free f Val
-
-
-{ Free }
diff --git a/std/http.glu b/std/http.glu
index 4d5d05941c..6fb6b2bc82 100644
--- a/std/http.glu
+++ b/std/http.glu
@@ -1,3 +1,5 @@
+//! A minimal library for writing HTTP servers.
+
 let prelude = import! std.prelude
 let function = import! std.function
 let { ? } = import! std.io
diff --git a/std/identity.glu b/std/identity.glu
index d1703a54fc..51fd8d502a 100644
--- a/std/identity.glu
+++ b/std/identity.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! The identity functor and monad.
 
 let { Functor } = import! std.functor
 let { Applicative } = import! std.applicative
diff --git a/std/json/value.glu b/std/json.glu
similarity index 100%
rename from std/json/value.glu
rename to std/json.glu
diff --git a/std/json/de.glu b/std/json/de.glu
index fb7b9701ce..18043ca8cc 100644
--- a/std/json/de.glu
+++ b/std/json/de.glu
@@ -2,7 +2,7 @@
 //!
 //! _This module is only available if gluon is compiled with the `serialization` feature._
 
-let { Value } = import! std.json.value
+let { Value } = import! std.json
 let prim = import! std.json.prim
 
 let { Result, ? } = import! std.result
diff --git a/std/json/ser.glu b/std/json/ser.glu
index 1abf3d3e4a..c6914dcfa2 100644
--- a/std/json/ser.glu
+++ b/std/json/ser.glu
@@ -2,7 +2,7 @@
 //!
 //! _This module is only available if gluon is compiled with the `serialization` feature._
 
-let { Value } = import! std.json.value
+let { Value } = import! std.json
 let prim = import! std.json.prim
 let { Result, ? } = import! std.result
 let { for } = import! std.traversable
diff --git a/std/lazyt.glu b/std/lazyt.glu
index 3420655f4e..f7cdbeeaa6 100644
--- a/std/lazyt.glu
+++ b/std/lazyt.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! Monad transformer version of `Lazy`
 
 let { Applicative, apply, wrap } = import! std.applicative
 let { (<<) } = import! std.function
diff --git a/std/transformer.glu b/std/transformer.glu
index 0425f6fff8..7e7d9d3a99 100644
--- a/std/transformer.glu
+++ b/std/transformer.glu
@@ -1,4 +1,5 @@
 //@NO-IMPLICIT-PRELUDE
+//! Utilities for writing `Monad` transformers
 
 let { Monad } = import! std.prelude
 
diff --git a/vm/src/api/json.rs b/vm/src/api/json.rs
index ebbb2784b9..847afd1f75 100644
--- a/vm/src/api/json.rs
+++ b/vm/src/api/json.rs
@@ -133,7 +133,7 @@ impl VmType for Value {
 }
 
 #[derive(VmType)]
-#[gluon(vm_type = "std.json.value.Value")]
+#[gluon(vm_type = "std.json.Value")]
 #[gluon(gluon_vm)]
 pub struct JsonValue(crate::vm::RootedValue);
 

From 347115cdd431d8cef883003c1a8eec7572afb7e8 Mon Sep 17 00:00:00 2001
From: Markus Westerlind 
Date: Mon, 7 Oct 2019 09:45:07 +0200
Subject: [PATCH 5/6] Fix travis testing

---
 scripts/travis.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/scripts/travis.sh b/scripts/travis.sh
index 401337f75f..b5217e2b1c 100755
--- a/scripts/travis.sh
+++ b/scripts/travis.sh
@@ -6,6 +6,8 @@ export RUST_BACKTRACE=1
 # Split the tests into two on travis so to avoid timing out
 
 if [ -z $NO_NORMAL_TEST ]; then
+    cargo test --features "test" --all "$@"
+    cargo test --features "test" --all --bins "$@"
     cargo test --features "test" --all --examples "$@"
     cargo test --features "test" --benches "$@" -- --test
     echo "" | cargo run --features "test" --example 24

From 3857668d164f327701f1f5329fe4a6241846f962 Mon Sep 17 00:00:00 2001
From: Markus Westerlind 
Date: Mon, 7 Oct 2019 10:22:07 +0200
Subject: [PATCH 6/6] Format repl.glu

---
 repl/src/repl.glu | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/repl/src/repl.glu b/repl/src/repl.glu
index b130d6ca58..f790092299 100644
--- a/repl/src/repl.glu
+++ b/repl/src/repl.glu
@@ -217,7 +217,8 @@ let do_command commands line : Commands -> String -> Eff (ReplEffect r) ReplActi
         | Some command -> command.action arg
         | None -> io.println ("Unknown command \'" ++ cmd ++ "\'") *> wrap Continue
     | Err err ->
-        io.println "Expected a command such as `:h`" *> wrap Continue
+        io.println "Expected a command such as `:h`"
+            *> wrap Continue
 
 let loop _ : () -> Eff (ReplEffect r) () =
     do repl = ask