Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Type Mapping

The type mapper converts Terraform’s go-cty types to NixOS module types. This is the core of the code generation pipeline – everything else is wiring.

Implementation: lib/TerranixCodegen/TypeMapper.hs

Mapping table

Primitives

TerraformNix
stringtypes.str
numbertypes.number
booltypes.bool
dynamictypes.anything

Collections

TerraformNixNotes
list(T)types.listOf (mapType T)
set(T)types.listOf (mapType T)Nix has no set type; mapped to list
map(T)types.attrsOf (mapType T)

Structural types

TerraformNix
object({...})types.submodule { options = {...}; }
tuple([...])types.tupleOf [...]

All mappings are recursive – a list(object({name = string})) produces types.listOf (types.submodule { options = { name = mkOption { type = types.str; }; }; }).

Objects

Terraform objects have typed fields, some of which may be optional:

CtyObject
  (Map.fromList [("host", CtyString), ("port", CtyNumber)])
  (Set.fromList ["port"])  -- optional fields

Generates:

types.submodule {
  options = {
    host = mkOption { type = types.str; };
    port = mkOption { type = types.nullOr types.number; };
  };
}

Optional object fields are wrapped in types.nullOr.

Tuples

Terraform tuples are fixed-length lists with per-position types. Nix has no built-in tuple type, so we provide a custom types.tupleOf that validates both length and per-element types.

CtyTuple [CtyString, CtyNumber, CtyBool]

Generates:

types.tupleOf [types.str types.number types.bool]

tupleOf is implemented as a proper mkOptionType with merge support, functor composition, and position-aware error messages.

Optional wrapping

The type mapper has two entry points:

  • mapCtyTypeToNix – returns the bare type
  • mapCtyTypeToNixWithOptional – wraps in types.nullOr when the attribute is optional or computed

This wrapping is applied at the attribute level by the option builder, not within nested type structures.

Attribute semantics

How Terraform’s required/optional/computed flags affect the generated option:

FlagsType wrappingDefaultreadOnly
requiredbare typenone (user must provide)no
optionaltypes.nullOr Tnullno
computed onlytypes.nullOr Tnullyes
optional + computedtypes.nullOr Tnullno

Block nesting modes

Nested blocks in Terraform schemas have a nesting mode that determines how they appear in configuration:

ModeNix typeDefault
singletypes.submodule { ... }null
grouptypes.submodule { ... }none
listtypes.listOf (types.submodule { ... })[]
settypes.listOf (types.submodule { ... })[]
maptypes.attrsOf (types.submodule { ... }){}

set maps to listOf for the same reason as collection sets – Nix doesn’t distinguish ordered from unordered at the type level.