RBS使いながらのメモ

row[1..].each_with_index do |item, index|
# ...
end
と書くと、row[1..]
がnil
かも知れないのでエラーが出る。
row[1..]
がnil
にならないと確信している時、どう書いたらいいのだろう。
Steep向けに
items = row[1..] #: Array[String]
items.each_with_index do |item, index|
# ...
end
みたいに、一回変数に代入したら型を指定できるのだけど、シグニチャーチェックのためにわざわざしたくない。

これも同様:
@base_uri = URI("https://example.com/")
Net::HTTP.start(@base_uri.host, @base_uri.port, use_ssl: @base_uri.scheme == "https") do |http|
# ...
end
@base_uri.host
はシグニチャーとしては(::String | nil)
になる。Net::HTTP.start
の第一引数にnil
を渡せないので、これはエラーになる。しかしこの場合は@base_uri
は必ず::String
になるのでそのことをSteepに伝えたい。変数に割り当てたらいけるのは分かるけど・・・

Arrow::Table.load
時にカラムの型を指定できる?

ファイルがCSVの場合はCSV.new
へのオプションと同じ物を渡せる。ので:converters
を使っての変換はできる:
Arrow::Table.load("path/to/data.csv", converters: [:date])
Arrow::Table.load("path/to/data.csv", converters: [->(field) {field.to_s}])
カラムを指定して、「このカラムだけ」みたいなのはできなさそう・・・

メソッドリファインメント(refine
とusing
)ってどうやって書いたらいいの?

Kernel
じゃなくてmain
のシグニチャーって提供されていない?
トップレベルでusing
を使うとエラーになる。
(クラス定義内でusing
を使う分にはエラーにならない。)
refine
は問題無い。

トップレベルusing
はパッチを投げた: https://github.com/ruby/rbs/pull/2357

↑のコメントにあるけどusing
はKernel
じゃなかった・・・
あと、現時点でmain
オブジェクトはサポートしていないとのこと: https://github.com/ruby/rbs/issues/2361

File.write
がPathname
を受け付けないことになっている。
シグニチャー(RBS v3.9.1):
def self.write: (String path, _ToS data, ?Integer offset, ?external_encoding: String | Encoding | nil, ?internal_encoding: String | Encoding | nil, ?encoding: String | Encoding | nil, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?mode: String) -> Integer
単純にString | Pathname
にして大丈夫かな?

タプルは扱えることになっている(Tuple type):
record = ["me", 12] #: [String, Integer]
けど、タプルをスプラットする時にSteepがシグニチャーを理解してくれないからエラーになる。
何言っているか分からないと思うけどこういうこと:
def do_something(name, count)
p [name, count]
end
record = ["me", 12] #: [String, Integer]
do_something(*record)
class Object
def do_something: (String name, Integer count) -> void
end
% steep check tuple.rb
# Type checking files:
F
tuple.rb:6:13: [error] Cannot pass a value of type `[::String, ::Integer]` as an argument of type `::Array[[::String, ::Integer]]`
│ [::String, ::Integer] <: ::Array[[::String, ::Integer]]
│ (::String | ::Integer) <: [::String, ::Integer]
│ ::String <: [::String, ::Integer]
│
│ Diagnostic ID: Ruby::ArgumentTypeMismatch
│
└ do_something(*record)
~~~~~~~
Detected 1 problem from 1 file
CSVを扱う時によく使うパターンなので対応できると嬉しいが・・・。

インスタンス変数のアノテーションってできない?
class App
def run
init
files = @conn.mlsd("path/to/dir")
ensure
if @conn && !@conn.closed?
@conn.close
end
end
private
def init
@conn = Net::FTP.new("ho.st", "user", "password")
end
end
で
class App
@conn: Net::FTP | nil
end
とすると(初期化時は@conn
はnil
なので)、@conn.mlsd
がエラーになってしまう、@conn
がnil
の時にmlsd
メソッドが無いから。
これがローカル変数なら(Steepを使っている場合)
# @type var conn: Net::FTP
files = conn.mlsd("path/to/dir")
ってアノテーションできるんだけど、インスタンス変数はできない?
Steepのマニュアルではできることになっているんだけど、手許ではエラーになってしまう・・・。

files = @conn.mlsd("path/to/dir")
の直前にアノテーションしてもだめだけど、メソッドの一行目に書けば大丈夫だった。
def run
# @type var conn: Net::FTP
init
files = @conn.mlsd("path/to/dir")