Giter VIP home page Giter VIP logo

Comments (6)

yannham avatar yannham commented on May 27, 2024 1

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.

yannham avatar yannham commented on May 27, 2024

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.

vkleen avatar vkleen commented on May 27, 2024

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.

suimong avatar suimong commented on May 27, 2024

@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.

yannham avatar yannham commented on May 27, 2024

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.

suimong avatar suimong commented on May 27, 2024

Got it. It is still pretty exciting to be able to manipulate types as values, especially after ADT lands!

from nickel.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.