Comments (2)
If you want to do this, I think you should trade either the =
sign or the :
sign for a |
(contract annotation), depending on what you want to express.
Writing {inputs = {number : Number}}
has the following effect:
{number : Number}
is a record type. Why it's not a record with an undefined field of type number is dictated by specific rules, that have to be in place because we decided to have a unified syntax for types, contracts and values. For more details, see the relevant section of the manual. But basically it has a field with a type annotation and without a definition.- you use
=
, the field assignment operator. It means "put the value ... inside theinputs
field". Here, the value is a type. As per the idea behind having a unified syntax, we can use types as normal values: in that case, they are converted to their corresponding contract. Because record types and record contracts have subtle but important differences, this isn't just a normal record, but some opaque function. The only thing you can do, mostly, is to use it in another type or contract annotation, or to apply it usingstd.contract.apply
.
So, what you get is {inputs = some_obscure_internal_function_generated_by_the_interpreter}
, which I imagine is not what you want, because it can't be merged with anything meaningfully.
You have several possible fixes:
inputs | {number : Number}
: now, you don't give a value toinputs
, but you're saying "this is the shapeinputs
should have", and provide a type. It's closer to what you want to express, but see next point.inputs | {number | Number}
: it's very close to the previous suggestion, but I think it make more sense in your context. Type annotation are useful to typecheck a block statically, but here,inputs
doesn't have a definition. So the type will be converted to a contract, and having a static type annotation in the first place is both a bit misleading and not very useful, so I would advise to use|
everywhere as soon as you are in "dynamic contract check" land anyway.inputs = {number | Number}
. This one would work as well. The difference with the previous solution is subtle, but the idea is that when you attach a contract toinputs
directly using|
, as in the previous solutioninputs | {number | Number}
, you say "I impose that the shape ofinputs
must be the following, whatever it is merged with, or overridden by". In particular you can't add a new field which is notnumber
. Even if you override inputs with merging, the new value has to respect the contract. On the other hand, writinginputs = {number | Number}
is to merely provide an incomplete initial value toinputs
. If you're overridingnumber
directly, the effect will be similar, but this is more permissive, as you can add other fields, or even overrideinputs
with something totally different that isn't even a record.
I hope it's not too confusing, but I wanted to explain a bit the different possibilities, because it's not always obvious what to do, when you can chose either |
, :
or =
in many places when defining such nested record contracts. In the end, I would go with .2
, and more generally to use |
everywhere it makes sense, because 2. works (as opposed to the original try), is more restrictive (you can't override inputs with something totally unrelated), and doesn't mix static types and dynamic contract in setting which will be mostly dynamic anyway.
let node1 = {inputs | {number | Number}, outputs | {number | Number}} in
let node2 = {inputs | {number | Number}, outputs | {number | Number} = {number = inputs.number * 2}} in
let compose = fun n1 n2 => n2 & {inputs=n1.outputs} in
compose node1 node2
Now, you get a better motivated "missing definition for outputs
", because indeed node1.outputs
is not defined:
error: missing definition for `outputs`
┌─ repl-input-1:3:44
│
1 │ let node1 = {inputs | {number | Number}, outputs | {number | Number}} in
│ --------------------------------------------------------- in this record
2 │ let node2 = {inputs | {number | Number}, outputs | {number | Number} = {number = inputs.number * 2}} in
3 │ let compose = fun n1 n2 => n2 & {inputs=n1.outputs} in
│ ---^^^^^^^
│ │ │
│ │ required here
│ accessed here
From there, you might have another problem which is that all your inputs
will be merged together, which is probably not what you want. Maybe you'll have to "namespace" your inputs when merging, to make a dinstinction between "global resulting inputs after the merge", "old inputs of node1 now unified with global resulting inputs", "old inputs of node2 now unified with node1.outputs" (and same thing for outputs).
from nickel.
We've fiddled with that idea a bit more, and here is a more polished version of composition:
let node1 = {
Input = { number | Number },
inputs | Input,
outputs | { number | Number } = {
number =
inputs.number + 1
},
}
in
let node2 = {
Input = { number | Number},
inputs | Input,
outputs | { number | Number } = {
number =
inputs.number * 2
}
}
in
let compose = fun n1 n2 =>
{
Input = n1.Input,
inputs | n1.Input,
outputs =
let inputs' = inputs in
let n1' = n1 & { inputs = inputs' } in
(n2 & { inputs = n1'.outputs }).outputs,
}
in
let node3 = (compose node1 node2) in
let composed = (compose node3 node1) in
(composed & {inputs.number = 2}).outputs
==> { number = 7 }
In the end, it's "just" encoding functions as recursive record, which is in some sense a natural encoding. That's also the idea behind NixOS modules versus packages-as-functions, and the Package As Record Model proposed by Nickel-Nix RFC.
Anyway, I think we can consider the original question to be solved. Feel free to re-open if not (or open a new issue or discussion for a related but separate matter).
from nickel.
Related Issues (20)
- Dynamic import resolution HOT 5
- Confusing merge behaviour in `std.record.{update,remove}` HOT 1
- `std.string.find_all` HOT 1
- Add `record.has_field_all` and `record.fields_all` HOT 1
- Add raw strings
- NLS crashes on recursive definitions HOT 11
- Proposal: freeze recursive records upon insert/remove/update/map
- NLS reports same diagnostic message multiple times HOT 3
- VSCode extension does not show hints for functions annotated with record contract HOT 1
- VSCode Extension should not disregard cursor position and collapse all symbol breadcrumbs to "..." in all occasions
- Weird `nix develop` error HOT 9
- Strange error in std.array.any HOT 2
- Provide contract interface to pyckel HOT 1
- Add functions to create and generically unwrap enum variants HOT 2
- Inconsistent behaviour while loading multiple files vs single file HOT 2
- Getting `record_insert: tried to extend but already exists` when using patterns with duplicate bound variables HOT 2
- Memory leak with long-running nickel process HOT 1
- Opaque foreign values HOT 1
- Update documentation for new pattern features
- Accessing late-bind field fails HOT 6
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nickel.