As the nix language has no static types we use Type informations in doc-comments to communicate type expectations, behaviors and abstract interfaces.
In this short tutorial your will learn how to properly type a nix function, such that it can be discovered by noogle and most imporantantly understood by other developers.
Type | Description |
---|---|
Bool | A value of type boolean can be either true or false |
Int | The classical integer type |
Float | A float with 64 bits |
String | A string of any unicode characters |
Path | Path referencing a location or a file. With no assumptions on the existence of that |
Null | The value null |
Never | The bottom type. e.g. the function builtins.abort which returns Never |
T
, U
, ...; are placeholders for any types, those MUST be specifically declared on usage
Annotation | Type | Description |
---|---|---|
[ T ] | List | List of elements with type T each |
{ U::T } | AttrSet | AttrSet where member N references value of type T |
T -> U | Lambda | A function that takes a single argument of type T and returns a value of type U |
Sometimes common aliases for more complex types can be used.
They are composed from other types as follows
Type | Composition | Description |
---|---|---|
Derivation | see builtins.derivation | is just a special AttrSet. see builtins.derivation |
Any | ? | There is no Any type. Avoid using it. Use type variables (e.g. a -> b ) instead if you want to allow variable type signatures. |
Number | Int {or} Float | The Number is either of type Int or of type Float |
StorePath | Path | The StorePath is just a meaningful alias of the type Path |
These examples should give you a short feeling of correctly using type signatures.
bitOr :: Int -> Int -> Int
map :: (a -> b) -> [a] -> [b]
map :: (a -> b) -> [a] -> [b]
mapAttrs :: (String -> a -> b) -> { ${name} :: a } -> { ${name} :: b }
Advice
In some cases it is very hard and complex to describe all behaviors. Keep it simple.
Entering the last and likewise important chapter, 'Operators'
All Operators SHOULD be used with surrounding whitespace.
::
declares the typee.g. name :: String
()
ParenthesisParenthesis 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 }
Currently this is very inconsistent in nixpkgs.
Lets improve here
|
syntactic orsyntactic Or
can be used for composition or enums
Precedence: 2
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
.
...
- arbitrary input valuescan only be used within an AttrSet to allow any more name-value pairs
.
...
= ${rest} :: a
within an AttrSet context
Precedence: None
->
arrow operatorAllows for lambda types
Precedence: 1
?
optional arguments in an AttrSet--e.g. { opt ? :: Int }
Precedence: None