Created
August 5, 2023 15:20
-
-
Save thufschmitt/c3a89306f87185b061c3e7d06dcfa4dd to your computer and use it in GitHub Desktop.
Modules for Nickel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A library for NixOS-style “modules” | |
# | |
# Modules are records whose contract gets extended through merging: `Module c` | |
# is a contract semantically equivalent to `c`, except that if `foo` has | |
# contract `Module x` and `bar` has contract `Module y`, then `foo & bar` will | |
# have contract `Module (x&y)`. | |
{ | |
empty = { contract.contract | optional }, | |
Module | |
| doc m%" | |
Define a module contract | |
# Example | |
```nickel | |
{ foo = 1, bar = 2 } | Module { foo | Number, bar | Number } | |
``` | |
"% | |
= fun module_contract label value => | |
(value & { contract = module_contract }), | |
ValidModule | |
| doc m%" | |
Extensible records which define their own contract. | |
The contract for a record is defined by its `contract` field. | |
This allows modules to have a contract that's both closed and extensible by | |
merging (by extending the `contract` field). | |
# Examples | |
```nickel | |
let m1 = { contract.foo | Number, foo = 1 } in | |
let m2 = { contract.bar | String, bar = "bar" } in | |
(m1 & m2) | ValidModule | |
"% | |
= fun label value => | |
let with_ensured_contract = value & empty in | |
std.contract.apply | |
with_ensured_contract.contract | |
label | |
with_ensured_contract, | |
SubModule = fun label value => std.contract.apply ValidModule label value |> fix, | |
fix | ValidModule -> Dyn | |
# | std.contract.unstable.DependentFun ValidModule (fun x => std.record.remove "contract" x.contract) | |
= fun module => std.record.remove "contract" (module & { contract | force = null }), | |
test.simple = fix ({ foo = 1 } | Module { foo | Number }), | |
test."merge" = | |
let m1 | Module { foo | Number } = { foo = 1 } in | |
let m2 | Module { bar | String } = { bar = "blah" } in | |
fix (m1 & m2), | |
test.sub = | |
let sub | Module { y | Number, z | Number | default = y+1 } = {} in | |
fix | |
( | |
{ | |
foo.x = sub & { y = 1 }, | |
foo.z = sub & { y = 1 }, | |
foo.z.z = 3, | |
} | Module { | |
foo | {_: SubModule} | |
} | |
), | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment