From c587d3aaff90064519f25627735a70d98c08baf5 Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Mon, 9 Sep 2024 12:42:33 +0200 Subject: [PATCH] Update to latest topiary-queries (formatting) (#2032) * Update to latest topiary-queries Formatting of Nickel code has been updated upstream to better anticipate the usage of `std.contract.custom` and `std.contract.from_predicate`. This commit updates to the latest version, and update the formatting of the stdlib, the examples and the snippets of the manual. * Update topiary's entry in flake.lock as well * Format stdlib/internals and benchmarks --- Cargo.lock | 4 +- Cargo.toml | 2 +- ..._eval_stderr_array_at_empty_array.ncl.snap | 4 +- ...eval_stderr_array_at_out_of_bound.ncl.snap | 4 +- ...derr_array_range_reversed_indices.ncl.snap | 4 +- ...rr_array_range_step_negative_step.ncl.snap | 4 +- ..._stderr_caller_contract_violation.ncl.snap | 4 +- core/benches/functions/church.ncl | 8 +- core/benches/mantis/deploy-example.ncl | 11 +- core/benches/mantis/deploy.ncl | 11 +- core/benches/mantis/lib.ncl | 32 +- core/benches/mantis/schemas/nomad/types.ncl | 1107 ++++++++--------- core/benches/nixpkgs/lists.ncl | 37 +- core/stdlib/internals.ncl | 54 +- core/stdlib/std.ncl | 662 +++++----- doc/manual/contracts.md | 184 ++- doc/manual/correctness.md | 44 +- doc/manual/syntax.md | 10 +- doc/manual/tutorial.md | 50 +- doc/manual/types-vs-contracts.md | 1 + doc/manual/typing.md | 45 +- examples/config-gcc/config-gcc.ncl | 88 +- .../foreach-pattern-on-import.ncl | 14 +- examples/foreach-pattern/foreach-pattern.ncl | 14 +- examples/record-contract/record-contract.ncl | 14 +- .../simple-contracts/simple-contract-div.ncl | 16 +- flake.lock | 6 +- 27 files changed, 1146 insertions(+), 1288 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b838fbb1c..000edc4538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3131,9 +3131,9 @@ dependencies = [ [[package]] name = "topiary-queries" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89106b9c504a9e3247b8e40f879a73a922f0d516e1051a6ef739b4cf7d54c8d9" +checksum = "103bcbfd5c605f2ef94074ae4950473f9dcf159f8bb01959a13856f96a2ffc8c" [[package]] name = "topiary-tree-sitter-facade" diff --git a/Cargo.toml b/Cargo.toml index edf8872f80..c3d1f4d903 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ metrics = "0.21" metrics-util = "0.15" topiary-core = "0.4.0" -topiary-queries = { version = "0.4.1", default-features = false, features = ["nickel"] } +topiary-queries = { version = "0.4.2", default-features = false, features = ["nickel"] } # This version should agree with the topiary-query version: Nickel queries # target a specific version of the Nickel grammar (though that dependency # doesn't appear explicitly in `topiary-queries`'s Cargo.toml file). For now you diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_empty_array.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_empty_array.ncl.snap index 401ef8b1b5..ab6e249b8b 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_empty_array.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_empty_array.ncl.snap @@ -4,9 +4,9 @@ expression: err --- error: contract broken by the caller of `at` invalid array indexing - ┌─ :166:9 + ┌─ :164:9 │ -166 │ | std.contract.unstable.IndexedArrayFun 'Index +164 │ | std.contract.unstable.IndexedArrayFun 'Index │ -------------------------------------------- expected type │ ┌─ [INPUTS_PATH]/errors/array_at_empty_array.ncl:3:16 diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_out_of_bound.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_out_of_bound.ncl.snap index 7e00925c79..1f42c95a48 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_out_of_bound.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_at_out_of_bound.ncl.snap @@ -4,9 +4,9 @@ expression: err --- error: contract broken by the caller of `at` invalid array indexing - ┌─ :166:9 + ┌─ :164:9 │ -166 │ | std.contract.unstable.IndexedArrayFun 'Index +164 │ | std.contract.unstable.IndexedArrayFun 'Index │ -------------------------------------------- expected type │ ┌─ [INPUTS_PATH]/errors/array_at_out_of_bound.ncl:3:16 diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap index 423b2001ae..a06c9f1260 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_reversed_indices.ncl.snap @@ -4,9 +4,9 @@ expression: err --- error: contract broken by the caller of `range` invalid range - ┌─ :757:9 + ┌─ :755:9 │ -757 │ | std.contract.unstable.RangeFun Dyn +755 │ | std.contract.unstable.RangeFun Dyn │ ---------------------------------- expected type │ ┌─ [INPUTS_PATH]/errors/array_range_reversed_indices.ncl:3:19 diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap index 2d1ed5e294..a0750a16f1 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_array_range_step_negative_step.ncl.snap @@ -4,9 +4,9 @@ expression: err --- error: contract broken by the caller of `range_step` invalid range step - ┌─ :732:9 + ┌─ :730:9 │ -732 │ | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn) +730 │ | std.contract.unstable.RangeFun (std.contract.unstable.RangeStep -> Dyn) │ ----------------------------------------------------------------------- expected type │ ┌─ [INPUTS_PATH]/errors/array_range_step_negative_step.ncl:3:27 diff --git a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_caller_contract_violation.ncl.snap b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_caller_contract_violation.ncl.snap index d958d9dcf9..e16b46d0ff 100644 --- a/cli/tests/snapshot/snapshots/snapshot__eval_stderr_caller_contract_violation.ncl.snap +++ b/cli/tests/snapshot/snapshots/snapshot__eval_stderr_caller_contract_violation.ncl.snap @@ -4,9 +4,9 @@ expression: err --- error: contract broken by the caller of `map` expected an array - ┌─ :150:33 + ┌─ :148:33 │ -150 │ : forall a b. (a -> b) -> Array a -> Array b +148 │ : forall a b. (a -> b) -> Array a -> Array b │ ------- expected type of the argument provided by the caller │ ┌─ [INPUTS_PATH]/errors/caller_contract_violation.ncl:3:31 diff --git a/core/benches/functions/church.ncl b/core/benches/functions/church.ncl index 522a177980..ac0f371307 100644 --- a/core/benches/functions/church.ncl +++ b/core/benches/functions/church.ncl @@ -19,11 +19,9 @@ let encoded = base |> std.array.map encode - |> std.array.map - ( - fun nChurch => - add (mult (encode 3) nChurch) (encode 5) - ) + |> std.array.map (fun nChurch => + add (mult (encode 3) nChurch) (encode 5) + ) |> std.array.fold_right add (encode 0) in diff --git a/core/benches/mantis/deploy-example.ncl b/core/benches/mantis/deploy-example.ncl index 8b55b2fa6f..a1b1002c62 100644 --- a/core/benches/mantis/deploy-example.ncl +++ b/core/benches/mantis/deploy-example.ncl @@ -1,6 +1,5 @@ -(import "deploy.ncl") - { - namespace = "mantis-staging", - job = "miner", - role = 'miner, - } +(import "deploy.ncl") { + namespace = "mantis-staging", + job = "miner", + role = 'miner, +} diff --git a/core/benches/mantis/deploy.ncl b/core/benches/mantis/deploy.ncl index 5f628a41f1..225b08d083 100644 --- a/core/benches/mantis/deploy.ncl +++ b/core/benches/mantis/deploy.ncl @@ -41,12 +41,11 @@ fun vars => namespace = vars.namespace, datacenters | Array ( - lib.contracts.OneOf - [ - "eu-central-1", - "us-east-2", - "eu-west-2" - ] + lib.contracts.OneOf [ + "eu-central-1", + "us-east-2", + "eu-west-2" + ] ) | default = ["eu-central-1", "us-east-2"], diff --git a/core/benches/mantis/lib.ncl b/core/benches/mantis/lib.ncl index 345d6dfd37..7bf429d1b3 100644 --- a/core/benches/mantis/lib.ncl +++ b/core/benches/mantis/lib.ncl @@ -2,23 +2,20 @@ contracts = { AllOf = fun contracts label value => std.array.fold_right - ( - fun c acc => - c - label - acc + (fun c acc => + c + label + acc ) contracts value, Dummy = fun label value => value, OneOf = fun values => - std.contract.from_predicate - ( - fun value => - std.array.elem - value - values - ), + std.contract.from_predicate (fun value => + std.array.elem + value + values + ), Nullable = fun ctr label value => if value == null then null @@ -40,12 +37,11 @@ std.contract.blame_with_message "not a string" label, PseudoOr = fun alts label value => std.array.fold_right - ( - fun ctr rest => - if ctr.pred value then - ctr.contract value - else - rest + (fun ctr rest => + if ctr.pred value then + ctr.contract value + else + rest ) (std.contract.blame_with_message "no alternative matched" label), OrableFromPred : (Dyn -> Bool) -> { pred : Dyn -> Bool, contract : Dyn -> Dyn -> Dyn }, diff --git a/core/benches/mantis/schemas/nomad/types.ncl b/core/benches/mantis/schemas/nomad/types.ncl index 2997de91cb..3b7eaa2eac 100644 --- a/core/benches/mantis/schemas/nomad/types.ncl +++ b/core/benches/mantis/schemas/nomad/types.ncl @@ -58,23 +58,22 @@ in RTargety | String, Operand | ( - lib.contracts.OneOf - [ - "regexp", - "set_contains_all", - "set_contains", - "set_contains_any", - "=", - "==", - "is", - "!=", - "not", - ">", - ">=", - "<", - "<=", - "version" - ] + lib.contracts.OneOf [ + "regexp", + "set_contains_all", + "set_contains", + "set_contains_any", + "=", + "==", + "is", + "!=", + "not", + ">", + ">=", + "<", + "<=", + "version" + ] ), Weight | std.number.Nat @@ -92,37 +91,34 @@ in RTarget | String, Operand | ( - lib.contracts.OneOf - [ - "regexp", - "set_contains", - "distinct_hosts", - "distinct_property", - "=", - "==", - "is", - "!=", - "not", - ">", - ">=", - "<", - "<=" - ] + lib.contracts.OneOf [ + "regexp", + "set_contains", + "distinct_hosts", + "distinct_property", + "=", + "==", + "is", + "!=", + "not", + ">", + ">=", + "<", + "<=" + ] ), }, Spread = { Attribute | String, Weight - | lib.contracts.Nullable - ( - lib.contracts.AllOf - [ - std.number.Nat, - lib.contracts.GreaterEq - 100, - lib.contracts.LesserEq 100 - ] - ) + | lib.contracts.Nullable ( + lib.contracts.AllOf [ + std.number.Nat, + lib.contracts.GreaterEq - 100, + lib.contracts.LesserEq 100 + ] + ) | default = null, SpreadTarget @@ -134,14 +130,12 @@ in SpreadTargetElem = { Value | String, Percent - | lib.contracts.Nullable - ( - lib.contracts.AllOf - [ - std.number.PosNat, - lib.contracts.LesserEq 100 - ] - ) + | lib.contracts.Nullable ( + lib.contracts.AllOf [ + std.number.PosNat, + lib.contracts.LesserEq 100 + ] + ) | default = null, }, @@ -162,17 +156,16 @@ in | default = false, MountOptions - | lib.contracts.Nullable - { - FsType - | lib.contracts.Nullable String - | default - = null, - mountFlags - | lib.contracts.Nullable String - | default - = null - } + | lib.contracts.Nullable { + FsType + | lib.contracts.Nullable String + | default + = null, + mountFlags + | lib.contracts.Nullable String + | default + = null + } | default = null, }, @@ -183,26 +176,22 @@ in | default = null, DelayFunction - | lib.contracts.Nullable - ( - lib.contracts.OneOf - [ - "constant", - "exponential", - "fibonacci" - ] - ) + | lib.contracts.Nullable ( + lib.contracts.OneOf [ + "constant", + "exponential", + "fibonacci" + ] + ) | default = null, Delay - | lib.contracts.Nullable - ( - lib.contracts.AllOf - [ - std.number.Nat, - lib.contracts.GreaterEq 5 * 1000 - ] - ) # >=time.ParseDuration("5s") + | lib.contracts.Nullable ( + lib.contracts.AllOf [ + std.number.Nat, + lib.contracts.GreaterEq 5 * 1000 + ] + ) # >=time.ParseDuration("5s") | default = null, Interval @@ -304,12 +293,11 @@ in # TODO Volumes: [string]: #json.Volume ReschedulePolicy | json.ReschedulePolicy, EphemeralDisk - | lib.contracts.Nullable - { - Migrate | Bool, - SizeMB | std.number.Nat, - Sticky | Bool, - } + | lib.contracts.Nullable { + Migrate | Bool, + SizeMB | std.number.Nat, + Sticky | Bool, + } | default = null, Migrate @@ -379,12 +367,11 @@ in }, CheckRestart - | lib.contracts.Nullable - { - Limit | std.number.Nat | default = 0, - Grace | std.number.Nat | default = 10000000000, - IgnoreWarnings | Bool | default = false, - }, + | lib.contracts.Nullable { + Limit | std.number.Nat | default = 0, + Grace | std.number.Nat | default = 10000000000, + IgnoreWarnings | Bool | default = false, + }, Lifecycle = { Hook | [| 'prestart, 'poststart, 'poststop |], @@ -392,11 +379,10 @@ in }, LogConfig - | lib.contracts.Nullable - { - MaxFiles | std.number.PosNat, - MaxFileSizeMB | std.number.PosNat, - }, + | lib.contracts.Nullable { + MaxFiles | std.number.PosNat, + MaxFileSizeMB | std.number.PosNat, + }, Service = { Id | String | default = "", @@ -591,474 +577,455 @@ in Affinities = std.array.map - ( - fun a => - { - LTarget = a.attribute, - RTarget = a.value, - Operand = a.operator, - Weight = a.weight - } + (fun a => + { + LTarget = a.attribute, + RTarget = a.value, + Operand = a.operator, + Weight = a.weight + } ) job.affinities, Constraints = std.array.map - ( - fun c => - { - LTarget = c.attribute, - RTarget = c.value, - Operand = c.operator - } + (fun c => + { + LTarget = c.attribute, + RTarget = c.value, + Operand = c.operator + } ) job.constraints, Spreads = std.array.map - ( - fun s => - { - Attribute = s.attribute, - Weight = s.weight, - SpreadTarget = - std.array.map - ( - fun t => - { - Value = t.value, - Percent = t.percent - } - ) - s.target, - } + (fun s => + { + Attribute = s.attribute, + Weight = s.weight, + SpreadTarget = + std.array.map + (fun t => + { + Value = t.value, + Percent = t.percent + } + ) + s.target, + } ) job.spreads, TaskGroups = lib.records.mapToArray - ( - fun tgName tg => - { - Name = tgName, - Count = tg.count, - - Affinities = - std.array.map - ( - fun a => + (fun tgName tg => + { + Name = tgName, + Count = tg.count, + + Affinities = + std.array.map + (fun a => + { + LTarget = a.attribute, + RTarget = a.value, + Operand = a.operator, + Weight = a.weight, + } + ) + tg.affinities, + + Constraints = + std.array.map + (fun c => + { + LTarget = c.attribute, + RTarget = c.value, + Operand = c.operator, + } + ) + tg.constraints, + + Spreads = + std.array.map + (fun s => + { + Attribute = s.attribute, + Weight = s.weight, + SpreadTarget = + std.array.map + (fun t => + { + Value = t.value, + Percent = t.percent, + } + ) + s.target, + } + ) + tg.spreads, + } + & ( + if tg.reschedule != null then + { + ReschedulePolicy = + { + Attempts = tg.reschedule.attempts, + DelayFunction = tg.reschedule.delay_function, + Unlimited = tg.reschedule.unlimited, + } + & ( + if tg.reschedule.delay != null then { - LTarget = a.attribute, - RTarget = a.value, - Operand = a.operator, - Weight = a.weight, + # test above was: != _|_. Not sure what is the difference? Should + # we test for the presence of the field? + Delay = time.ParseDuration tg.reschedule.delay } + else + {} ) - tg.affinities, - - Constraints = - std.array.map - ( - fun c => + & ( + if tg.reschedule.interval != null then { - LTarget = c.attribute, - RTarget = c.value, - Operand = c.operator, + # test above was: != _|_. Not sure what is the difference? Should + # we test for the presence of the field? + Interval = time.ParseDuration tg.reschedule.interval } + else + {} ) - tg.constraints, - - Spreads = - std.array.map - ( - fun s => + & ( + if tg.reschedule.max_delay != null then { - Attribute = s.attribute, - Weight = s.weight, - SpreadTarget = - std.array.map - ( - fun t => - { - Value = t.value, - Percent = t.percent, - } - ) - s.target, + # test above was: != _|_. Not sure what is the difference? Should + # we test for the presence of the field? + MaxDelay = time.ParseDuration tg.reschedule.max_delay } + else + {} ) - tg.spreads, - } - & ( - if tg.reschedule != null then - { - ReschedulePolicy = - { - Attempts = tg.reschedule.attempts, - DelayFunction = tg.reschedule.delay_function, - Unlimited = tg.reschedule.unlimited, - } - & ( - if tg.reschedule.delay != null then - { - # test above was: != _|_. Not sure what is the difference? Should - # we test for the presence of the field? - Delay = time.ParseDuration tg.reschedule.delay - } - else - {} - ) - & ( - if tg.reschedule.interval != null then - { - # test above was: != _|_. Not sure what is the difference? Should - # we test for the presence of the field? - Interval = time.ParseDuration tg.reschedule.interval - } - else - {} - ) - & ( - if tg.reschedule.max_delay != null then - { - # test above was: != _|_. Not sure what is the difference? Should - # we test for the presence of the field? - MaxDelay = time.ParseDuration tg.reschedule.max_delay - } - else - {} - ) + } + else + {} + ) + & ( + if tg.ephemeral_disk != null then + { + EphemeralDisk = { + SizeMB = tg.ephemeral_disk.size, + Migrate = tg.ephemeral_disk.migrate, + Sticky = tg.ephemeral_disk.sticky, } - else - {} - ) - & ( - if tg.ephemeral_disk != null then - { - EphemeralDisk = { - SizeMB = tg.ephemeral_disk.size, - Migrate = tg.ephemeral_disk.migrate, - Sticky = tg.ephemeral_disk.sticky, - } + } + else + {} + ) + & ( + if tg.restart != null then + { + RestartPolicy = { + Interval = time.ParseDuration tg.restart.interval, + Attempts = tg.restart.attempts, + Delay = time.ParseDuration tg.restart.delay, + Mode = tg.restart.mode, } - else - {} - ) - & ( - if tg.restart != null then + } + else + {} + ) + # only one network can be specified at group level, and we never use + # deprecated task level ones. + & ( + if tg.network != null then + let ports = + tg.network.port + |> lib.records.toArray + |> std.array.partition + tg.network.port + (fun entry => + entry.value.static != null + ) + in + # would be better with destructuring + let mkPort = fun { key, value = { static, to, host_network }, .. } => { - RestartPolicy = { - Interval = time.ParseDuration tg.restart.interval, - Attempts = tg.restart.attempts, - Delay = time.ParseDuration tg.restart.delay, - Mode = tg.restart.mode, - } + Label = key, + Value = static, + To = to, + HostNetwork = host_network, } - else - {} - ) - # only one network can be specified at group level, and we never use - # deprecated task level ones. - & ( - if tg.network != null then - let ports = - tg.network.port - |> lib.records.toArray - |> std.array.partition - tg.network.port - ( - fun entry => - entry.value.static != null - ) - in - # would be better with destructuring - let mkPort = fun { key, value = { static, to, host_network }, .. } => + in + { + Networks = [ { - Label = key, - Value = static, - To = to, - HostNetwork = host_network, + Mode = tg.network.mode, + ReservedPorts = std.array.map mkPort ports.right, + DynamicPorts = std.array.map mkPort ports.wrong, } - in - { - Networks = [ - { - Mode = tg.network.mode, - ReservedPorts = std.array.map mkPort ports.right, - DynamicPorts = std.array.map mkPort ports.wrong, - } - ] - } - else - {} - ) - & { - Services = - std.array.map - ( - fun sName s => + ] + } + else + {} + ) + & { + Services = + std.array.map + (fun sName s => + { + Name = sName, + TaskName = s.task, + Tags = s.tags, + AddressMode = s.address_mode, + } + & ( + if s.check_restart != null then { - Name = sName, - TaskName = s.task, - Tags = s.tags, - AddressMode = s.address_mode, + CheckRestart = { + Limit = s.check_restart.limit, + Grace = time.ParseDuration (s.check_restart.grace), + IgnoreWarnings = s.check_restart.ignore_warnings, + } } - & ( - if s.check_restart != null then + else + {} + ) + & { + Checks = + lib.records.mapToArray + (fun cName c => { - CheckRestart = { - Limit = s.check_restart.limit, - Grace = time.ParseDuration (s.check_restart.grace), - IgnoreWarnings = s.check_restart.ignore_warnings, - } + AddressMode = c.address_mode, + Type = c.type, + PortLabel = c.port, + Interval = time.ParseDuration (c.interval) } - else - {} - ) - & { - Checks = - lib.records.mapToArray - ( - fun cName c => - { - AddressMode = c.address_mode, - Type = c.type, - PortLabel = c.port, - Interval = time.ParseDuration (c.interval) - } - & ( - if c.type == "http" then - { - Path = c.path, - Method = c.method, - Protocol = c.protocol - } - else - {} - ) - & { - Timeout = time.ParseDuration (c.timeout), - SuccessBeforePassing = c.success_before_passing, - FailuresBeforeCritical = c.failures_before_critical, - TLSSkipVerify = c.tls_skip_verify, - InitialStatus = c.initial_status, - Header = c.header, - Body = c.body, + & ( + if c.type == "http" then + { + Path = c.path, + Method = c.method, + Protocol = c.protocol + } + else + {} + ) + & { + Timeout = time.ParseDuration (c.timeout), + SuccessBeforePassing = c.success_before_passing, + FailuresBeforeCritical = c.failures_before_critical, + TLSSkipVerify = c.tls_skip_verify, + InitialStatus = c.initial_status, + Header = c.header, + Body = c.body, + } + & ( + if c.check_restart != null then + { + CheckRestart = { + Limit = c.check_restart.limit, + Grace = time.ParseDuration (c.check_restart.grace), + IgnoreWarnings = c.check_restart.ignore_warnings, } - & ( - if c.check_restart != null then - { - CheckRestart = { - Limit = c.check_restart.limit, - Grace = time.ParseDuration (c.check_restart.grace), - IgnoreWarnings = c.check_restart.ignore_warnings, - } - } - else - {} - ) - ) - s.check, - - PortLabel = s.port, - Meta = s.meta, - } - ) - tg.service, + } + else + {} + ) + ) + s.check, + + PortLabel = s.port, + Meta = s.meta, + } + ) + tg.service, - Tasks = - lib.records.mapToArray - ( - fun tName t => + Tasks = + lib.records.mapToArray + (fun tName t => + { + Name = tName, + Driver = t.driver, + Config = t.config, + Env = t.env, + KillSignal = t.kill_signal, + } + & ( + if t.kill_timeout != null then { - Name = tName, - Driver = t.driver, - Config = t.config, - Env = t.env, - KillSignal = t.kill_signal, + KillTimeout = time.ParseDuration (t.kill_timeout) } - & ( - if t.kill_timeout != null then + else + {} + ) + & { + Affinities = + std.array.map + (fun { attribute, value, operator, weight, .. } => { - KillTimeout = time.ParseDuration (t.kill_timeout) + LTarget = attribute, + RTarget = value, + Operand = operator, + Weight = weight, } - else - {} - ) - & { - Affinities = - std.array.map - ( - fun { attribute, value, operator, weight, .. } => - { - LTarget = attribute, - RTarget = value, - Operand = operator, - Weight = weight, - } - ) - t.affinities, - # END AFFINITIES - - Constraints = - lib.records.mapToArray - ( - fun { attribute, value, operator } => - { - LTarget = attribute, - RTarget = value, - Operand = operator, - } - ) - t.constraints, - # END CONSTRAINTS - } - & ( - if t.logs != null then + ) + t.affinities, + # END AFFINITIES + + Constraints = + lib.records.mapToArray + (fun { attribute, value, operator } => { - LogConfig = { - MaxFiles = t.logs.max_files, - MaxFileSizeMB = t.logs.max_file_size, - } + LTarget = attribute, + RTarget = value, + Operand = operator, } - else - {} - ) - & ( - if t.restart != null then + ) + t.constraints, + # END CONSTRAINTS + } + & ( + if t.logs != null then + { + LogConfig = { + MaxFiles = t.logs.max_files, + MaxFileSizeMB = t.logs.max_file_size, + } + } + else + {} + ) + & ( + if t.restart != null then + { + RestartPolicy = { + Interval = time.ParseDuration (t.restart.interval), + Attempts = t.restart.attempts, + Delay = time.ParseDuration (t.restart.delay), + Mode = t.restart.mode, + } + } + else + {} + ) + & ( + if t.lifecycle != null then + { + Lifecycle = { + Hook = t.lifecycle.hook, + Sidecar = t.lifecycle.sidecar, + } + } + else + {} + ) + & { + Resources = { + CPU = t.resources.cpu, + MemoryMB = t.resources.memory, + }, + + Leader = t.leader, + + Templates = + lib.records.mapToArray + (fun tplName tpl => { - RestartPolicy = { - Interval = time.ParseDuration (t.restart.interval), - Attempts = t.restart.attempts, - Delay = time.ParseDuration (t.restart.delay), - Mode = t.restart.mode, - } + DestPath = tplName, + EmbeddedTmpl = tpl.data, + SourcePath = tpl.source, + Envvars = tpl.env, + ChangeMode = tpl.change_mode, + ChangeSignal = tpl.change_signal, + Perms = tpl.perms, + LeftDelim = tpl.left_delimiter, + RightDelim = tpl.right_delimiter, } - else - {} - ) - & ( - if t.lifecycle != null then + ) + t.template, + + Artifacts = + lib.records.mapToArray + (fun artName art => { - Lifecycle = { - Hook = t.lifecycle.hook, - Sidecar = t.lifecycle.sidecar, - } + GetterHeaders = art.headers, + GetterMode = art.mode, + GetterOptions = art.options, + GetterSource = art.source, + RelativeDest = artName, } - else - {} - ) - & { - Resources = { - CPU = t.resources.cpu, - MemoryMB = t.resources.memory, - }, - - Leader = t.leader, - - Templates = - lib.records.mapToArray - ( - fun tplName tpl => - { - DestPath = tplName, - EmbeddedTmpl = tpl.data, - SourcePath = tpl.source, - Envvars = tpl.env, - ChangeMode = tpl.change_mode, - ChangeSignal = tpl.change_signal, - Perms = tpl.perms, - LeftDelim = tpl.left_delimiter, - RightDelim = tpl.right_delimiter, - } - ) - t.template, - - Artifacts = - lib.records.mapToArray - ( - fun artName art => - { - GetterHeaders = art.headers, - GetterMode = art.mode, - GetterOptions = art.options, - GetterSource = art.source, - RelativeDest = artName, - } - ) - t.artifact, + ) + t.artifact, + } + & ( + if t.vault != null then + { + Vault = { + ChangeMode = t.vault.change_mode, + ChangeSignal = t.vault.change_signal, + Env = t.vault.env, + Namespace = t.vault.namespace, + Policies = t.vault.policies, + } } - & ( - if t.vault != null then + else + {} + ) + & { + VolumeMounts = + lib.records.mapToArray + (fun volName vol => { - Vault = { - ChangeMode = t.vault.change_mode, - ChangeSignal = t.vault.change_signal, - Env = t.vault.env, - Namespace = t.vault.namespace, - Policies = t.vault.policies, - } + Destination = vol.destination, + PropagationMode = "private", + ReadOnly = vol.read_only, + Volume = volName, } - else - {} - ) - & { - VolumeMounts = - lib.records.mapToArray - ( - fun volName vol => - { - Destination = vol.destination, - PropagationMode = "private", - ReadOnly = vol.read_only, - Volume = volName, - } - ) - t.volume_mount, - } - ) - tg.task, - # END TASKS - } - & ( - if tg.vault != null then - { - Vault = { - ChangeMode = tg.vault.change_mode, - ChangeSignal = tg.vault.change_signal, - Env = tg.vault.env, - Namespace = tg.vault.namespace, - Policies = tg.vault.policies, + ) + t.volume_mount, } + ) + tg.task, + # END TASKS + } + & ( + if tg.vault != null then + { + Vault = { + ChangeMode = tg.vault.change_mode, + ChangeSignal = tg.vault.change_signal, + Env = tg.vault.env, + Namespace = tg.vault.namespace, + Policies = tg.vault.policies, } - else - {} - ) - & lib.records.mapToArray - ( - fun volName vol => + } + else + {} + ) + & lib.records.mapToArray + (fun volName vol => + { + Volumes."#{volName}" = { - Volumes."#{volName}" = + Name = volName, + Type = vol.type, + Source = vol.source, + ReadOnly = vol.read_only, + } + & ( + if vol.type == "csi" then { - Name = volName, - Type = vol.type, - Source = vol.source, - ReadOnly = vol.read_only, + MountOptions = { + FsType = vol.mount_options.fs_type, + mountFlags = vol.mount_options.mount_flags, + } } - & ( - if vol.type == "csi" then - { - MountOptions = { - FsType = vol.mount_options.fs_type, - mountFlags = vol.mount_options.mount_flags, - } - } - else - {} - ) - } - ) - tg.volume + else + {} + ) + } + ) + tg.volume ) job.group, }, @@ -1152,23 +1119,22 @@ in = null, RTarget | String, Operand - | lib.contracts.OneOf - [ - "regexp", - "set_contains_all", - "set_contains", - "set_contains_any", - "=", - "==", - "is", - "!=", - "not", - ">", - ">=", - "<", - "<=", - "version" - ] + | lib.contracts.OneOf [ + "regexp", + "set_contains_all", + "set_contains", + "set_contains_any", + "=", + "==", + "is", + "!=", + "not", + ">", + ">=", + "<", + "<=", + "version" + ] | default = "=", Weight @@ -1186,23 +1152,22 @@ in = null, value | String, operator - | lib.contracts.OneOf - [ - "=", - "!=", - ">", - ">=", - "<", - "<=", - "distinct_hosts", - "distinct_property", - "regexp", - "set_contains", - "version", - "semver", - "is_set", - "is_not_set" - ] + | lib.contracts.OneOf [ + "=", + "!=", + ">", + ">=", + "<", + "<=", + "distinct_hosts", + "distinct_property", + "regexp", + "set_contains", + "version", + "semver", + "is_set", + "is_not_set" + ] | default = "=", }, @@ -1213,15 +1178,13 @@ in | default = null, weight - | lib.contracts.Nullable - ( - lib.contracts.AllOf - [ - std.number.Nat, - lib.contracts.GreaterEq - 100, - lib.contracts.SmallerEq 100 - ] - ) + | lib.contracts.Nullable ( + lib.contracts.AllOf [ + std.number.Nat, + lib.contracts.GreaterEq - 100, + lib.contracts.SmallerEq 100 + ] + ) | default = null, target @@ -1236,26 +1199,23 @@ in | default = null, percent - | lib.contracts.Nullable - ( - lib.contracts.AllOf - [ - std.number.Nat, - lib.contracts.GreaterEq - 100, - lib.contracts.SmallerEq 100 - ] - ) + | lib.contracts.Nullable ( + lib.contracts.AllOf [ + std.number.Nat, + lib.contracts.GreaterEq - 100, + lib.contracts.SmallerEq 100 + ] + ) | default = null, }, ephemeral_disk = - lib.contracts.Nullable - { - size | std.number.PosNat, - migrate | Bool | default = false, - sticky | Bool | default = false, - }, + lib.contracts.Nullable { + size | std.number.PosNat, + migrate | Bool | default = false, + sticky | Bool | default = false, + }, group = { type | [| 'service, 'batch, 'system |], @@ -1328,13 +1288,12 @@ in network = { mode | [| 'host, 'bridge |], dns - | lib.contracts.Nullable - { - servers - | Array String - | default - = [] - } + | lib.contracts.Nullable { + servers + | Array String + | default + = [] + } | default = null, port diff --git a/core/benches/nixpkgs/lists.ncl b/core/benches/nixpkgs/lists.ncl index 040ac81237..961a21f3df 100644 --- a/core/benches/nixpkgs/lists.ncl +++ b/core/benches/nixpkgs/lists.ncl @@ -333,12 +333,11 @@ "% = fun pred => foldr - ( - fun h t => - if pred h then - { right = [h] @ t.right, wrong = t.wrong } - else - { right = t.right, wrong = [h] @ t.wrong } + (fun h t => + if pred h then + { right = [h] @ t.right, wrong = t.wrong } + else + { right = t.right, wrong = [h] @ t.wrong } ) { right = [], wrong = [] }, @@ -365,10 +364,9 @@ "% = fun pred => foldl_ - ( - fun r e => - let key = pred e in - { "%{key}" = (r."%{key}" @ [e]) } & (std.record.remove key r) + (fun r e => + let key = pred e in + { "%{key}" = (r."%{key}" @ [e]) } & (std.record.remove key r) ) {}, @@ -540,16 +538,15 @@ = fun strictLess list => let len = std.array.length list in let first = std.array.first list in - let rec pivot_ = ( - fun n acc @ { left = left_, right = right_ } => - let el = std.array.at n list in - let next = pivot_ (n + 1) in - if n == len then - acc - else if strictLess first el then - next { left = left_, right = [el] @ right_ } - else - next { left = [el] @ left_, right = right_ } + let rec pivot_ = (fun n acc @ { left = left_, right = right_ } => + let el = std.array.at n list in + let next = pivot_ (n + 1) in + if n == len then + acc + else if strictLess first el then + next { left = left_, right = [el] @ right_ } + else + next { left = [el] @ left_, right = right_ } ) in let pivot = pivot_ 1 { left = [], right = [] } in diff --git a/core/stdlib/internals.ncl b/core/stdlib/internals.ncl index 56d0f60f1b..bd74ab5483 100644 --- a/core/stdlib/internals.ncl +++ b/core/stdlib/internals.ncl @@ -64,12 +64,11 @@ "$func" = fun Domain Codomain => fun label value => if %typeof% value == 'Function then - 'Ok ( - fun x => - %contract/apply% - Codomain - (%label/go_codom% label) - (value (%contract/apply% Domain (%label/flip_polarity% (%label/go_dom% label)) x)) + 'Ok (fun x => + %contract/apply% + Codomain + (%label/go_codom% label) + (value (%contract/apply% Domain (%label/flip_polarity% (%label/go_dom% label)) x)) ) else 'Error { message = "expected a function" }, @@ -78,15 +77,13 @@ "$func_dom" = fun Domain => fun label value => if %typeof% value == 'Function then - 'Ok ( - fun x => - value - ( - %contract/apply% - Domain - (%label/flip_polarity% (%label/go_dom% label)) - x - ) + 'Ok (fun x => + value ( + %contract/apply% + Domain + (%label/flip_polarity% (%label/go_dom% label)) + x + ) ) else 'Error { message = "expected a function" }, @@ -95,12 +92,11 @@ "$func_codom" = fun Codomain => fun label value => if %typeof% value == 'Function then - 'Ok ( - fun x => - %contract/apply% - Codomain - (%label/go_codom% label) - (value x) + 'Ok (fun x => + %contract/apply% + Codomain + (%label/go_codom% label) + (value x) ) else 'Error { message = "expected a function" }, @@ -260,12 +256,11 @@ let fields_with_contracts = %record/map% split_result.right_center - ( - fun field value => - %contract/apply% - field_contracts."%{field}" - (%label/go_field% field label) - value + (fun field value => + %contract/apply% + field_contracts."%{field}" + (%label/go_field% field label) + value ) in @@ -299,9 +294,8 @@ 'Ok ( %record/map% value - ( - fun _field field_value => - %contract/apply% Contract (%label/go_dict% label) field_value + (fun _field field_value => + %contract/apply% Contract (%label/go_dict% label) field_value ) ) else diff --git a/core/stdlib/std.ncl b/core/stdlib/std.ncl index 3421361eb8..4be3ef336a 100644 --- a/core/stdlib/std.ncl +++ b/core/stdlib/std.ncl @@ -60,17 +60,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Array then - if %array/length% value != 0 then - 'Ok value - else - 'Error { message = "empty array" } - else - 'Error { message = "not a array" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Array then + if %array/length% value != 0 then + 'Ok value + else + 'Error { message = "empty array" } + else + 'Error { message = "not a array" } + ), first : forall a. Array a -> a @@ -979,9 +977,8 @@ %contract/record_lazy_apply% ctr_label value - ( - fun field => - contract_map."%{field}" + (fun field => + contract_map."%{field}" ), 'Array => fun ctr_label value => @@ -990,10 +987,9 @@ if value_length == %array/length% constant then %array/generate% value_length - ( - fun i => - %array/at% value i - |> std.contract.apply (Equal (%array/at% constant i)) ctr_label + (fun i => + %array/at% value i + |> std.contract.apply (Equal (%array/at% constant i)) ctr_label ) else ctr_label @@ -1151,14 +1147,12 @@ ``` "% = fun pred => - %contract/custom% - ( - fun _label value => - if pred value then - 'Ok value - else - 'Error {} - ), + %contract/custom% (fun _label value => + if pred value then + 'Ok value + else + 'Error {} + ), from_validator | ( @@ -1215,16 +1209,14 @@ # A validator turns out to be a valid custom contract as well (which just # never returns a value with delayed checks) = fun validator => - %contract/custom% - ( - fun label value => - let result = validator value in + %contract/custom% (fun label value => + let result = validator value in - if result == 'Ok then - 'Ok value - else - result - ), + if result == 'Ok then + 'Ok value + else + result + ), Sequence | Array Dyn -> Dyn @@ -1261,14 +1253,12 @@ ``` "% = fun contracts => - %contract/custom% - ( - fun label value => - std.array.try_fold_left - (fun acc Contract => std.contract.check Contract label acc) - value - contracts - ), + %contract/custom% (fun label value => + std.array.try_fold_left + (fun acc Contract => std.contract.check Contract label acc) + value + contracts + ), label | doc m%" @@ -1629,35 +1619,32 @@ ``` "% = fun contracts => - %contract/custom% - ( - fun label value => - std.array.try_fold_left - ( - fun _acc Contract => - let label = - %label/with_message% - "any_of: a delayed check of the picked branch failed" - label - in - std.contract.check Contract label value - # We want to short-circuit on contract success. Since try_fold_left - # short-circuits on failure, we need to flip the two. - |> match { - 'Ok value => 'Error value, - 'Error msg => 'Ok msg - } - ) - ('Ok null) - contracts + %contract/custom% (fun label value => + std.array.try_fold_left + (fun _acc Contract => + let label = + %label/with_message% + "any_of: a delayed check of the picked branch failed" + label + in + std.contract.check Contract label value + # We want to short-circuit on contract success. Since try_fold_left + # short-circuits on failure, we need to flip the two. |> match { - 'Ok _ => - 'Error { - message = "any_of: value didn't match any of the contracts", - }, - 'Error value => 'Ok value, + 'Ok value => 'Error value, + 'Error msg => 'Ok msg } - ), + ) + ('Ok null) + contracts + |> match { + 'Ok _ => + 'Error { + message = "any_of: value didn't match any of the contracts", + }, + 'Error value => 'Ok value, + } + ), all_of : Array Dyn -> Dyn @@ -1701,16 +1688,14 @@ ``` "% = fun Contract => - %contract/custom% - ( - fun label value => - value - |> std.contract.check Contract label - |> match { - 'Ok _ => 'Error { message = "not: value matched the immediate part of the contract" }, - error => 'Ok value, - } - ), + %contract/custom% (fun label value => + value + |> std.contract.check Contract label + |> match { + 'Ok _ => 'Error { message = "not: value matched the immediate part of the contract" }, + error => 'Ok value, + } + ), unstable | doc m%" @@ -1742,17 +1727,14 @@ ``` "% = fun Domain Codomain => - %contract/custom% - ( - fun label function => - 'Ok ( - fun arg => - let arg_with_ctr = std.contract.apply Domain label arg in - - function arg_with_ctr - |> std.contract.apply (Codomain arg_with_ctr) label - ) - ), + %contract/custom% (fun label function => + 'Ok (fun arg => + let arg_with_ctr = std.contract.apply Domain label arg in + + function arg_with_ctr + |> std.contract.apply (Codomain arg_with_ctr) label + ) + ), RangeStep | doc m%" @@ -1766,17 +1748,15 @@ checked by an associated static type annotation. "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Number && value < 0 then - 'Error { - message = "invalid range step", - notes = ["Expected a positive number, got %{%to_string% value}"], - } - else - 'Ok value - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Number && value < 0 then + 'Error { + message = "invalid range step", + notes = ["Expected a positive number, got %{%to_string% value}"], + } + else + 'Ok value + ), RangeFun | doc m%" @@ -1795,24 +1775,22 @@ "% = fun Codomain => let RangeSecond = fun start => - %contract/custom% - ( - fun _label value => - if %typeof% start == 'Number - && %typeof% value == 'Number - && start > value then - let start_as_str = %to_string% start in - let end_as_str = %to_string% value in - - 'Error { - message = "invalid range", - notes = [ - "Expected a range end greater than %{start_as_str} (range start), got %{end_as_str}" - ] - } - else - 'Ok value - ) + %contract/custom% (fun _label value => + if %typeof% start == 'Number + && %typeof% value == 'Number + && start > value then + let start_as_str = %to_string% start in + let end_as_str = %to_string% value in + + 'Error { + message = "invalid range", + notes = [ + "Expected a range end greater than %{start_as_str} (range start), got %{end_as_str}" + ] + } + else + 'Ok value + ) in fun label value => @@ -1849,53 +1827,49 @@ let attach_message = label_module.with_message "invalid array indexing" in let ArrayIndexFirst = - %contract/custom% - ( - fun label value => - if %typeof% value == 'Number then - let label = - label - |> attach_message - |> label_module.append_note "Expected array index to be a positive integer, got %{%to_string% value} " - in - std.contract.check std.number.Nat label value - else - 'Ok value - ) + %contract/custom% (fun label value => + if %typeof% value == 'Number then + let label = + label + |> attach_message + |> label_module.append_note "Expected array index to be a positive integer, got %{%to_string% value} " + in + std.contract.check std.number.Nat label value + else + 'Ok value + ) in let ArrayIndexSecond = fun type min_size => - %contract/custom% - ( - fun _label value => - if %typeof% min_size == 'Number && %typeof% value == 'Array then - let max_idx = - type - |> match { - 'Index => %array/length% value - 1, - 'Split => %array/length% value - } - in - - if min_size > max_idx then - let index_as_str = %to_string% min_size in - let max_as_str = %to_string% max_idx in - let note = - if %array/length% value == 0 then - "Can't index into an empty array" - else - "Expected an array index between 0 and %{max_as_str} (included), got %{index_as_str}" - in - - 'Error { - message = "invalid array indexing", - notes = [note], - } + %contract/custom% (fun _label value => + if %typeof% min_size == 'Number && %typeof% value == 'Array then + let max_idx = + type + |> match { + 'Index => %array/length% value - 1, + 'Split => %array/length% value + } + in + + if min_size > max_idx then + let index_as_str = %to_string% min_size in + let max_as_str = %to_string% max_idx in + let note = + if %array/length% value == 0 then + "Can't index into an empty array" else - 'Ok value - else - 'Ok value - ) + "Expected an array index between 0 and %{max_as_str} (included), got %{index_as_str}" + in + + 'Error { + message = "invalid array indexing", + notes = [note], + } + else + 'Ok value + else + 'Ok value + ) in fun type => @@ -1926,76 +1900,68 @@ let attach_message = label_module.with_message "invalid array slice indexing" in let SliceIndexFirst = - %contract/custom% - ( - fun label value => - if %typeof% value == 'Number then - let label = - label - |> attach_message - |> label_module.append_note "Expected the array slice start index to be a positive integer, got %{%to_string% value}" - in - std.contract.check std.number.Nat label value - else - 'Ok value - ) + %contract/custom% (fun label value => + if %typeof% value == 'Number then + let label = + label + |> attach_message + |> label_module.append_note "Expected the array slice start index to be a positive integer, got %{%to_string% value}" + in + std.contract.check std.number.Nat label value + else + 'Ok value + ) in let SliceIndexSecond = fun start => - %contract/custom% - ( - fun label value => - if %typeof% start == 'Number && %typeof% value == 'Number then - if value < start then - 'Error { - message = "invalid array slice indexing", - notes = [ - "Expected the array slice indices to satisfy `start <= end`, but got %{%to_string% start} (start) and %{%to_string% value} (end)" - ], - } - else - let label = - label - |> attach_message - |> label_module.append_note "Expected the array slice end index to be a positive integer, got %{%to_string% value}" - in - std.contract.check std.number.Nat label value - else - 'Ok value - ) + %contract/custom% (fun label value => + if %typeof% start == 'Number && %typeof% value == 'Number then + if value < start then + 'Error { + message = "invalid array slice indexing", + notes = [ + "Expected the array slice indices to satisfy `start <= end`, but got %{%to_string% start} (start) and %{%to_string% value} (end)" + ], + } + else + let label = + label + |> attach_message + |> label_module.append_note "Expected the array slice end index to be a positive integer, got %{%to_string% value}" + in + std.contract.check std.number.Nat label value + else + 'Ok value + ) in let ArraySliceArray = fun end_index => - %contract/custom% - ( - fun _label value => - if %typeof% end_index == 'Number - && %typeof% value == 'Array - && end_index > %array/length% value then - let index_as_str = %to_string% end_index in - let size_as_str = %to_string% (%array/length% value) in - - 'Error { - message = "invalid array slice indexing", - notes = [ - "Expected the slice end index to be between 0 and %{size_as_str} (array's length), got %{index_as_str}" - ], - } - else - 'Ok value - ) + %contract/custom% (fun _label value => + if %typeof% end_index == 'Number + && %typeof% value == 'Array + && end_index > %array/length% value then + let index_as_str = %to_string% end_index in + let size_as_str = %to_string% (%array/length% value) in + + 'Error { + message = "invalid array slice indexing", + notes = [ + "Expected the slice end index to be between 0 and %{size_as_str} (array's length), got %{index_as_str}" + ], + } + else + 'Ok value + ) in DependentFun SliceIndexFirst - ( - fun start_index => - DependentFun - (SliceIndexSecond start_index) - ( - fun end_index => - ArraySliceArray end_index -> Dyn - ) + (fun start_index => + DependentFun + (SliceIndexSecond start_index) + (fun end_index => + ArraySliceArray end_index -> Dyn + ) ), HasField @@ -2026,21 +1992,19 @@ "%% = fun Codomain => let HasFieldSecond = fun field => - %contract/custom% - ( - fun _label value => - if %typeof% field == 'String - && %typeof% value == 'Record - && !(%record/has_field% field value) then - 'Error { - message = "missing field", - notes = [ - "The record given as an argument lacks the required field `%{field}`." - ] - } - else - 'Ok value - ) + %contract/custom% (fun _label value => + if %typeof% field == 'String + && %typeof% value == 'Record + && !(%record/has_field% field value) then + 'Error { + message = "missing field", + notes = [ + "The record given as an argument lacks the required field `%{field}`." + ] + } + else + 'Ok value + ) in DependentFun @@ -2082,14 +2046,12 @@ error "% = - %contract/custom% - ( - fun _label value => - if std.is_enum value then - 'Ok value - else - 'Error {} - ), + %contract/custom% (fun _label value => + if std.is_enum value then + 'Ok value + else + 'Error {} + ), TagOrString | doc m%%" @@ -2129,19 +2091,17 @@ ``` "%% = - %contract/custom% - ( - fun _label value => - %typeof% value - |> match { - 'String => 'Ok (%enum/from_string% value), - 'Enum if !(is_enum_variant value) => 'Ok value, - _ => - 'Error { - message = "expected either a string or an enum tag" - }, - } - ), + %contract/custom% (fun _label value => + %typeof% value + |> match { + 'String => 'Ok (%enum/from_string% value), + 'Enum if !(is_enum_variant value) => 'Ok value, + _ => + 'Error { + message = "expected either a string or an enum tag" + }, + } + ), is_enum_tag : Dyn -> Bool @@ -2388,17 +2348,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Number then - if value % 1 == 0 then - 'Ok value - else - 'Error { message = "expected an integer" } - else - 'Error { message = "expected a number" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Number then + if value % 1 == 0 then + 'Ok value + else + 'Error { message = "expected an integer" } + else + 'Error { message = "expected a number" } + ), Nat | doc m%" @@ -2416,17 +2374,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Number then - if value % 1 == 0 && value >= 0 then - 'Ok value - else - 'Error { message = "expected a natural" } - else - 'Error { message = "expected a number" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Number then + if value % 1 == 0 && value >= 0 then + 'Ok value + else + 'Error { message = "expected a natural" } + else + 'Error { message = "expected a number" } + ), PosNat | doc m%" @@ -2444,17 +2400,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Number then - if value % 1 == 0 && value > 0 then - 'Ok value - else - 'Error { message = "expected a positive integer" } - else - 'Error { message = "expected a number" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Number then + if value % 1 == 0 && value > 0 then + 'Ok value + else + 'Error { message = "expected a positive integer" } + else + 'Error { message = "expected a number" } + ), NonZero | doc m%" @@ -2470,17 +2424,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'Number then - if value != 0 then - 'Ok value - else - 'Error { message = "value is zero" } - else - 'Error { message = "expected a number" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'Number then + if value != 0 then + 'Ok value + else + 'Error { message = "value is zero" } + else + 'Error { message = "expected a number" } + ), is_integer : Number -> Bool @@ -3331,17 +3283,15 @@ let pattern = m%"^[+-]?(\d+(\.\d*)?(e[+-]?\d+)?|\.\d+(e[+-]?\d+)?)$"% in let is_num_literal = %string/is_match% pattern in - %contract/custom% - ( - fun _label value => - if %typeof% value == 'String then - if is_num_literal value then - 'Ok value - else - 'Error { message = "invalid number literal" } - else - 'Error { message = "expected a string" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'String then + if is_num_literal value then + 'Ok value + else + 'Error { message = "invalid number literal" } + else + 'Error { message = "expected a string" } + ), Character | doc m%" @@ -3361,17 +3311,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'String then - if length value == 1 then - 'Ok value - else - 'Error { message = "length different than one" } - else - 'Error { message = "expected a string" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'String then + if length value == 1 then + 'Ok value + else + 'Error { message = "length different than one" } + else + 'Error { message = "expected a string" } + ), Stringable | doc m%" @@ -3400,22 +3348,20 @@ ``` "% = - %contract/custom% - ( - fun _label value => - let type = std.typeof value in - - if value == null - || type == 'Number - || type == 'Bool - || type == 'String - # note that `type == 'Enum` isn't sufficient, as it includes enum - # variants - || std.enum.is_enum_tag value then - 'Ok value - else - 'Error {} - ), + %contract/custom% (fun _label value => + let type = std.typeof value in + + if value == null + || type == 'Number + || type == 'Bool + || type == 'String + # note that `type == 'Enum` isn't sufficient, as it includes enum + # variants + || std.enum.is_enum_tag value then + 'Ok value + else + 'Error {} + ), NonEmpty | doc m%" @@ -3433,17 +3379,15 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if %typeof% value == 'String then - if %string/length% value > 0 then - 'Ok value - else - 'Error { message = "empty string" } - else - 'Error { message = "not a string" } - ), + %contract/custom% (fun _label value => + if %typeof% value == 'String then + if %string/length% value > 0 then + 'Ok value + else + 'Error { message = "empty string" } + else + 'Error { message = "not a string" } + ), join : String -> Array String -> String @@ -3955,14 +3899,12 @@ ``` "% = - %contract/custom% - ( - fun _label value => - if value then - 'Ok value - else - 'Error {} - ), + %contract/custom% (fun _label value => + if value then + 'Ok value + else + 'Error {} + ), assert_all | Array Assert -> Bool diff --git a/doc/manual/contracts.md b/doc/manual/contracts.md index ac80c988d6..db9ac4f803 100644 --- a/doc/manual/contracts.md +++ b/doc/manual/contracts.md @@ -107,14 +107,12 @@ Here is another example of a port number contract: ```nickel { Port = - std.contract.from_predicate - ( - fun value => - std.is_number value - && std.number.is_integer value - && value >= 0 - && value <= 65535 - ) + std.contract.from_predicate (fun value => + std.is_number value + && std.number.is_integer value + && value >= 0 + && value <= 65535 + ) } ``` @@ -160,22 +158,20 @@ reporting: ```nickel #repl > let IsFoo = - std.contract.from_validator - ( - match { - "foo" => 'Ok, - value if std.is_string value => - 'Error { - message = "expected \"foo\", got \"%{value}\"", - }, - value => - let typeof = value |> std.typeof |> std.to_string in - 'Error { - message = "expected a String, got a %{typeof}", - notes = ["The value must be a string equal to \"foo\"."], - }, - } - ) + std.contract.from_validator (match { + "foo" => 'Ok, + value if std.is_string value => + 'Error { + message = "expected \"foo\", got \"%{value}\"", + }, + value => + let typeof = value |> std.typeof |> std.to_string in + 'Error { + message = "expected a String, got a %{typeof}", + notes = ["The value must be a string equal to \"foo\"."], + }, + } + ) > 1 | IsFoo error: contract broken by a value @@ -247,14 +243,13 @@ Let us consider a contract for bounds checking: ```nickel let Between5And10 = - std.contract.from_predicate - ( - fun value => - std.is_number value - && value >= 5 - && value <= 10 - ) + std.contract.from_predicate (fun value => + std.is_number value + && value >= 5 + && value <= 10 + ) in + let Schema = { level | Between5And10, } @@ -267,23 +262,21 @@ Now, we add a new field to our schema, which must be between `0` and `1`: ```nickel let Between5And10 = - std.contract.from_predicate - ( - fun value => - std.is_number value - && value >= 5 - && value <= 10 - ) + std.contract.from_predicate (fun value => + std.is_number value + && value >= 5 + && value <= 10 + ) in + let Between0And1 = - std.contract.from_predicate - ( - fun value => - std.is_number value - && value >= 0 - && value <= 1 - ) + std.contract.from_predicate (fun value => + std.is_number value + && value >= 0 + && value <= 1 + ) in + let Schema = { level | Between5And10, strength | Between0And1, @@ -302,8 +295,9 @@ single function parametrized by some arguments which returns a contract: ```nickel let Between = fun min max => std.contract.from_predicate (fun value => - value >= min && - value <= max) + value >= min + && value <= max + ) in let Schema = { @@ -339,13 +333,12 @@ argument contract. ```nickel let Nullable = fun Contract => - std.contract.custom - (fun label value => - if value == null then - 'Ok value - else - std.contract.check Contract label value - ) + std.contract.custom (fun label value => + if value == null then + 'Ok value + else + std.contract.check Contract label value + ) in [ @@ -376,10 +369,12 @@ builtin contract `[| 'Foo Contract |]` parametrized by `Contract`: ```nickel #repl > let FooOf = fun Contract => - std.contract.custom (fun label => match { - 'Foo arg => 'Ok ('Foo (std.contract.apply Contract label arg)), - _ => 'Error {}, - }) + std.contract.custom (fun label => + match { + 'Foo arg => 'Ok ('Foo (std.contract.apply Contract label arg)), + _ => 'Error {}, + } + ) > 'Foo 5 | FooOf Number 'Foo 5 @@ -401,13 +396,12 @@ case of the `Nullable` example above: ```nickel #repl > let Nullable = fun Contract => - std.contract.custom - (fun label value => - if value == null then - 'Ok value - else - std.contract.check Contract label value - ) + std.contract.custom (fun label value => + if value == null then + 'Ok value + else + std.contract.check Contract label value + ) > null | Nullable Number null @@ -880,37 +874,35 @@ value which is wrapping the original value with delayed checks inside**: ```nickel { NumberBoolDict = - std.contract.custom - (fun label value => - let with_delayed_checks = - value - |> std.record.map - (fun name value => - let label_with_msg = - std.contract.label.with_message "field `%{name}` is not a boolean" label - in - # Note: we use `apply` and not `check` here since we - # are inside a delayed check - std.contract.apply Bool label_with_msg value - ) - in - - if std.is_record value then - value - |> std.record.fields - |> std.array.fold_right - (fun field_name rest => - if std.string.is_match "^\\d+$" field_name then - rest - else - 'Error { - message = "field name `%{field_name}` is not a number" - } - ) - ('Ok with_delayed_checks) - else - 'Error { message = "not a record" } - ) + std.contract.custom (fun label value => + let with_delayed_checks = + value + |> std.record.map (fun name value => + let label_with_msg = + std.contract.label.with_message "field `%{name}` is not a boolean" label + in + # Note: we use `apply` and not `check` here since we + # are inside a delayed check + std.contract.apply Bool label_with_msg value + ) + in + + if std.is_record value then + value + |> std.record.fields + |> std.array.fold_right + (fun field_name rest => + if std.string.is_match "^\\d+$" field_name then + rest + else + 'Error { + message = "field name `%{field_name}` is not a number" + } + ) + ('Ok with_delayed_checks) + else + 'Error { message = "not a record" } + ) } ``` diff --git a/doc/manual/correctness.md b/doc/manual/correctness.md index 577b419c4d..f80c1c43de 100644 --- a/doc/manual/correctness.md +++ b/doc/manual/correctness.md @@ -123,17 +123,16 @@ of keys and an array of values. Let's call it `split`: #hide-range{1-14} > let split = fun pairs => - std.array.fold_right - ( - fun pair acc => - { - # problem: the correct expression to use is [pair.key] - keys = acc.keys @ [pair.key], - values = acc.values @ [pair.value], - } - ) - { keys = [], values = [] } - pairs + std.array.fold_right + (fun pair acc => + { + # problem: the correct expression to use is [pair.key] + keys = acc.keys @ [pair.key], + values = acc.values @ [pair.value], + } + ) + { keys = [], values = [] } + pairs > split [{key = "foo", value = 1}, {key = "bar", value = 2}] { keys = [ "bar", "foo" ], values = [ 2, 1 ], } @@ -156,13 +155,12 @@ first wrapping it in an array (note that in real life, you should rather use { split = fun pairs => std.array.fold_right - ( - fun pair acc => - { - # problem: the correct expression to use is [pair.key] - keys = acc.keys @ pair.key, - values = acc.values @ [pair.value], - } + (fun pair acc => + { + # problem: the correct expression to use is [pair.key] + keys = acc.keys @ pair.key, + values = acc.values @ [pair.value], + } ) { keys = [], values = [] } pairs @@ -191,9 +189,10 @@ elements of the same type as the input `value`s. An idiomatic way to express these properties in Nickel is to use the following annotation: -```nickel #no-check -forall a. Array {key: String, value: a} - -> {keys: Array String, values: Array a} +```nickel +forall a. + Array {key: String, value: a} + -> {keys: Array String, values: Array a} ``` Where `forall a.` means that `a` can be any type, but that the `a` in the input @@ -326,7 +325,8 @@ level is valid: # lib.ncl { OptLevel = std.contract.from_predicate (fun value => - std.array.elem value ["O0", "O1", "O2", "O3"]) + std.array.elem value ["O0", "O1", "O2", "O3"] + ) } ``` diff --git a/doc/manual/syntax.md b/doc/manual/syntax.md index 7d7dbb3daf..6c0266733b 100644 --- a/doc/manual/syntax.md +++ b/doc/manual/syntax.md @@ -607,8 +607,8 @@ Here are some examples of function definitions in Nickel: 3 > let add = fun a b => a + b in - let add1 = add 1 in - add1 2 + let add1 = add 1 in + add1 2 3 ``` @@ -623,15 +623,15 @@ them inside parentheses, for example: 3 > let increment = fun n => (+) 1 n in - increment 41 + increment 41 42 > let increment = (+) 1 in - increment 41 + increment 41 42 > let flatten = std.array.fold_right (@) [] in - flatten [[1, 2], [3], [4, 5]] + flatten [[1, 2], [3], [4, 5]] [ 1, 2, 3, 4, 5 ] ``` diff --git a/doc/manual/tutorial.md b/doc/manual/tutorial.md index a568bdfcec..5541294aa4 100644 --- a/doc/manual/tutorial.md +++ b/doc/manual/tutorial.md @@ -62,8 +62,7 @@ thanks to the LSP, and to document your code at the same time. ```nickel { - UserSchema = - { + UserSchema = { name | String, ssh-keys @@ -89,29 +88,30 @@ to use. ```nickel #parse let { UserSchema, .. } = import "users-schema.ncl" in { - users | Array UserSchema = [ - { - name = "Alice", - is-admin = true, - extra-groups = ["support"], - ssh-keys = [ - "AAAAAe4QAAAAAB5OLAo1...... alice@nixos", - "AAAAA26vx+AqGIPZd/NT...... alice@android", - ], - }, - { - name = "Bob", - extra-groups = ["pentester"], - }, - { - name = "Mallory" - }, - { - name = "Grace", - is-admin = true, - extra-groups = ["administrative"], - }, - ] + users | Array UserSchema + = [ + { + name = "Alice", + is-admin = true, + extra-groups = ["support"], + ssh-keys = [ + "AAAAAe4QAAAAAB5OLAo1...... alice@nixos", + "AAAAA26vx+AqGIPZd/NT...... alice@android", + ], + }, + { + name = "Bob", + extra-groups = ["pentester"], + }, + { + name = "Mallory" + }, + { + name = "Grace", + is-admin = true, + extra-groups = ["administrative"], + }, + ] } ``` diff --git a/doc/manual/types-vs-contracts.md b/doc/manual/types-vs-contracts.md index 0c9a1dd097..0df0847aab 100644 --- a/doc/manual/types-vs-contracts.md +++ b/doc/manual/types-vs-contracts.md @@ -83,6 +83,7 @@ let Schema = { = [], } in + { name = "hello", build = [ diff --git a/doc/manual/typing.md b/doc/manual/typing.md index b964e2c40d..b42fbd6a82 100644 --- a/doc/manual/typing.md +++ b/doc/manual/typing.md @@ -232,12 +232,11 @@ The following type constructors are available: ```nickel let protocol_id : [| 'http, 'ftp, 'sftp |] -> [| 'Ok Number, 'Error String |] - = - match { - 'http => 'Ok 1, - 'ftp => 'Ok 2, - 'sftp => 'Error "SSL isn't supported", - } + = match { + 'http => 'Ok 1, + 'ftp => 'Ok 2, + 'sftp => 'Error "SSL isn't supported", + } in protocol_id 'http ``` @@ -403,11 +402,11 @@ let snd : forall a b. a -> b -> b = fun x y => y in Or even nest them: ```nickel -let higherRankId : forall a. (forall b. b -> b) -> a -> a +let higher_rank_id: forall a. (forall b. b -> b) -> a -> a = fun id x => id x in let id : forall a. a -> a = fun x => x in -higherRankId id 0 : Number +higher_rank_id id 0 : Number ``` #### Type inference and polymorphism @@ -525,10 +524,10 @@ tail that can be substituted for something else. For example: ```nickel { port_of : forall a. [| 'http, 'ftp; a |] -> Number = match { - 'http => 80, - 'ftp => 21, - _ => 8000, - } + 'http => 80, + 'ftp => 21, + _ => 8000, + } } ``` @@ -545,12 +544,11 @@ an enum type that can be extended arbitrarily. This typically happens when a match expression has a catch-all case: ```nickel #repl -> let is_ok : forall a b tail. [| 'Ok a, 'ok b ; tail |] -> Bool = - match { - 'Ok x => true, - 'ok x => true, - _ => false, - } +> let is_ok : forall a b tail. [| 'Ok a, 'ok b ; tail |] -> Bool = match { + 'Ok x => true, + 'ok x => true, + _ => false, + } in is_ok 'other false @@ -690,9 +688,9 @@ calling to the statically typed `std.array.filter` from dynamically typed code: ```nickel #repl > std.array.filter (fun x => if x % 2 == 0 then x else null) [1,2,3,4,5,6] error: contract broken by the caller of `filter` - ┌─ :429:25 + ┌─ :427:25 │ -429 │ : forall a. (a -> Bool) -> Array a -> Array a +427 │ : forall a. (a -> Bool) -> Array a -> Array a │ ---- expected return type of a function provided by the caller │ ┌─ :1:55 @@ -826,7 +824,8 @@ let Port = std.contract.from_predicate (fun value => std.is_number value && value % 1 == 0 && value >= 0 - && value <= 65535) in + && value <= 65535) +in (10 - 1 : Port) ``` @@ -837,9 +836,9 @@ Result: ```text error: incompatible types - ┌─ :7:2 + ┌─ :8:2 │ -7 │ (10 - 1 : Port) +8 │ (10 - 1 : Port) │ ^^^^^^ this expression │ = Expected an expression of type `Port` (a contract) diff --git a/examples/config-gcc/config-gcc.ncl b/examples/config-gcc/config-gcc.ncl index 58e39a1676..874ec26848 100644 --- a/examples/config-gcc/config-gcc.ncl +++ b/examples/config-gcc/config-gcc.ncl @@ -10,64 +10,56 @@ let GccFlag = std.array.elem (std.string.substring 0 1 string) supported_flags in - std.contract.custom - ( - fun label => - match { - value if std.is_string value && is_valid_flag value => 'Ok value, - { flag, arg } if std.array.elem flag supported_flags => - # We normalize a record representation to a string - 'Ok "%{flag}%{arg}", - value if std.is_string value => - 'Error { message = "unknown flag %{value}" }, - { flag, arg = _ } => - 'Error { message = "unknown flag %{flag}" }, - { .. } => - 'Error { - message = "bad record structure: missing field `flag` or `arg`", - }, - _ => 'Error { message = "expected record or string" }, - } - ) + std.contract.custom (fun label => + match { + value if std.is_string value && is_valid_flag value => 'Ok value, + { flag, arg } if std.array.elem flag supported_flags => + # We normalize a record representation to a string + 'Ok "%{flag}%{arg}", + value if std.is_string value => + 'Error { message = "unknown flag %{value}" }, + { flag, arg = _ } => + 'Error { message = "unknown flag %{flag}" }, + { .. } => + 'Error { + message = "bad record structure: missing field `flag` or `arg`", + }, + _ => 'Error { message = "expected record or string" }, + } + ) in let Path = let pattern = m%"^(.+)/([^/]+)$"% in - std.contract.from_validator - ( - fun value => - if std.is_string value then - if std.string.is_match pattern value then - 'Ok - else - 'Error { message = "invalid path" } - else - 'Error { message = "not a string" } - ) + std.contract.from_validator (fun value => + if std.is_string value then + if std.string.is_match pattern value then + 'Ok + else + 'Error { message = "invalid path" } + else + 'Error { message = "not a string" } + ) in let SharedObjectFile = - std.contract.from_validator - ( - fun value => - if std.is_string value then - if std.string.is_match m%"\.so$"% value then - 'Ok - else - 'Error { message = "not an .so file" } - else - 'Error { message = "not a string" } - ) + std.contract.from_validator (fun value => + if std.is_string value then + if std.string.is_match m%"\.so$"% value then + 'Ok + else + 'Error { message = "not an .so file" } + else + 'Error { message = "not a string" } + ) in let OptLevel = - std.contract.from_predicate - ( - match { - 0 or 1 or 2 => true, - _ => false, - } - ) + std.contract.from_predicate (match { + 0 or 1 or 2 => true, + _ => false, + } + ) in let Contract = { diff --git a/examples/foreach-pattern/foreach-pattern-on-import.ncl b/examples/foreach-pattern/foreach-pattern-on-import.ncl index c9f8195892..4adc3f42ee 100644 --- a/examples/foreach-pattern/foreach-pattern-on-import.ncl +++ b/examples/foreach-pattern/foreach-pattern-on-import.ncl @@ -2,12 +2,10 @@ { posix_users = (import "data_users.yml").users - |> std.array.map - ( - fun name => - { - username = name, - email = "%{name}@nickel-lang.org" - } - ) + |> std.array.map (fun name => + { + username = name, + email = "%{name}@nickel-lang.org" + } + ) } diff --git a/examples/foreach-pattern/foreach-pattern.ncl b/examples/foreach-pattern/foreach-pattern.ncl index a2c873b35d..1abddffa42 100644 --- a/examples/foreach-pattern/foreach-pattern.ncl +++ b/examples/foreach-pattern/foreach-pattern.ncl @@ -2,12 +2,10 @@ { user = ["jane", "pete", "richie"] - |> std.array.map - ( - fun name => - { - username = name, - email = "%{name}@nickel-lang.org", - } - ) + |> std.array.map (fun name => + { + username = name, + email = "%{name}@nickel-lang.org", + } + ) } diff --git a/examples/record-contract/record-contract.ncl b/examples/record-contract/record-contract.ncl index ff888d09bc..a78406ccaf 100644 --- a/examples/record-contract/record-contract.ncl +++ b/examples/record-contract/record-contract.ncl @@ -10,14 +10,12 @@ # https://github.com/tweag/nickel-kubernetes/ instead let Port | doc "A contract for a port number" = - std.contract.from_predicate - ( - fun value => - std.is_number value - && (value % 1 == 0) - && (value >= 0) - && (value <= 65535) - ) + std.contract.from_predicate (fun value => + std.is_number value + && (value % 1 == 0) + && (value >= 0) + && (value <= 65535) + ) in let PortElt | doc "A contract for a port element of a Kubernetes configuration" diff --git a/examples/simple-contracts/simple-contract-div.ncl b/examples/simple-contracts/simple-contract-div.ncl index 66d10e5369..3777d2518e 100644 --- a/examples/simple-contracts/simple-contract-div.ncl +++ b/examples/simple-contracts/simple-contract-div.ncl @@ -3,19 +3,15 @@ # /!\ THIS EXAMPLE IS EXPECTED TO FAIL # Illustrates a basic contract violation. let Even = - std.contract.from_predicate - ( - fun value => - std.is_number value && value % 2 == 0 - ) + std.contract.from_predicate (fun value => + std.is_number value && value % 2 == 0 + ) in let DivBy3 = - std.contract.from_predicate - ( - fun value => - std.is_number value && value % 3 == 0 - ) + std.contract.from_predicate (fun value => + std.is_number value && value % 3 == 0 + ) in # Will cause an error! 4 is not divisible by 3. diff --git a/flake.lock b/flake.lock index ee5c4502b8..374c474261 100644 --- a/flake.lock +++ b/flake.lock @@ -324,11 +324,11 @@ ] }, "locked": { - "lastModified": 1718001536, - "narHash": "sha256-gEgYnX8KyJV0c5wAIN0QAf9/vFuCzEVoaqb12UfQgSQ=", + "lastModified": 1725634392, + "narHash": "sha256-xpxdzcIXR/ISpfE74tc+8N225RSPnZbFPEay78kcnC4=", "owner": "tweag", "repo": "topiary", - "rev": "c92b16c72707549fbc1dcded4accba427be658e6", + "rev": "cff73fc3d58490b260935a4d9968a180acf75b8b", "type": "github" }, "original": {