-
Notifications
You must be signed in to change notification settings - Fork 10
Implement ADT framework #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
If we optionally allow providing a function as a matcher on a
single-constructor type, then it follows that we would allow a value for
matching on a unary type as well:
```
> unit = adt.struct "unit" adt.fields.none
> unit.match unit.ctors.make 5
5
```
However, this would make using the long-form attrset for matching on the
same type ambiguous:
```
> unit = adt.struct "unit" adt.fields.none
> unit.match unit.ctors.make { make = 5; }
\# should this be `5` or `{ make = 5; }`?
```
To prevent this, this switches back to requiring attrsets to avoid
ambiguity.
|
i had a thought that maybe an adt framework should be implemented outside of nix-std. i'm not sure though. either that or we can implement it here, and make sure it's known to be very experimental. |
|
what it is ? https://en.wikipedia.org/wiki/Abstract_data_type ? sounds fits LISP (NIX). like define type system and rules and generate types adhering these. |
|
No; algebraic data types, a la structs and Rust-style enums |
|
for Nix to handle K8S YAMLs it kind of should be somewhat there (enum), they often has several types for same YAML node value. none of K8S nix integration do that (yet), neither modules do that too https://github.com/NixOS/nixpkgs/blob/master/lib/options.nix |
Tracking PR for WIP implementation of automatic ADT generation.
Currently, the API is as follows:
Call
adt.newwith a set of constructor names to constructor specifications to automatically generate an ADT. The returned set includes a match function, a check function, and an attrset of the constructorsadt.structis shorthand for ADTs with only one constructor, and names the constructormake.Every constructor has a specification for its fields, which can be typed or untyped. Available specifications are in
adt.fields.positional [ { name = "x"; type = types.int; } { name = "y"; type = types.int; } ]would make a constructor likemake 1 2, and a match function like{ make = x: y: ...; }positional_ [ "x" "y" ]is the same, but untypedrecord { x = types.int; y = types.int }would make a constructor likemake { x = 1; y = 2; }, and a match function like{ make = { x, y }: ...; }record_ ["x" "y"]is the same, but untypednoneis for unary constructors, likeadt.struct "unit" adt.fields.noneanon [ types.int types.int ]is likepositional, but the internal field names are automatically generatedanon_ 2is likepositional_, but the internal field names are automatically generatedCurrently, the only typechecking is via
check. Constructors do not typecheck their arguments, since this would be awful for slow-to-check types.checkwill check both the field types andadt's autogenerated annotationsmatchis used the same aslist.match, etc. Whilematchcould hypothetically be a key in the value, I kept it separate for consistency with these functions.Some questions to answer:
check?adt.structname the single constructormake,new, etc., or name it after the type name? For example,unit = adt.struct "unit" adt.fields.nonewould currently haveunit.ctors.make, which doesn't make too much sense, but the user can always unpack the result and rename everything.adt.structused to allow optionally using a plain function (e.g.point.match p (x: y: x + y)) instead of the full single-element attrset (e.g.point.match p { make = x: y: x + y; }) but this was found to be ambiguous on types with one value, where you naturally could just use a value here. For example, shouldunit.match x { make = 5; }return5or{ make = 5; }? As a solution, I temporarily removed the shorthand. One possible solution is to arbitrarily require a one-argument function for such a type that we arbitrarily passnullto, but this is pretty inconsistent and not that great either.ctorsfield and we just have the user unpack everything into the API they want to expose?__toString? Could we support user-generated__toStrings?adt.new "ordering" { gt = adt.fields.none; eq = adt.fields.none; lt = adt.fields.none; }. Where would we put these?Also could use tests and much better docs.