你会如何用冰糕做玫瑰记忆?

How would you do rose memoization with Sorbet?

试图注释这段代码,玫瑰记忆 (@||=) 给我一个错误 Use of undeclared variable @git_sha

# typed: strict
# frozen_string_literal: true

module Util
  extend T::Sig

  sig { returns(String) }
  def self.git_sha
    @git_sha ||= ENV.fetch(
      'GIT_REV',
      `git rev-parse --verify HEAD 2>&1`
    ).chomp
  end
end

据我所知,我应该用 T.let 声明变量的类型,但还没有弄清楚具体如何。

从 0.4.4679 开始,Sorbet 现在有 built-in 支持。在此之前,还有其他解决方法(见下文)。

  1. 初始化实例变量为T.nilable,将其他地方对实例变量的所有直接访问替换为方法:
# typed: strict
# frozen_string_literal: true

module Util
  extend T::Sig

  sig { returns(String) }
  def self.git_sha
    @git_sha = T.let(@git_sha, T.nilable(String))
    @git_sha ||= ENV.fetch(
      'GIT_REV',
      `git rev-parse --verify HEAD 2>&1`
    ).chomp
  end
end

→ View on sorbet.run

这是首选解决方案。

  1. 在方法外初始化实例变量,并给它一个类型注解:
# typed: strict
# frozen_string_literal: true

module Util
  extend T::Sig

  @git_sha = T.let(nil, T.nilable(String))

  sig { returns(String) }
  def self.git_sha
    @git_sha ||= ENV.fetch(
      'GIT_REV',
      `git rev-parse --verify HEAD 2>&1`
    ).chomp
  end
end

→ View on sorbet.run

从概念上讲,此 class 有两个执行阶段:初始化时和使用时。如果一个实例变量在 Sorbet 中初始化时没有给出类型注解,那么它将到处都是 T.untyped(或者在 # typed: strict 中出错)。因为如果不在初始化中注释,Sorbet 无法知道哪个代码路径可能首先写入该位置。 (即使在只有一个位置的情况下,Sorbet 也不会进行那种全局分析。)

Sorbet 只在实例变量为 nilable 时放宽这一点,在这种情况下它可以在任何地方初始化,因为 Sorbet 不需要保证它被初始化为 non-nil。

  1. 使用不同的严格级别

Docs on strictness levels.

如果您觉得添加类型注释太麻烦,您可以使用 # typed: true 选择不要求类型注释,其中实例变量需要类型注释的错误被消除。