Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

For named parameters, this is the best one can do in JavaScript without constructing a fully static type analyzer:

let use-named({ name, age }) ->

use-named(name: "ckknight", age: 25)

For refinements, what are you referring to?



Ah, destructuring is so sweet!

You're right, it's no real need for it and it's unfeasible because you need to either (a) fully static type analyze everything or (b) what anonymous just explained... and they're both out of the question.

The refinements thing was a joke, in the spirit of what else can one add: it's a controlled form of monkeypatching / classboxing about which a lot of fuss was made in the Ruby community after Matz decided to add it to the language (http://timelessrepo.com/refinements-in-ruby), mainly because it's very hard to implement without creating more problems, even if you have your own interpreter to hack on (short answer: don't bother looking about it and even if you're smart enough to figure out a way to add it to a compile-to-JS language, don't waste your effort on it - they're not an anti-feature and they could make sense in languages that allow monkeypatching, like JS and Ruby, but they're too much work for too little benefit and too much possibility for bad use and unneeded complexity).


Actually, here's a better example, if you want defaults.

Also nice is that it doesn't have to create a full defaults object at runtime, and each binding would only be evaluated if individually needed:

    let func-with-named-params({
      id as String // TypeError if not a String
      metadata // don't care about type, defaults to void
      seed as Number = get-random-seed() // requires a Number, only calls function if not provided.
    }) ->
You can do all sorts of things with object destructuring.

Yeah, Refinements seem like a similar thing to extension classes in .NET where methods defined somewhere else affect a class but only if included, without polluting the globals.

The only possible way I could see supporting something like this is with a different method call operator, so something like:

NOTE: The following is theoretical code. It does not work in GorillaScript 0.9.x, but might be implemented if there is enough positive feedback for it.

    refinement String
      def repeat(count as Number)
        if count < 1
          ""
        else if count < 2
          this
        else if count bitand 0b1 // if it's odd
          this->repeat(count - 1) & this
        else
          (this & this)->repeat(count bitrshift 1)
    
    // and used as such:
    "Hello "->repeat(5) == "Hello Hello Hello Hello Hello "
    // would turn into this code:
    String_$_repeat("Hello ", 5)
    
    let unknown(x)
      // This would throw a compile-time error, as x's type
      // would be unknown. It would have to be a single
      // named type that has a refinement defined, either at
      // the top of the file or with an import.
      x->repeat(5)
Also, refinements could have macros on them, so one could do

    "Hello"->for-code-point point, index
      // This is a body in a macro defined as "for-code-point" on a String refinement.
      do-something(point)
So, this would be a nice way to have refinements, but have the terrible downside of requiring the type to be statically known.

You could also define a refinement on a union type or even an object type, such as the following:

    refinement Array|{ length: Number }
      // define a getter, why not?
      def get median()
        this[this.length \ 0]
      
      macro loop
        syntax item as Identifier, index as (",", index as Identifier)?, body as Body
          index ?= @tmp \i
          AST
            let mutable $index = 0
            while $index < @length, $index += 1 // it knows that `@length` is a Number, no need to type checking
              let $item = this[$index]
              $body

      def each(callback)
        // look, we're using the macro we just defined!
        this->loop value, index
          callback value, index
The refinement namespace would not conflict with the normal invocation namespace, so you could have `"Hello".same()` and have it be exactly as expected, but `"Hello"->same()` not, because it would turn into `String_$_same("Hello")` (or something like it).

To clarify before, the following would work:

    refinement Number
      // automatically knows it returns a Number
      def log(base as Number = Math.E)
        Math.log(this) / Math.log(base)
    
    let alpha = 1e100->log(10) // knows that 1e100 is a Number, obviously, and since ->log returns a Number, alpha is automatically known to be a Number.
    let bravo = alpha->log(10) // knows alpha is a Number, etc.
    let charlie = Math.pow(2, 10)->log(2) // since we know Math.pow returns a Number, we're good.
    let delta as Number = some-library.some-unknown-method()
    let echo = delta->log() // we specifically declared delta as a Number, so we can use ->log
    
    let foxtrot = [1, 2, 3]->median // Array subtypes from { length: Number }
    let golf = "hotel"->median // Hey, so does String!
    let india = arguments->median // And so does Arguments
    let juliet = { length: 4, 2: \kilo } // And so does this custom type
    let lima as { length: Number } = some-outside-source()
    let mike = lima->median
    // assuming I come up with a "cast" operator, i.e. type-
    // check with error or default value
    // this would allow a String, Array, etc., would check
    // at runtime (but not in production, with
    // DISABLE_TYPE_CHECKING set to true)
    let november = (some-outside-source() as { length: Number })->median
    // require that the result is an array, otherwise
    // evaluate to []. The default could be an expensive
    // operation that is only executed when necessary.
    let oscar = (some-outside-source() as Array = [])->median

    // unknown is of the "any" type, representing all
    // possible values, including null and void.
    let unknown = some-library.some-other-method()
    // Since unknown isn't at least { length: Number }, we
    // don't know that ->median should map to our refinement
    // { length: Number }->median and thus cannot be used
    // without casting.
    let wrong = unknown->median
I dunno, something to think about. Would people want to have this feature?


I'm guessing he had in mind this (very controversial) feature that was proposed for Ruby 2.0 and then scaled back: http://www.rubyinside.com/ruby-refinements-an-overview-of-a-....




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: