Comments (6)
Discussed in the weekly meeting. There's really no good reason not to let types live as values, and only convert them to contracts when needed (and there are many good reason to do so, this issue being an example).
from nickel.
Currently, types are evaluated away to their corresponding contract, which is a generated function - you can observe it from the REPL:
nickel> [| 'A, 'B, 'C |]
fun
label
value
=>
if (%typeof% value) == 'Enum then
(%apply_contract% case label) value
else
%blame% (%label_with_message% "not an enum tag" label)
nickel>
Thus, from within the language, you can't really inspect them at the moment - even from a hypothetical new primitive operator.
That being said, the only reasonable operation to perform with a type used as a value is to apply it as a contract. The current situation is more of an implementation detail, and I don't think we make any guarantee that what you get is a proper function that you can apply normally - it could very well be an opaque value.
What I'm getting at is that I don't think it would be an issue to not evaluate types away, but just consider them values (already evaluated form). apply_contract
could then take on the responsibility of converting them to actual functions, to recover the current behavior. Doing so, we could then have new primitive operators - and, using those primitive operators, normal stdlib functions - able to introspect types at runtime and implement what you want.
I'm putting this on the agenda of the next weekly meeting.
from nickel.
I've had more luck going in the other direction, generating (something similar to) Foo
from Tags
:
{
Tags = [ 'A, 'B, 'C ],
Foo = fun l v =>
v
|> std.contract.apply std.enum.Tag l
|> (
fun v =>
if std.array.elem v Tags then
v
else
std.contract.blame_with_message "%{std.string.from v} is not a valid tag" l
),
from nickel.
@yannham Thank you for the helpful explanation. While obvious (after reading your comment), it didn't strike me that Foo
is actually a type, and it's definitely enticing to think that Nickel is capable of type level programming, however limited. Just out of curiosity, if you were to "not evaluate types away", what would this "type" look like?
@vkleen That was a very nice workaround, thanks! My use case is to define two enums, say Foo
and Bar
, that represent two "dimensions" for a particular config, and while generating a matrix of Foo × Bar
, applying rules to certain set of (<Foo tag>, <Bar tag>)
combinations. In fact, it seems more natural to define Foo as ["A", "B", "C"] and build the enum contract.
from nickel.
So, it's not exactly type-level programming per se, although it looks like it, because you can't really do anything on types at typechecking time (that is, statically, before evaluation). Like, say, unionize two enum types: the typechecker won't understand that.
But types can be used to build contracts, and you can include contracts within types (they act as opaque types at typechecking time), so we thought that it makes more sense to have a unified syntax, whether you use types in a static type annotation, or to build a contract, which is value. You can find more context and detailed information in RFC002.
if you were to "not evaluate types away", what would this "type" look like?
At parsing time, types appearing in value position - such as let x = Number -> Number in ...
are already put in a special Type
node in the Nickel abstract syntax tree. This node contains the internal Nickel representation of a type (that is, it's just another AST as well). Currently we evaluate this node away to the corresponding contract whenever we encounter a type (you can always derive a contract, that is a function or a record in practice, from a type - there's a mechanical way to do that), but we could just keep the current Type
node. You could see it as a runtime representation of the type. Once we have that, we can add various primitive operations to explore this representation, such as listing the tags of an enum type.
Heck, when algebraic data types land, you could even imagine reify types as Nickel values, for example std.reify_type (Number -> Number) == 'Arrow { domain = 'Native 'Number, codomain = 'Native 'Number }
or std.reify_type [| 'Foo, 'Bar |] == 'Enum [ 'EnumRowElt {tag = 'Foo}, 'EnumRowElt {tag = 'Bar} ]
, and process that however you want.
from nickel.
Got it. It is still pretty exciting to be able to manipulate types as values, especially after ADT lands!
from nickel.
Related Issues (20)
- 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 4
- 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 1
- 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
- Update documentation for new pattern features
- Accessing late-bind field fails HOT 6
- Context-sensitive completion in match cases
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.