Crystal: try &.@var

Created on 15 Apr 2019  路  13Comments  路  Source: crystal-lang/crystal

https://carc.in/#/r/6q46:

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.

bug someday compiler

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?

All 13 comments

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 a null 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:

  • being able to take the pointer of an instance variable: pointerof(foo.@bar)
  • being able to implement Struct#== to compare each field without needing a getter

All 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
Was this page helpful?
0 / 5 - 0 ratings

Related issues

lgphp picture lgphp  路  3Comments

asterite picture asterite  路  3Comments

oprypin picture oprypin  路  3Comments

Papierkorb picture Papierkorb  路  3Comments

relonger picture relonger  路  3Comments