Operators
All Operators SHOULD be used with surrounding whitespace.
:: declares the type
The variable name on the LHS is declared to have the type on the RHS
e.g. name :: String
In attribute set contexts ::
defines both the type of a and b as illustrated below.
def pair=λa.λb.λf.((f a) b)
<=>
{ "foo" = 1; }
typeOf { "foo" = 1; }
=>
{ String :: Int; } # Example type conversion may not closely represent the type conversion done by a real type engine.
def first p=λa.λb.a
first { String :: Int; }
=>
String
def second p=λa.λb.b
second { String :: Int; }
=>
Int
() Parenthesis
Parenthesis to clarify order of type evaluation
e.g. ( a -> b ) | Bool
Precedence: (Highest)
; Separator for subsequent entries (like in AttrSet)
e.g. { foo :: Number; bar :: String }
Important: This is a required syntax rule! It terminates the type expression.
Currently this is very inconsistent in nixpkgs.
| syntactic or
syntactic Or can be used for composition or enums
Let T and U be different Types.
Then the | operator evaluates to either T or U.
Sometimes within this paper
|is written as{or}This is only due to readability and not allowed in the real language
Precedence: 2
Triviality / special Cases
Any | a
Is always Any; Because any other type a must already be a subtype of any, due to the definition of Any.
b | Never
Is always b; Due to the definition of Never; b must be a supertype of Never.
Examples
Float | Int
( Number | Bool ) | Int
{ opt :: Int | String }
# lets say we want to create a type which can represent the type of both 'foo' and 'bar'
let
/*
Type:
FooBar = Int | String
foo :: FooBar
*/
foo = 1;
/*
Type:
bar :: FooBar
*/
bar = "baz";
in
# ....
& conjunction operator
TODO
Triviality / special Cases
Any & a
Is always a
b & Never
Is always Never
... - arbitrary input values
can only be used within an AttrSet to allow Any more name-value pairs.
... = ${rest} :: a within an AttrSet context
e.g.
/*
Type: foo :: { bar :: a, ...} -> a
*/
Foo = {bar, ...}@inp:
#...
Precedence: None
// merge operator
syntactically merges Types of AttrSets
{ foo :: String } // { bar :: Number } => { foo :: String; bar :: Number }
{ foo :: String } // { ${names} :: a } => { foo :: String; ${names} :: a }
Overwrites occur like in the nix language itself
{ foo :: String } // { foo :: Number } => { foo :: Number }
Precedence: 3
-> arrow operator
Allows for lambda types
Precedence: 1
? optional arguments in an AttrSet
--e.g. { opt :: Int ? }
Note: The very end of type side contains the ? operator.
The reason for this is extendability. Initially the ?-Operator is introduced as Unary-operator, marking the entry type as optional.
In future it will be desirable to use the ? as binary-operator to add default values on optional entries.
e.g.
{
foo :: Int ? 1;
}
Precedence: None
Precedence
Precedence is just taken from the official nix operators. To make usage as intuitive as possible. However the type language only uses a tight subset of the operator available in nix.
-> see grammar