-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
TypeNode.instance_vars should work everywhere #7504
Comments
Instance methods returned by the macro reflection API are never typed. But I agree that it would be great if instance vars could be accessed at the class level - even if they're not fully typed. |
Is there any chance it gets implemented? It would be a big leap forward for many cool shards in the wild. |
No |
Welp, closing then 👍 |
BTW, if a person spends much time on describing an issue, constructing it, selecting right words and shaving it with Okkam's razor. Then they expect a similar level of effort put into a response. It could be treated as a rude response otherwise. Writing code is one thing, but formulating problems, finding bugs and proposing features is another and takes time as well. Such an approach, when an issue persists for ages and many experienced Crystal developers asking for a feature, getting simple "no" as a response. It's devastating and wrong. I have an argument in my issue, and it's pretty solid (the fact that we have access to methods, but not vars), and I expect to receive a solid counter-argument and explanation on why it's not possible. |
Yes, I am on my cellphone, I planned to add an explanation about why this is hard to do. |
So, I said "no" because right now the first pass in the compiler doesn't even look at instance variables. We would have to have a separate structure to hold this "yet to be put in the right place" instance vars without types, or something like that. And I don't want to introduce risky changes. Then, you didn't provide any use case to justify adding this. Finally, I agree that in the general case if you ask a question then then response should have equal effort put into it, but from my side I want to ask the questions of many people and I simply don't have time to put that much effort into it. Right now I'lll focus more on fixing existing bugs. I believe the language as it is right now is already super useful, but some bugs are annoying people (and myself) and I'd like to fix them all. You can also notice that nobody else is working on compiler stuff like this (changing fundamental things), I'm the only one who is doing it right now so me saying "no" also means "I won't do it, so nobody else will (soon)". |
I'm sorry for putting pressure on you, Ary. Me (and others, for sure) are thankful to you for Crystal, as you're being the one who initially implemented the language and still being one of the most major figures in developing it. I wish you had more time and resources to spend on Crystal (if that is what you really want). And you're right about the importance of fixing existing bugs either. That's a pity that there are only a few developers able to work on fundamentals. For me, I've selected a more mass-user-oriented path. I'm working on frameworks which utilize language features in a beautiful but still efficient way. And this, sadly, requires instance variables to be known in compilation time. Otherwise the expressiveness goals I've given to myself aren't reachable. One of the main Crystal applications in foreseeable future is web development. And web development is often about wrapping databases with API. Having both type-safe-as-possible and enjoyable ORM is crucial for the stack. Existing solutions suffer from current language limitations, with this very issue being one of the major ones. Knowing instance variables in compilation time would allow to declare methods and arguments needed for enjoyable development experience, avoiding explicit language constructs. Onyx::SQL is already 2 years-old, and it was rewritten 4 times from scratch IIRC (++25k LoC, --18k LoC). I've tried many approaches to implement the best possible API, and I always faced the need to know them variables. Please believe me on that. Current annotation approach is nearly perfect in the terms of codebase and API, and resolving the issue would make it even more ideal. For instance, any Query builder method involving a model's attribute referenced by name currently relies on enums autocasting. Enums are defined with a dirty hack based on getters and then restricted in methods like Another example is Query builder One more use-case is defining throw-away model records with strict types (as an ORM model instance actual variables depend on origin SQL query): class User
include Onyx::SQL::Model
# It can't be NULL in DB, but an SQL query could have it missing in SELECT clause
@[Onyx::SQL::Field(not_null: true)]
property id : Int32?
@[Onyx::SQL::Field(not_null: true)] # It can't be NULL in DB either
property name : String?
property age : Int32?
end
User.record UserObject, id, name
# Expands to (simplified)
struct UserObject
property id : Int32 # Its type is known from User, and it's `not_null: true`
property name : String # Ditto
end
user = UserObject.from_rs(db.query("SELECT id, name FROM users"))
user.id # Is never nil Who knows which features are more to be seen once the issue is resolved properly. I truly hope it gets some love over the time. It's not critical in my case, more of QoL improvement for both sides, but isn't that what we love Crystal for — incredible joy of using of? |
If # this assumes the same ivar is never declared more than once
{% for decl in User.definitions %}
{% body = decl.body %}
{% if body.is_a?(Expressions) %}
{% for exp in body.expressions %}
{% if exp.is_a?(TypeDeclaration) && exp.var.is_a?(InstanceVar) %}
# `exp.var` is not always a `MacroId`, contrary to the docs
# `exp.type` is the type restriction of the ivar, which might not correspond to a real type
# (in contrast, `MetaVar#type` always attempts to resolve the ivar type)
{% elsif exp.is_a?(Assign) && exp.target.is_a?(InstanceVar) %}
# ivar initialized in metaclass context with deduced type
{% elsif exp.is_a?(Def) %}
# iterate through `exp.body` if necessary, as additional ivars may be declared
{% end %}
{% end %}
{% elsif body.is_a?(TypeDeclaration) && body.var.is_a?(InstanceVar) %}
# alternatively the macro interpreter could simply wrap singleton
# expression nodes into `Expressions` so that we don't need this branch
{% end %}
{% end %} Alternatively we might add a |
We recently had a discussion in gitter and found out that you can have an access to type instance methods on class level, i.e. https://carc.in/#/r/6evi:
The result is:
We can see that
foo
is untyped and that is kind of expected, as of this time the compiler did not revealed its return type yet (shouldn't it, though?).However, it doesn't work with instance variables (https://carc.in/#/r/6evk):
@asterite has a strong position on that:
I see an issue here. The behavior is inconsistent. If we can have an access to instance methods before their types are fully known, why can't we do the same with instance variables? Let them be untyped if types are not know yet, i.e. their
.type
accessor returnsNilLiteral
. It should be the developer's responsibility to understand that instance vars aren't fully typed at some moments of time. Macros are somewhat advanced level and I guess it's better to have features which behave differently in different conditions than not having these features at all. Moreover, we already do have such complex feature of accessing instance methods which could or could not be typed at certain moments of time.Thanks for the attention!
The text was updated successfully, but these errors were encountered: