Crystal: Change syntax of `as` to look like a method

Created on 20 Apr 2016  路  13Comments  路  Source: crystal-lang/crystal

Right now as looks like this:

exp as Type

It's the only expression (I think) that doesn't look like a method call. Every other "special" expression (is_a?, responds_to?, sizeof, pointerof, uninitialized, include, typeof, nil?) looks like a method.

What if we change it to:

exp.as(Type) # or just exp.as Type

Pros:

  • Looks like a method, so it's similar to every other "special" method and there isn't a different syntax for just one construct
  • Doesn't need parens at the beginning when chaining method: (exp as Type).method vs. exp.as(Type).method. Parens at the beginning sometimes means I have to go back, add them, and then go forward when typing. The last form is also maybe easier to read.
  • Can be used with &.: exp.method &.as(Int32) vs. exp.method { |x| x as Int32 }

Cons:

  • Maybe special expressions should have a different syntax (in other languages they usually do). However every other special expression in Crystal already looks like a method, so...
  • Existing code will break. However, we can keep both syntaxes working for a couple of releases and have the formatter rewrite the current form to the new one (though maybe it will end up with extra parens).

Bouns

As a bonus, we can later add a similar special expression, exp.as?(T), that returns exp as T if the expression is of type T, or returns nil otherwise. So one can do:

foo.bar.baz.as?(Int32).try &.abs

instead of being forced to use a local variable:

tmp = foo.bar.baz
tmp.is_a?(Int32) ? tmp.abs : nil

This is actually inspired by Kotlin's as?.

draft compiler

Most helpful comment

I like the consistency, I just want to give an example why doing it with as can be a bad idea.

as can be used in a huge variety of contexts and it can be a part of many different existing DSL-s, which can't be said for words like

responds_to?, sizeof, pointerof, uninitialized, include, typeof, nil?

(e.g. in a parser DSL like here: lingo )

All 13 comments

@BlaXpirit @lbguilherme Why the downvotes? I'm interested in good arguments against this change.

@asterite: I don't really have any strong point against it, just a matter of style and syntax. Doing it as an operator looks odd to me, specially because of the dot there.

what about change x = [] of Int32 to x = [].of(Int32)

@kostya Such change would definitely make writing Ruby compatible code easier (#593), because it is possible to monkey patch the of and as methods in some compatibility layer.

An alternative syntax might be probably x = [].as(Array(Int32)), which also gets rid of theof keyword. Still this means more typing, which is a bad thing.

@ssvb these are not methods, they're, hmm, "pseudo methods" - keywords in practice. The change doesn't make it a method, it simply uses dot-method syntax.

If it were method-looking, I'd expect [Int32, Int16].each {|k| p 3.as(k)} to work, which I don't think it does currently.

@will, this does not work:

[Int32, Int16].map {|k| p 3.is_a?(k)}

@kostya It's true that of is similar to as, in that it has space around it. However, of is only useful for array/hash literals, so it's not a general thing. There's also no &.of(...) or similar. It's true that for chaining you have to do ([] of Int32).method, but the only places I found that are in specs.

And it's true that as and is_a? are special in that they accept an explicit type (they can't accept a varaible). However, at least for is_a?, we could make it work in those cases, so having it look like a method isn't that strange.

Syntax sugar is a part of any language, so in this case this is a sugar of Crystal. If we are going to make everything looks like a method call, then probably let's do calculation only this way:

1.+(2)

:smile:

I like the consistency, I just want to give an example why doing it with as can be a bad idea.

as can be used in a huge variety of contexts and it can be a part of many different existing DSL-s, which can't be said for words like

responds_to?, sizeof, pointerof, uninitialized, include, typeof, nil?

(e.g. in a parser DSL like here: lingo )

Conflating methods and operators seems perilous: confuses new developers and makes the learning curve and documentation burden even higher.

Syntax like Go's type assertion style would be simpler, less confusing and easier to explain and document:

exp.(Type).method or exp.Type.method

And much easier to read quickly.

@steakknife But how are methods different from operators? Both takes data in and produce data out, possibly with a different type. as can pretty much be a method, or at least look like a method. Making these things look like methods means less things to learn, not more. They use the same familiar method calling syntax.

@steakknife, aside from the method/operator confusion (as is neither): that syntax idea is very interesting!

Was this page helpful?
0 / 5 - 0 ratings