Sorbet を使用して同じ構造体を定義するにはどうすればよいですか?
概要
全て! 汎用の成功タイプと汎用の失敗タイプを結合した汎用の結果データ型を定義したいと考えています。 TS では同じことが次のようになります。
type Success<T> = {
value: T
}
type Failure<E> = {
error: E
}
type Result<T, E> = Success<T> | Failure<E>
または Rust では次のようになります。
enum Result<T, E> {
Ok(T),
Err(E),
}
しかし、残念ながら、Sorbet タイプの注釈を使用してそれを行う方法は見つかりませんでした。 それは可能ですか?
どうもありがとうございます。
私が見つけた最も近いものは gem dry-monad の型定義でしたが、Success クラスと Failure クラスの両方が両方の type_members を再定義する必要があるため、ハックのように見えるので、実際には私が望んでいることではありません。
説明
この例を見てみましょう: https://translate.google.com/translate?hl=ja&sl=en&tl=ja&u=https://gist.github.com/woarewe/f4f3ee502f35c4c0d097695a52031b14 私の目標は、次のような署名を定義することです。
sig { params(value: Integer).returns(Core::Type::Result[Integer, String]) }
しかし、あるクラスから別のクラスにジェネリック型を渡す方法がないように見えるため、それは不可能のようです。
私が見つけた唯一の回避策は、関数定義内で特定の型を使用して共用体を構築することです。
sig { params(value: Integer).returns(T.any(Core::Type::Success[Integer], Core::Type::Failure[String])) }
def zero?(value)
if value.zero?
Core::Type::Success.new(value)
else
Core::Type::Failure.new("It is not zero")
end
end
最終的な解決策は次のようになります
# typed: strict
# frozen_string_literal: true
module Core
module Type
class Success
extend T::Sig
extend T::Generic
ValueType = type_member
sig { returns(ValueType) }
attr_reader :value
sig { params(value: ValueType).void }
def initialize(value)
@value = value
end
end
class Failure
extend T::Sig
extend T::Generic
ErrorType = type_member
sig { returns(ErrorType) }
attr_reader :error
sig { params(error: ErrorType).void }
def initialize(error)
@error = error
end
end
end
end
extend T::Sig
sig { params(value: Integer).returns(T.any(Core::Type::Success[Integer], Core::Type::Failure[String])) }
def zero?(value)
if value.zero?
Core::Type::Success.new(value)
else
Core::Type::Failure.new("It is not zero")
end
end
result = zero?(0)
case result
when Core::Type::Success
p result.value
when Core::Type::Failure
p result.error
end
解決策
私は最近、あなたが探しているものを正確に実装する gem の開発を手伝いました。 https://github.com/maxveldink/sorbet-result
この gem を使用すると、コードを次のように書き換えることができます。
sig { params(value: Integer).returns(Typed::Result[Integer, String]) }
def zero?(value)
if value.zero?
Typed::Success.new(value)
else
Typed::Failure.new("It is not zero")
end
end
result = zero?(0)
if result.success?
p result.payload
else
p result.error
end
この gem は、チェーンやその他の優れた機能もサポートしています。