Skip to content
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

[RFC] Voluntary, scoped, "explicit"/"strict" modes (constraints and demands on code) #900

Closed
ozra opened this issue Jun 29, 2015 · 9 comments

Comments

@ozra
Copy link
Contributor

ozra commented Jun 29, 2015

I was hacking around in the lexer a bit for fun, refactored a bit, and had some errors that showed up some times, aka "slightly harder to debug than consistent ones".

In this case local vars keeps states over several screen pages in this long function. Refactoring gives no error because assigning to a symbol declares and allocate the variable.

The implicit allocation is a very nice feature in prototyping, script-style one-off programs, and shorter methods (which they should be, but reality...), which is one of those things that attract me to Crystal. In longer and/or more complex scenarios however, a bit of voluntary compile time protection is essential to remain efficient.

My proposal is something similar to "use strict mode" in some (semi) dynamic languages.

  • Preferably different demands on the code should be possible to make specifically
  • The constraints should be scoped
    • Probably constraint introduction should be limited to beginning of method, module or file scope.

Some voluntary constraints that come to mind, with loose syntactic ideas:

  • "Make variables immutable by default"
  • "Require variable declaration"
    • Initial use of variable must be declared my_var : _
    • When setting a value simultaneously, my_var := 47 or similar could be used for terseness
    • Perhaps let my_var = 47 for explicitly immutable (with "immutable by default", this would be equivalent to prior example, then some syntax to explicitly make vars mutable would be needed [perhaps mut like in rust, or an assign-modifier])
  • "Only pure 'function' (method) calls allowed in this scope"

The single one I find most important is the "require variable declaration" directive.
[ed: update:] And even more important: "require instance variable declaration".

I realize that this will complicate parsing a bit, making it more conditional, especially if many different combinations are allowed, and immutable must be introduced conceptually in semantics (strings are though, but only the value I guess?). The different constraints could be distilled to fewer parsing/constraint directives to simplify a bit.

And, I repeat, for those cringing: this would not affect anyone who doesn't touch the constraint directives, it would only be a help for writing more secure applications for those who find it useful to express their intentions a bit more verbose in order to get help from the compiler catching potential bugs earlier.

@asterite
Copy link
Member

asterite commented Jul 1, 2015

One thing I'd like to note is that "this would not affect anyone who doesn't touch the constraint directives" is never true, no matter what the language feature is.

If person A decides to use it but then person B wants to collaborate with A, B needs to learn the feature to understand the code. And B will probably want to follow A's style.

@refi64
Copy link
Contributor

refi64 commented Jul 1, 2015

This reminds me of two things I'd like to see:

  • A way of declaring a variable as a compile-time constant. Mostly for optimization purposes. This might already be possible and I didn't notice.
  • A variable annotation that forces stack allocation. Basically, it's the programmer's way of saying, "Shut up, I know what I'm doing!" This good be good for tight loops.

@ozra
Copy link
Contributor Author

ozra commented Jul 1, 2015

@asterite - of course you're right about that. Project owners dictates style. I reckon above 'hardening directives' would be in hard core central code, though, so anyone getting in to that is probably prepared to cope with a few additional rules. Macros is by far the greatest leap in making a language 'need to figure stuff out and learn more all the time', past that, I don't really see anything that can make it harder in that respect. For instance, I had an impossibly hard time figuring out where 'parse_or' was in the compiler until I was pointed to the macro via IRC ;-)

@kirbyfan64 - Use an uppercase letter as initial for a symbol you assign to: voila, constant. Foo = 47. For stack: use struct instead of class - it doesn't really solve the per-location variation of course, so maybe you were aware of that... Whether to use stack or heap should be possible for the compiler to solve via escape analysis though (when/if implemented)...

@jhass jhass added the RFC label Aug 31, 2015
@js-ojus
Copy link
Contributor

js-ojus commented Sep 8, 2015

Having debugged scores of subtle issues that boil down to typing mistakes in variable names (several that involve conditionally resetting a variable to a predetermined value), I have always felt that "declare before use" would be a great boon. I am, of course, talking about experiences with Ruby.

Crystal being a compiled language, it makes even more sense to have that rule.

On the smaller matter of syntax, I first encountered ":= for declaration+initialisation and = for assignment" in Caruso's Vision a couple of decades ago. In recent times, Go has popularised it again. Not a bad choice, in my opinion!

@ozra
Copy link
Contributor Author

ozra commented Oct 4, 2015

Yeah, @js-ojus, if type annotation was to be unified (see #758), it would be very natural, (if assignment was parsed after type annotation [seems more natural]):

a : Int32 = 47  # typed var, declared and assigned
b := 42  # declared var, no type after ':' = 'auto', and assigned
c = 48  # that was wrong!

Still though - I think the requirement should just be when "explicitly demanded" for the scope (be it def, module or file) - that way Crystal can still be used "script style", for one offs and the like, in a smooth fashion.

@js-ojus
Copy link
Contributor

js-ojus commented Oct 5, 2015

As recently as last week, I was stumped by this "declaration" problem yet again. In a current project, I encountered a puzzling compilation error when running tests. It was something like (I do not remember the exact message):

@assocs is not initialized in all paths

pointing to a line in some method in the struct and a constructor.

I checked the constructors, and could not find any problem. After a few confused minutes, I realised that the name I declared there was @assoc -- not a plural!

Life would have been uneventfully simple if the compiler just said unknown instance variable @assocs.

However, since declaration is not mandatory, the compiler possibly goes through a complicated process of scope hierarchy lookups upon encountering a new variable. Finally, its error message is technically correct but practically unhelpful.

I urge @asterite and team - yet again - to consider introducing mandatory declaration of variables, with := for declaration + initialisation, and = for assignment!

@asterite
Copy link
Member

I don't think will ever make variable declarations mandatory. That's what makes dynamic languages nice to work with: "Need a variable? Just assign it a value". The philosophy change in the language will be huge, and we wouldn't want that. So I'm closing this.

@faustinoaq
Copy link
Contributor

Something like TypeScript would be useful.
Not making types mandatory but making type explicit checking optional. something like --strict flag.

@RX14
Copy link
Contributor

RX14 commented Jun 22, 2017

Having variable types be specified at assignment is pretty pointless as we have flow typing, for example:

foo : Int32? = self.foo
foo : String = "fail" unless foo
# what type is foo here? It's not any of the ones specified above. 

If you want to know what type your variables are all the time without having to think, use another language. Or write an ide to tell you the types, I know which I'd rather have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants