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.hostは必ず::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")