Skip to content
vic

adisbladis/korora

A tiny & fast type system for Nix in Nix

adisbladis/korora.json
{
"createdAt": "2023-12-14T11:20:54Z",
"defaultBranch": "master",
"description": "A tiny & fast type system for Nix in Nix",
"fullName": "adisbladis/korora",
"homepage": "",
"language": "Nix",
"name": "korora",
"pushedAt": "2025-11-15T08:37:33Z",
"stargazersCount": 94,
"topics": [],
"updatedAt": "2025-11-27T02:52:27Z",
"url": "https://github.com/adisbladis/korora"
}

A tiny & fast composable type system for Nix, in Nix.

Named after the little penguin.

  • Types
    • Primitive types (string, int, etc)
    • Polymorphic types (union, attrsOf, etc)
    • Struct types
  • Verification

Basic verification is done with the type function verify:

{ korora }:
let
t = korora.string;
value = 1;
# Error contains the string "Expected type 'string' but value '1' is of type 'int'"
error = t.verify 1;
in if error != null then throw error else value

Errors are returned as a string. On success null is returned.

  • Checking (assertions)

For convenience you can also check a value on-the-fly:

{ korora }:
let
t = korora.string;
value = 1;
# Same error as previous example, but `check` throws.
value = t.check value value;
in value

On error check throws. On success it returns the value that was passed in.

For usage example see [tests.nix]!(./tests.nix).

Declare a custom type using a bool function

name

: Name of the type as a string

verify

: Verification function returning a bool.

Declare a custom type using an option function.

name

: Name of the type as a string

verify

: Verification function returning null on success & a string with error message on error.

String

Type alias for string

Any

Never

Int

Single precision floating point

Either an int or a float

Bool

Attribute with undefined attribute types

Attribute with undefined element types

Function

Path

Derivation

Type

Option

t

: Null or t

listOf

t

: Element type

listOf

t

: Attribute value type

union<types…>

types

: Any of

intersection<types…>

types

: All of

rename<name, type>

Because some polymorphic types such as attrsOf inherits names from it’s sub-types we need to erase the name to not cause infinite recursion.

myType = types.attrsOf (
types.rename "eitherType" (types.union [
types.string
myType
])
);

name

: Function argument

type

: Function argument

struct<name, members…>

korora.struct "myStruct" {
foo = types.string;
}
  • Totality

By default, all attribute names must be present in a struct. It is possible to override this by specifying totality. Here is how to do this:

(korora.struct "myStruct" {
foo = types.string;
}).override { total = false; }

This means that a myStruct struct can have any of the keys omitted. Thus these are valid:

let
s1 = { };
s2 = { foo = "bar"; }
in ...
  • Unknown attribute names

By default, unknown attribute names are allowed.

It is possible to override this by specifying unknown.

(korora.struct "myStruct" {
foo = types.string;
}).override { unknown = false; }

This means that

{
foo = "bar";
baz = "hello";
}

is normally valid, but not when unknown is set to false.

Because Nix lacks primitive operations to iterate over attribute sets dynamically without allocation this function allocates one intermediate attribute set per struct verification.

  • Custom invariants

Custom struct verification functions can be added as such:

(types.struct "testStruct2" {
x = types.int;
y = types.int;
}).override {
verify = v: if v.x + v.y == 2 then "VERBOTEN" else null;
};

name

: Name of struct type as a string

members

: Attribute set of type definitions.

optionalAttr

t

: Function argument

enum<name, elems…>

name

: Name of enum type as a string

elems

: List of allowable enum members

defun<name, args, returns, function>

let
wrappedFunc = types.defun "fn" [ types.str ] types.str (s: s + "-checked");
in
# Returns "foo-checked"
wrappedfunc "foo"