module system: extensible option types
This commit is contained in:
parent
d10356b825
commit
e14de56613
@ -231,12 +231,20 @@ rec {
|
||||
correspond to the definition of 'loc' in 'opt.file'. */
|
||||
mergeOptionDecls = loc: opts:
|
||||
foldl' (res: opt:
|
||||
if opt.options ? default && res ? default ||
|
||||
opt.options ? example && res ? example ||
|
||||
opt.options ? description && res ? description ||
|
||||
opt.options ? apply && res ? apply ||
|
||||
# Accept to merge options which have identical types.
|
||||
opt.options ? type && res ? type && opt.options.type.name != res.type.name
|
||||
let t = res.type;
|
||||
t' = opt.options.type;
|
||||
mergedType = t.typeMerge t'.functor;
|
||||
typesMergeable = mergedType != null;
|
||||
typeSet = if (bothHave "type") && typesMergeable
|
||||
then { type = mergedType; }
|
||||
else {};
|
||||
bothHave = k: opt.options ? ${k} && res ? ${k};
|
||||
in
|
||||
if bothHave "default" ||
|
||||
bothHave "example" ||
|
||||
bothHave "description" ||
|
||||
bothHave "apply" ||
|
||||
(bothHave "type" && (! typesMergeable))
|
||||
then
|
||||
throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}."
|
||||
else
|
||||
@ -258,7 +266,7 @@ rec {
|
||||
in opt.options // res //
|
||||
{ declarations = res.declarations ++ [opt.file];
|
||||
options = submodules;
|
||||
}
|
||||
} // typeSet
|
||||
) { inherit loc; declarations = []; options = []; } opts;
|
||||
|
||||
/* Merge all the definitions of an option to produce the final
|
||||
@ -422,12 +430,14 @@ rec {
|
||||
options = opt.options or
|
||||
(throw "Option `${showOption loc'}' has type optionSet but has no option attribute, in ${showFiles opt.declarations}.");
|
||||
f = tp:
|
||||
let optionSetIn = type: (tp.name == type) && (tp.functor.wrapped.name == "optionSet");
|
||||
in
|
||||
if tp.name == "option set" || tp.name == "submodule" then
|
||||
throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}."
|
||||
else if tp.name == "attribute set of option sets" then types.attrsOf (types.submodule options)
|
||||
else if tp.name == "list or attribute set of option sets" then types.loaOf (types.submodule options)
|
||||
else if tp.name == "list of option sets" then types.listOf (types.submodule options)
|
||||
else if tp.name == "null or option set" then types.nullOr (types.submodule options)
|
||||
else if optionSetIn "attrsOf" then types.attrsOf (types.submodule options)
|
||||
else if optionSetIn "loaOf" then types.loaOf (types.submodule options)
|
||||
else if optionSetIn "listOf" then types.listOf (types.submodule options)
|
||||
else if optionSetIn "nullOr" then types.nullOr (types.submodule options)
|
||||
else tp;
|
||||
in
|
||||
if opt.type.getSubModules or null == null
|
||||
|
@ -92,7 +92,7 @@ rec {
|
||||
internal = opt.internal or false;
|
||||
visible = opt.visible or true;
|
||||
readOnly = opt.readOnly or false;
|
||||
type = opt.type.name or null;
|
||||
type = opt.type.description or null;
|
||||
}
|
||||
// (if opt ? example then { example = scrubOptionValue opt.example; } else {})
|
||||
// (if opt ? default then { default = scrubOptionValue opt.default; } else {})
|
||||
|
142
lib/types.nix
142
lib/types.nix
@ -17,10 +17,43 @@ rec {
|
||||
};
|
||||
|
||||
|
||||
# Default type merging function
|
||||
# takes two type functors and return the merged type
|
||||
defaultTypeMerge = f: f':
|
||||
let wrapped = f.wrapped.typeMerge f'.wrapped.functor;
|
||||
payload = f.binOp f.payload f'.payload;
|
||||
in
|
||||
# cannot merge different types
|
||||
if f.name != f'.name
|
||||
then null
|
||||
# simple types
|
||||
else if (f.wrapped == null && f'.wrapped == null)
|
||||
&& (f.payload == null && f'.payload == null)
|
||||
then f.type
|
||||
# composed types
|
||||
else if (f.wrapped != null && f'.wrapped != null) && (wrapped != null)
|
||||
then f.type wrapped
|
||||
# value types
|
||||
else if (f.payload != null && f'.payload != null) && (payload != null)
|
||||
then f.type payload
|
||||
else null;
|
||||
|
||||
# Default type functor
|
||||
defaultFunctor = name: {
|
||||
inherit name;
|
||||
type = types."${name}" or null;
|
||||
wrapped = null;
|
||||
payload = null;
|
||||
binOp = a: b: null;
|
||||
};
|
||||
|
||||
isOptionType = isType "option-type";
|
||||
mkOptionType =
|
||||
{ # Human-readable representation of the type.
|
||||
{ # Human-readable representation of the type, should be equivalent to
|
||||
# the type function name.
|
||||
name
|
||||
, # Description of the type, defined recursively by embedding the the wrapped type if any.
|
||||
description ? null
|
||||
, # Function applied to each definition that should return true if
|
||||
# its type-correct, false otherwise.
|
||||
check ? (x: true)
|
||||
@ -36,12 +69,26 @@ rec {
|
||||
getSubOptions ? prefix: {}
|
||||
, # List of modules if any, or null if none.
|
||||
getSubModules ? null
|
||||
, # Function for building the same option type with a different list of
|
||||
, # Function for building the same option type with a different list of
|
||||
# modules.
|
||||
substSubModules ? m: null
|
||||
, # Function that merge type declarations.
|
||||
# internal, takes a functor as argument and returns the merged type.
|
||||
# returning null means the type is not mergeable
|
||||
typeMerge ? defaultTypeMerge functor
|
||||
, # The type functor.
|
||||
# internal, representation of the type as an attribute set.
|
||||
# name: name of the type
|
||||
# type: type function.
|
||||
# wrapped: the type wrapped in case of compound types.
|
||||
# payload: values of the type, two payloads of the same type must be
|
||||
# combinable with the binOp binary operation.
|
||||
# binOp: binary operation that merge two payloads of the same type.
|
||||
functor ? defaultFunctor name
|
||||
}:
|
||||
{ _type = "option-type";
|
||||
inherit name check merge getSubOptions getSubModules substSubModules;
|
||||
inherit name check merge getSubOptions getSubModules substSubModules typeMerge functor;
|
||||
description = if description == null then name else description;
|
||||
};
|
||||
|
||||
|
||||
@ -52,29 +99,39 @@ rec {
|
||||
};
|
||||
|
||||
bool = mkOptionType {
|
||||
name = "boolean";
|
||||
name = "bool";
|
||||
description = "boolean";
|
||||
check = isBool;
|
||||
merge = mergeEqualOption;
|
||||
};
|
||||
|
||||
int = mkOptionType {
|
||||
name = "integer";
|
||||
int = mkOptionType rec {
|
||||
name = "int";
|
||||
description = "integer";
|
||||
check = isInt;
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
str = mkOptionType {
|
||||
name = "string";
|
||||
name = "str";
|
||||
description = "string";
|
||||
check = isString;
|
||||
merge = mergeOneOption;
|
||||
};
|
||||
|
||||
# Merge multiple definitions by concatenating them (with the given
|
||||
# separator between the values).
|
||||
separatedString = sep: mkOptionType {
|
||||
name = "string";
|
||||
separatedString = sep: mkOptionType rec {
|
||||
name = "separatedString";
|
||||
description = "string";
|
||||
check = isString;
|
||||
merge = loc: defs: concatStringsSep sep (getValues defs);
|
||||
functor = (defaultFunctor name) // {
|
||||
payload = sep;
|
||||
binOp = sepLhs: sepRhs:
|
||||
if sepLhs == sepRhs then sepLhs
|
||||
else null;
|
||||
};
|
||||
};
|
||||
|
||||
lines = separatedString "\n";
|
||||
@ -86,7 +143,8 @@ rec {
|
||||
string = separatedString "";
|
||||
|
||||
attrs = mkOptionType {
|
||||
name = "attribute set";
|
||||
name = "attrs";
|
||||
description = "attribute set";
|
||||
check = isAttrs;
|
||||
merge = loc: foldl' (res: def: mergeAttrs res def.value) {};
|
||||
};
|
||||
@ -114,8 +172,9 @@ rec {
|
||||
# drop this in the future:
|
||||
list = builtins.trace "`types.list' is deprecated; use `types.listOf' instead" types.listOf;
|
||||
|
||||
listOf = elemType: mkOptionType {
|
||||
name = "list of ${elemType.name}s";
|
||||
listOf = elemType: mkOptionType rec {
|
||||
name = "listOf";
|
||||
description = "list of ${elemType.description}s";
|
||||
check = isList;
|
||||
merge = loc: defs:
|
||||
map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def:
|
||||
@ -132,10 +191,12 @@ rec {
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: listOf (elemType.substSubModules m);
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
attrsOf = elemType: mkOptionType {
|
||||
name = "attribute set of ${elemType.name}s";
|
||||
attrsOf = elemType: mkOptionType rec {
|
||||
name = "attrsOf";
|
||||
description = "attribute set of ${elemType.description}s";
|
||||
check = isAttrs;
|
||||
merge = loc: defs:
|
||||
mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
|
||||
@ -147,6 +208,7 @@ rec {
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: attrsOf (elemType.substSubModules m);
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
# List or attribute set of ...
|
||||
@ -165,18 +227,21 @@ rec {
|
||||
def;
|
||||
listOnly = listOf elemType;
|
||||
attrOnly = attrsOf elemType;
|
||||
in mkOptionType {
|
||||
name = "list or attribute set of ${elemType.name}s";
|
||||
in mkOptionType rec {
|
||||
name = "loaOf";
|
||||
description = "list or attribute set of ${elemType.description}s";
|
||||
check = x: isList x || isAttrs x;
|
||||
merge = loc: defs: attrOnly.merge loc (imap convertIfList defs);
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: loaOf (elemType.substSubModules m);
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
# List or element of ...
|
||||
loeOf = elemType: mkOptionType {
|
||||
name = "element or list of ${elemType.name}s";
|
||||
loeOf = elemType: mkOptionType rec {
|
||||
name = "loeOf";
|
||||
description = "element or list of ${elemType.description}s";
|
||||
check = x: isList x || elemType.check x;
|
||||
merge = loc: defs:
|
||||
let
|
||||
@ -189,18 +254,22 @@ rec {
|
||||
else if !isString res then
|
||||
throw "The option `${showOption loc}' does not have a string value, in ${showFiles (getFiles defs)}."
|
||||
else res;
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
uniq = elemType: mkOptionType {
|
||||
inherit (elemType) name check;
|
||||
uniq = elemType: mkOptionType rec {
|
||||
name = "uniq";
|
||||
inherit (elemType) description check;
|
||||
merge = mergeOneOption;
|
||||
getSubOptions = elemType.getSubOptions;
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: uniq (elemType.substSubModules m);
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
nullOr = elemType: mkOptionType {
|
||||
name = "null or ${elemType.name}";
|
||||
nullOr = elemType: mkOptionType rec {
|
||||
name = "nullOr";
|
||||
description = "null or ${elemType.description}";
|
||||
check = x: x == null || elemType.check x;
|
||||
merge = loc: defs:
|
||||
let nrNulls = count (def: def.value == null) defs; in
|
||||
@ -211,6 +280,7 @@ rec {
|
||||
getSubOptions = elemType.getSubOptions;
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules = m: nullOr (elemType.substSubModules m);
|
||||
functor = (defaultFunctor name) // { wrapped = elemType; };
|
||||
};
|
||||
|
||||
submodule = opts:
|
||||
@ -236,6 +306,12 @@ rec {
|
||||
args = { name = ""; }; }).options;
|
||||
getSubModules = opts';
|
||||
substSubModules = m: submodule m;
|
||||
functor = (defaultFunctor name) // {
|
||||
# Merging of submodules is done as part of mergeOptionDecls, as we have to annotate
|
||||
# each submodule with its location.
|
||||
payload = [];
|
||||
binOp = lhs: rhs: [];
|
||||
};
|
||||
};
|
||||
|
||||
enum = values:
|
||||
@ -245,23 +321,35 @@ rec {
|
||||
else if builtins.isInt v then builtins.toString v
|
||||
else ''<${builtins.typeOf v}>'';
|
||||
in
|
||||
mkOptionType {
|
||||
name = "one of ${concatMapStringsSep ", " show values}";
|
||||
mkOptionType rec {
|
||||
name = "enum";
|
||||
description = "one of ${concatMapStringsSep ", " show values}";
|
||||
check = flip elem values;
|
||||
merge = mergeOneOption;
|
||||
functor = (defaultFunctor name) // { payload = values; binOp = a: b: unique (a ++ b); };
|
||||
};
|
||||
|
||||
either = t1: t2: mkOptionType {
|
||||
name = "${t1.name} or ${t2.name}";
|
||||
either = t1: t2: mkOptionType rec {
|
||||
name = "either";
|
||||
description = "${t1.description} or ${t2.description}";
|
||||
check = x: t1.check x || t2.check x;
|
||||
merge = mergeOneOption;
|
||||
typeMerge = f':
|
||||
let mt1 = t1.typeMerge (elemAt f'.wrapped 0).functor;
|
||||
mt2 = t2.typeMerge (elemAt f'.wrapped 1).functor;
|
||||
in
|
||||
if (name == f'.name) && (mt1 != null) && (mt2 != null)
|
||||
then functor.type mt1 mt2
|
||||
else null;
|
||||
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
|
||||
};
|
||||
|
||||
# Obsolete alternative to configOf. It takes its option
|
||||
# declarations from the ‘options’ attribute of containing option
|
||||
# declaration.
|
||||
optionSet = mkOptionType {
|
||||
name = builtins.trace "types.optionSet is deprecated; use types.submodule instead" "option set";
|
||||
name = builtins.trace "types.optionSet is deprecated; use types.submodule instead" "optionSet";
|
||||
description = "option set";
|
||||
};
|
||||
|
||||
# Augment the given type with an additional type check function.
|
||||
|
@ -65,4 +65,92 @@ options = {
|
||||
|
||||
</para>
|
||||
|
||||
<section xml:id="sec-option-declarations-eot"><title>Extensible Option
|
||||
Types</title>
|
||||
|
||||
<para>Extensible option types is a feature that allow to extend certain types
|
||||
declaration through multiple module files.
|
||||
This feature only work with a restricted set of types, namely
|
||||
<literal>enum</literal> and <literal>submodules</literal> and any composed
|
||||
forms of them.</para>
|
||||
|
||||
<para>Extensible option types can be used for <literal>enum</literal> options
|
||||
that affects multiple modules, or as an alternative to related
|
||||
<literal>enable</literal> options.</para>
|
||||
|
||||
<para>As an example, we will take the case of display managers. There is a
|
||||
central display manager module for generic display manager options and a
|
||||
module file per display manager backend (slim, kdm, gdm ...).
|
||||
</para>
|
||||
|
||||
<para>There are two approach to this module structure:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Managing the display managers independently by adding an
|
||||
enable option to every display manager module backend. (NixOS)</para>
|
||||
</listitem>
|
||||
<listitem><para>Managing the display managers in the central module by
|
||||
adding an option to select which display manager backend to use.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>Both approachs have problems.</para>
|
||||
|
||||
<para>Making backends independent can quickly become hard to manage. For
|
||||
display managers, there can be only one enabled at a time, but the type
|
||||
system can not enforce this restriction as there is no relation between
|
||||
each backend <literal>enable</literal> option. As a result, this restriction
|
||||
has to be done explicitely by adding assertions in each display manager
|
||||
backend module.</para>
|
||||
|
||||
<para>On the other hand, managing the display managers backends in the
|
||||
central module will require to change the central module option every time
|
||||
a new backend is added or removed.</para>
|
||||
|
||||
<para>By using extensible option types, it is possible to create a placeholder
|
||||
option in the central module (<xref linkend='ex-option-declaration-eot-service'
|
||||
/>), and to extend it in each backend module (<xref
|
||||
linkend='ex-option-declaration-eot-backend-slim' />, <xref
|
||||
linkend='ex-option-declaration-eot-backend-kdm' />).</para>
|
||||
|
||||
<para>As a result, <literal>displayManager.enable</literal> option values can
|
||||
be added without changing the main service module file and the type system
|
||||
automatically enforce that there can only be a single display manager
|
||||
enabled.</para>
|
||||
|
||||
<example xml:id='ex-option-declaration-eot-service'><title>Extensible type
|
||||
placeholder in the service module</title>
|
||||
<screen>
|
||||
services.xserver.displayManager.enable = mkOption {
|
||||
description = "Display manager to use";
|
||||
type = with types; nullOr (enum [ ]);
|
||||
};</screen></example>
|
||||
|
||||
<example xml:id='ex-option-declaration-eot-backend-slim'><title>Extending
|
||||
<literal>services.xserver.displayManager.enable</literal> in the
|
||||
<literal>slim</literal> module</title>
|
||||
<screen>
|
||||
services.xserver.displayManager.enable = mkOption {
|
||||
type = with types; nullOr (enum [ "slim" ]);
|
||||
};</screen></example>
|
||||
|
||||
<example xml:id='ex-option-declaration-eot-backend-kdm'><title>Extending
|
||||
<literal>services.foo.backend</literal> in the <literal>kdm</literal>
|
||||
module</title>
|
||||
<screen>
|
||||
services.xserver.displayManager.enable = mkOption {
|
||||
type = with types; nullOr (enum [ "kdm" ]);
|
||||
};</screen></example>
|
||||
|
||||
<para>The placeholder declaration is a standard <literal>mkOption</literal>
|
||||
declaration, but it is important that extensible option declarations only use
|
||||
the <literal>type</literal> argument.</para>
|
||||
|
||||
<para>Extensible option types work with any of the composed variants of
|
||||
<literal>enum</literal> such as
|
||||
<literal>with types; nullOr (enum [ "foo" "bar" ])</literal>
|
||||
or <literal>with types; listOf (enum [ "foo" "bar" ])</literal>.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
@ -62,23 +62,45 @@
|
||||
<listitem><para>A string. Multiple definitions are concatenated with a
|
||||
collon <literal>":"</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.separatedString</varname>
|
||||
<replaceable>sep</replaceable></term>
|
||||
<listitem><para>A string with a custom separator
|
||||
<replaceable>sep</replaceable>, e.g. <literal>types.separatedString
|
||||
"|"</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</section>
|
||||
|
||||
<section><title>Value Types</title>
|
||||
|
||||
<para>Value types are type that take a value parameter. The only value type
|
||||
in the library is <literal>enum</literal>.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>types.enum</varname> <replaceable>l</replaceable></term>
|
||||
<listitem><para>One element of the list <replaceable>l</replaceable>, e.g.
|
||||
<literal>types.enum [ "left" "right" ]</literal>. Multiple definitions
|
||||
cannot be merged.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.separatedString</varname>
|
||||
<replaceable>sep</replaceable></term>
|
||||
<listitem><para>A string with a custom separator
|
||||
<replaceable>sep</replaceable>, e.g. <literal>types.separatedString
|
||||
"|"</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.submodule</varname> <replaceable>o</replaceable></term>
|
||||
<listitem><para>A set of sub options <replaceable>o</replaceable>.
|
||||
<replaceable>o</replaceable> can be an attribute set or a function
|
||||
returning an attribute set. Submodules are used in composed types to
|
||||
create modular options. Submodule are detailed in <xref
|
||||
linkend='section-option-types-submodule' />.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section><title>Composed Types</title>
|
||||
|
||||
<para>Composed types allow to create complex types by taking another type(s)
|
||||
or value(s) as parameter(s).
|
||||
It is possible to compose types multiple times, e.g. <literal>with types;
|
||||
nullOr (enum [ "left" "right" ])</literal>.</para>
|
||||
<para>Composed types are types that take a type as parameter. <literal>listOf
|
||||
int</literal> and <literal>either int str</literal> are examples of
|
||||
composed types.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
@ -111,12 +133,6 @@
|
||||
merged. It is used to ensure option definitions are declared only
|
||||
once.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.enum</varname> <replaceable>l</replaceable></term>
|
||||
<listitem><para>One element of the list <replaceable>l</replaceable>, e.g.
|
||||
<literal>types.enum [ "left" "right" ]</literal>. Multiple definitions
|
||||
cannot be merged</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.either</varname> <replaceable>t1</replaceable>
|
||||
<replaceable>t2</replaceable></term>
|
||||
@ -125,14 +141,6 @@
|
||||
str</literal>. Multiple definitions cannot be
|
||||
merged.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>types.submodule</varname> <replaceable>o</replaceable></term>
|
||||
<listitem><para>A set of sub options <replaceable>o</replaceable>.
|
||||
<replaceable>o</replaceable> can be an attribute set or a function
|
||||
returning an attribute set. Submodules are used in composed types to
|
||||
create modular options. Submodule are detailed in <xref
|
||||
linkend='section-option-types-submodule' />.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</section>
|
||||
@ -191,7 +199,6 @@ options.mod = mkOption {
|
||||
type = with types; listOf (submodule modOptions);
|
||||
};</screen></example>
|
||||
|
||||
|
||||
<section><title>Composed with <literal>listOf</literal></title>
|
||||
|
||||
<para>When composed with <literal>listOf</literal>, submodule allows multiple
|
||||
@ -317,9 +324,13 @@ code before creating a new type.</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>name</varname></term>
|
||||
<listitem><para>A string representation of the type function name, name
|
||||
usually changes accordingly parameters passed to
|
||||
types.</para></listitem>
|
||||
<listitem><para>A string representation of the type function
|
||||
name.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>definition</varname></term>
|
||||
<listitem><para>Description of the type used in documentation. Give
|
||||
information of the type and any of its arguments.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>check</varname></term>
|
||||
@ -382,6 +393,53 @@ code before creating a new type.</para>
|
||||
type parameter, this function should be defined as <literal>m:
|
||||
composedType (elemType.substSubModules m)</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>typeMerge</varname></term>
|
||||
<listitem><para>A function to merge multiple type declarations. Takes the
|
||||
type to merge <literal>functor</literal> as parameter. A
|
||||
<literal>null</literal> return value means that type cannot be
|
||||
merged.</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><replaceable>f</replaceable></term>
|
||||
<listitem><para>The type to merge
|
||||
<literal>functor</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<para>Note: There is a generic <literal>defaultTypeMerge</literal> that
|
||||
work with most of value and composed types.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>functor</varname></term>
|
||||
<listitem><para>An attribute set representing the type. It is used for type
|
||||
operations and has the following keys:</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>type</varname></term>
|
||||
<listitem><para>The type function.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>wrapped</varname></term>
|
||||
<listitem><para>Holds the type parameter for composed types.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>payload</varname></term>
|
||||
<listitem><para>Holds the value parameter for value types.
|
||||
The types that have a <literal>payload</literal> are the
|
||||
<literal>enum</literal>, <literal>separatedString</literal> and
|
||||
<literal>submodule</literal> types.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>binOp</varname></term>
|
||||
<listitem><para>A binary operation that can merge the payloads of two
|
||||
same types. Defined as a function that take two payloads as
|
||||
parameters and return the payloads merged.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</section>
|
||||
|
@ -75,7 +75,10 @@ following incompatible changes:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para></para>
|
||||
<para>Module type system have a new extensible option types feature that
|
||||
allow to extend certain types, such as enum, through multiple option
|
||||
declarations of the same option across multiple modules.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
|
@ -256,7 +256,7 @@ if isOption opt then
|
||||
// optionalAttrs (opt ? default) { inherit (opt) default; }
|
||||
// optionalAttrs (opt ? example) { inherit (opt) example; }
|
||||
// optionalAttrs (opt ? description) { inherit (opt) description; }
|
||||
// optionalAttrs (opt ? type) { typename = opt.type.name; }
|
||||
// optionalAttrs (opt ? type) { typename = opt.type.description; }
|
||||
// optionalAttrs (opt ? options) { inherit (opt) options; }
|
||||
// {
|
||||
# to disambiguate the xml output.
|
||||
|
Loading…
Reference in New Issue
Block a user