class Foo
@id : Int32 | Nil
def initialize(@id)
end
def foo
pp self.try &.@id
end
end
Foo.new(42).foo
Syntax error in eval:8: expecting any of these tokens: IDENT, CONST, +, -, *, /, //, %, |, &, ^, ~, **, <<, <, <=, ==, !=, =~, !~, >>, >, >=, <=>, ===, [], []=, []?, [, &+, &-, &*, &** (not 'INSTANCE_VAR')
self.@id
works nicely, though.
Generally .@ivar
shouldn't be used. Use a getter
instead, public, protected or private depending of your use case.
Still a bug, but not a priority one I would say. There are simple (and nice) workarounds.
Real-life use case for self.@var
:
module Worker
def do_limits
# Only apply this limit to webhook workers
if self.is_a?(Worker::Webhook)
limit_session.add(Limit::Concurrency.new(
"#{namespace}:worker_limit:#{[email protected]!}",
[email protected]!.to_u, 1
))
end
end
end
class Worker::Socket
# No @model here
end
class Worker::Webhook
@model : SomeModel
end
I don't want to expose @model
, but need to access it from within the module method. Using @model
instead of self.@model
leads to a compilation error:
Can't infer the type of instance variable '@model' of Worker::Socket
Following it, there is place where I need try &.@var
:
Model::JobAttempt.new(
webhook_worker: self.as?(Worker::Webhook).try &.@model
)
I could use protected getters, though. However, there is a use-case.
Well, there is also another bug here:
in src/worcr/node/worker/routine.cr:216: BUG: NoReturn is not an InstanceVarContainer
worker_name: self.is_a?(Worker::Socket) ? self.@name : [email protected],
^
I guess self.@var
really should be synonmous to @var
in all instances.
FYI, self.try &.itself.@id
works, so there is no hurry in fixing this bug 鈽猴笍
I think .@var
is a smell, and should only benlimited to some very low level stuff. Wouldn't exposing a protected model getter solve your issue more elegantly?
There are some cases when it's better to use .@var
. For example, the msgpack-crystal shard has presense
option for a field:
if
true
, a@{{key}}_present
instance variable will be generated when the key was present (even if it has anull
value)
I have a procedure with Request
struct, which is MessagePack::Serializable
. I don't want to pollute it with these _present
getters, I'd rather use .@_present
approach within the procedure itself:
struct Request
include MessagePack::Serializable
@[MessagePack::Field(presense: true)]
getter foo : String?
end
def self.call(request : Request)
if request.@foo_present
if foo = request.foo
# foo is present and truthy
else
# foo is present and falsey
end
else
# foo is absent
end
end
The foo.@bar
has two use cases:
pointerof(foo.@bar)
Struct#==
to compare each field without needing a getterAll other cases are a smell, really. Crystal, like Ruby, is oriented towards methods and calls. You should use methods and calls all the time, instance variables should only be accessed from the type that define them.
You know, I'm a guy who always stands for his point of view but always ready to accept other's, given strong arguments. Ary, you say that it's a smell, but immediately stating:
being able to implement Struct#== to compare each field without needing a getter
That's exactly the case of my comment. Low-level enough, self.call
is not a part of a public API. A user would only see Request
. Similarly to Struct#===
, I don't want to pollute it with unnecessary getters.
I don't want to pollute it with unnecessary getters.
It's necessary then, if you need to access the variable from outside.
if true, a @{{key}}_present instance variable will be generated when the key was present (even if it has a null value)
I think that should also generate a getter, otherwise how do you access it from outside?
What's needed here is that getter, not providing more ways to access instance variables like that.
I don't want to pollute it with unnecessary getters
Use protected
then.
protected getter var
Most helpful comment
I think
.@var
is a smell, and should only benlimited to some very low level stuff. Wouldn't exposing a protected model getter solve your issue more elegantly?