{- | __Bricks__ is a lazy functional language that resembles . This module serves as fairly exhaustive overview of the entire package, and should usually serve as your go-to place to start when reading the Bricks documentation if you want an /in-depth/ understanding of how everything works. It is a fairly /large/ module, and probably ought to be used via a qualified import. > import qualified Bricks If you just want to use Bricks for common cases, look at the simple API in "Bricks.Prelude" instead. That module is much smaller and is designed to be imported unqualified. > import Bricks.Prelude -} module Bricks ( ------------------------------------------------- -- * Module overview -- $module-overview ------------------------------------------------- -- * Differences from Nix -- $differences-from-nix ------------------------------------------------- -- * Expressions Expression (..) , expression'source , expression'discardSource -- ** Rendering expressions , render'expression , render'expression'listContext , render'expression'dotLeftContext , render'expression'applyLeftContext , render'expression'applyRightContext , render'expression'inParens , render'expression'dictKey -- ** Parsing expressions , parse'expression , parse'expression'paren , parse'expression'antiquote , parse'expression'dictKey -- ** Parsing lists of expressions , parse'expressionList , parse'expressionList'1 , parse'expressionList'1'noDot ------------------------------------------------- -- * Variables , Var (..) , var'text , render'var , parse'var , var'to'str'static , var'to'str'dynamic , var'discardSource ------------------------------------------------- -- * Strings , str'escape , parse'str'within'normalQ , parse'str'escape'normalQ -- ** Static strings , Str'Static (..) , str'static'append , str'static'discardSource , str'static'to'dynamic , render'strStatic'unquotedIfPossible , render'strStatic'quoted , parse'strStatic , parse'strStatic'quoted , parse'strStatic'unquoted -- ** Dynamic strings , Str'Dynamic (..) , Str'1 (..) , str'1'discardSource , str'dynamic'append , str'dynamic'normalize , str'dynamic'discardSource , str'dynamic'to'static , render'strDynamic'unquotedIfPossible , render'strDynamic'quoted , render'str'1 , parse'str'dynamic -- ** Unquoted strings , UnquotedString , unquotedString'try , unquotedString'orThrow , unquotedString'text , text'canBeUnquoted , char'canBeUnquoted , parse'strUnquoted -- ** Indented strings , InStr (..) , InStr'1 (..) , inStr'1'toStrParts , inStr'toList , inStr'to'strDynamic , inStr'level , inStr'dedent , inStr'trim , inStr'discardSource , inStr'1'discardSource , render'str'indented , render'str'indented'1 , parse'inStr , parse'inStr'1 ------------------------------------------------- -- * Lists , List (..) , list'discardSource , render'list , parse'list ------------------------------------------------- -- * Dicts , Dict (..) , keyword'rec , dict'discardSource , render'dict , parse'dict , parse'dict'rec , parse'dict'noRec -- ** Dict bindings , DictBinding (..) , dictBinding'discardSource , render'dictBinding , parse'dictBinding , parse'dictBinding'inherit , parse'dictBinding'eq -- ** Dict lookup (dot) , Dot (..) , dot'discardSource , expression'applyDots , render'dot , parse'dot'rhs'chain ------------------------------------------------- -- * Functions -- ** Lambdas , Lambda (..) , lambda'discardSource , render'lambda , parse'lambda -- ** Function parameters , Param (..) , param'discardSource , render'param , parse'param , parse'param'var , parse'param'noVar -- ** Dict patterns , DictPattern (..) , dictPattern'discardSource , DictPattern'1 (..) , dictPattern'1'discardSource , render'dictPattern , render'dictPattern'1 , parse'dictPattern , parse'dictPattern'start -- ** Function application , Apply (..) , apply'discardSource , expression'applyArgs , render'apply ------------------------------------------------- -- * @let@ , Let (..) , let'discardSource , keyword'let , keyword'in , render'let , parse'let -- ** @let@ bindings , LetBinding (..) , letBinding'discardSource , render'letBinding , parse'letBinding , parse'letBinding'eq , parse'letBinding'inherit ------------------------------------------------- -- * @inherit@ , keyword'inherit ------------------------------------------------- -- * Keywords , Keyword , keywords , keywordString , keywordText , parse'keyword ------------------------------------------------- -- * Comments and whitespace , keyword'inlineComment , parse'spaces , parse'comment , parse'comment'inline , parse'comment'block ------------------------------------------------- -- * Rendering , Render , RenderContext (..) , renderContext'default , renderContext'terse ------------------------------------------------- ) where import Bricks.Expression import Bricks.Keyword import Bricks.Parsec import Bricks.Rendering import Bricks.UnquotedString {- $module-overview == Modules that are re-exported from "Bricks" The following modules are re-exported from this top-level "Bricks" module in their entireties. Modules related to syntax: - "Bricks.Keyword" - Enumerates the language's keywords - "Bricks.UnquotedString" - Defines the rules for what strings are allowed to appear unquoted in Bricks code - "Bricks.Expression" - Defines most of the types related to the AST, notably 'Expression' - "Bricks.Parsec" - Defines all of the Parsec parsers for parsing Bricks code into 'Expression's - "Bricks.Rendering" - Defines all of the renderers for turning 'Expression's into Bricks code Modules related to evaluation: - "Bricks.Term" - ... - "Bricks.ExpressionToTerm" - ... - "Bricks.Evaluation" - ... == Modules that are /not/ re-exported from "Bricks" - "Bricks.Expression.Construction" - Functions for constructing 'Expression's in a way that matches their 'Show' implementations. -} {- $differences-from-nix Bricks is heavily based on , but there are a number of significant differences. Most of the differences involve the /removal/ of some feature for the sake of simplicity in both implementation and use. This list is not comprehensive. == Top-level Bricks expressions may contain no free variables There are a number of cases where variables are allowed to appear free in a top-level Nix expression: @true@, @false@, @null@, @builtins@, and anything within the body of a @with@ expression (sort of - we'll elaborate on that complicated issue further below). None of those cases are present in Bricks: __/All variables must be explicitly bound/__. Throughout this section we will more thoroughly address specific Nix built-in variables and how to translate Nix expressions that use them into equivalent Bricks code. == Bricks has no built-in Boolean values Nix has built-in @true@ and @false@ variables, a handful of operators on them (@&&@, @||@, @!@), and an @if@-@then@-@else@ construct. > nix-repl> true && false > false > nix-repl> true || false > true > nix-repl> !true > false > nix-repl> if true then "a" else "b" > "a" None of these features are present in Bricks. __todo:__ Show how we can use the standard library instead. == Bricks has no built-in @null@ __todo:__ Show how we can use the standard library instead. == Bricks has no integer literals __todo:__ Show how we can use the standard library instead. == Bricks has no built-in @builtins@ __todo:__ Show how we can use the standard library instead. == Bricks has no infix operators (@+@, @-@, @//@, et cetera) __todo:__ Show how we can use the standard library instead. == Bricks has no @with@ expression The Nix language has a @with@ construct which introduces the contents of a dict into the lexical scope. The simple reason for omitting this feature is that it can easily lead to code that is difficult to trace. When @with@ expressions are nested, it is often unclear where a variable has come into scope from. The more subtle reason not to include the @with@ construct is that it introduces a significant departure from the lambda calulus. Consider the expression @(with d; x)@. In this expression, is @x@ free or bound? It is neither; it exists in some state of uncertainty where it /may or may not be/ bound, depending on the value of @d@. This has a practical consequence: When you use a @with@ expression, you sacrifice referential transparency. Consider the following Nix expression: > nix-repl> let v = (with { x = "a"; }; x); in (x: v) "b" > "a" If we attempt to reduce this expression by replacing @v@ with its definition, > nix-repl> (x: (with { x = "a"; }; x)) "b" > "b" then it no longer evaluates to the same value. We find this unacceptable. Bricks avoids the problem by simply not implementing this feature. == Bricks allows a list on the right-hand side of the @.@ operator The following example is syntactically valid Nix code, but it fails to evaluate: > nix-repl> { x = "a"; y = "b"; }.${[ "x" "y" ]} > error: value is a list while a string was expected We expand the meaning of the @.@ operator such that if the expression on the right-hand side evaluates to a list, then the entire expression evaluates to a list: > bricks-repl> { x = "a"; y = "b"; }.${[ "x" "y" ]} > [ "a" "b" ] Furthermore, when the expression on the right-hand side is a list literal (an expression of the form @[@ ... @]@), the antiquotation (wrapping the expression in @${@ ... @}@) may be omitted: > bricks-repl> { x = "a"; y = "b"; }.[ "x" "y" ] > [ "a" "b" ] This provides a convenient alternative to many situations in which one might use the @with@ keyword in Nix. For example, where in Nix we might write > ghcWithPackages (p: with p; [ base containers text ]); we may write this equivalently in Bricks as > ghcWithPackages (p: p.[ "base" "containers" "text" ]); == Bricks does not have URI literals If a string literal is a URI, it can be written in Nix without quotes. We have chosen not to include this feature because it provides very little convenience and steals some syntax from lambda expressions. Consider the following Nix example: > nix-repl> (let x = "a"; in y: x) "b" > "a" If we remove the space between after the colon (@:@), we get something entirely different: > nix-repl> (let x = "a"; in y:x) "b" > error: attempt to call something which is not > a function but a string, at (string):1:1 Because @y:x@ contains a colon, Nix interprets it as a URI and parses it as the string @"y:x"@ (this is the "string" to which the error message refers), rather than as a lambda. In Bricks, by contrast, the colon in a lambda is /not/ required to be followed by whitespace, and the previous example works as we would like. > bricks-repl> (let x = "a"; in y:x) "b" > "a" == Bricks does not have path literals In Nix, an unquoted string that contains a slash is interpreted as a filesystem path. Path literals have some subtle syntax rules. A common mistake is forgetting to always include a slash in the path. For an example, @./foo.nix@ is a URI: > nix-repl> ./foo.nix > /home/chris/foo.nix But @foo.nix@, without the leading @./@, is parsed differently: > nix-repl> foo.nix > error: undefined variable ‘foo’ at (string):1:1 As with URI literals, we find that the unquoted form for paths does not provide enough convenience to compensate for its potential for confusion, so we have opted to omit it. == Bricks does not have a built-in @import@ function In Nix, a path literal that does not start with a slash (such as @./foo.nix@) is interpreted as a /relative/ path, and the Nix parser immediately resolves it with respect to the directory in which the Nix file resides (as we saw in the example above, where it resolved to @\/home\/chris\/foo.nix@). We love being able to use relative imports, but we don't like needing a built-in language feature to do it. Fortunately, Bricks can achieve the same effect by using an ordinary function instead. __todo:__ Explain how imports work in the standard library, once it is implemented. In doing this, we buy back some purity that Nix's @import@ lacks. By passing the path argument through a function parameter, rather than deriving it implicitly from the context of "which file did the expression /come from/?" we eliminate a case where an expression's meaning depends on something other than the values of the formal parameters that bind its free variables. There is another benefit to the Bricks approach: While Nix import syntax is restricted to static paths only (the argument to @import@ cannot contain free variables), Bricks has no such limitation. == Bricks uses Haskell-style comments The Bricks inline comment keyword is @--@; in Nix it is @#@. The Bricks block comment form is @{\-@ ... @-\}@; in Nix it is @/*@ ... @*/@. This decision was made merely due to the Bricks authors' aesthetic preference and affinity for Haskell. == Bricks block comments may be nested Although Nix does have block comments, > nix-repl> /* */ "a" > "a" Nix does not support /nested/ block comments: > nix-repl> /* /* */ */ "a" > error: syntax error, unexpected '*', at (string):1:10 Bricks does: @ bricks-repl> {\- {\- -\} -\} "a" "a" @ == Bricks does not support escape sequences in indented strings Within the indented string form (@''@ ... @''@), Nix supports the following unorthodox escape sequences: - @''${@ → @${@ - @'''@ → @''@ - @''\\n@ → newline - @''\\r@ → carriage return - @''\\t@ → tab > nix-repl> ''ab''\ncd'' > "ab\ncd" Bricks does not support any of these. If you want to include any of these strings within an indented string, you can use antiquotation: > bricks-repl> ''ab${"\n"}cd'' > "ab\ncd" Or you can interpret escape sequences at runtime by passing your string through some function in the standard library that does this sort of thing (__todo:__ discuss said function, once it exists). == Bricks does not allow quotes in /let/ bindings In Nix, the left-hand side of a /let/ binding is allowed to be a quoted string. This lets you create variables that aren't valid as variable expressions (when you /refer to/ a variable, it may /not/ be quoted), which puts you in a weird sitation where there is a variable in scope which can only be referred to by inheriting it into a dict. > nix-repl> let "a b" = "c"; in { inherit "a b"; } > { "a b" = "c"; } This oddity does not seem to serve any real purpose, so we have omitted it. == The Nix "set" concept is renamed to "dict" in Bricks The Nix concept of "set" is referred to as "dict" in Bricks. This is not actually a language difference; we just use a different word to talk about the same thing. We believe that "dict" is a more familiar term for this data structure, and that Nix's use of "set" conflicts unnecessarily with the more common usage of the word. -}