Scala にはない依存型( example )を除いて、静的に行う方法がわかりません。定数のみを扱う場合は、必要なチェックを実行するマクロまたはコンパイラ プラグインを使用できるはずですが、任意の float 型の式がある場合は、実行時チェックに頼らなければならない可能性が非常に高くなります。
ここにアプローチがあります。float 値が必要な範囲内にあることを確認するためにランタイム チェックを実行するクラスを定義します。
abstract class AbstractRangedFloat(lb: Float, ub: Float) {
require (lb <= value && value <= ub, s"Requires $lb <= $value <= $ub to hold")
def value: Float
}
次のように使用できます。
case class NormalisedFloat(val value: Float)
extends AbstractRangedFloat(0.0f, 1.0f)
NormalisedFloat(0.99f)
NormalisedFloat(-0.1f) // Exception
または次のように:
case class RangedFloat(val lb: Float, val ub: Float)(val value: Float)
extends AbstractRangedFloat(lb, ub)
val RF = RangedFloat(-0.1f, 0.1f) _
RF(0.0f)
RF(0.2f) // Exception
パフォーマンスを向上させるために値クラスを使用できればいいのですが、(現在) コンストラクターでの呼び出しではそれがrequires
禁止されています。
編集: @paradigmaticによるコメントのアドレス指定
自然数に依存する型を、従属型を (完全に) サポートしない型システムでエンコードできる理由を直感的に説明します。ペアノ数字を使用したパス依存型として。ただし、実数はもはや列挙できないため、実数の各要素に対応する型を体系的に作成することはできなくなりました。
現在、コンピューターの浮動小数点数と実数は最終的には有限の集合ですが、型システムで合理的に効率的に列挙するにはまだ大きすぎます。もちろん、コンピューターの自然数のセットも非常に大きいため、型としてエンコードされたペアノ数値の算術演算に問題が生じます。この記事の最後の段落を参照してください。ただし、たとえばHListsで証明されているように、最初のn (かなり小さいnの場合) の自然数を使用するだけで十分な場合が多いと主張します。float に対応する主張を行うことは説得力に欠けます.0.0 と 1.0 の間で 10,000 個の float をエンコードする方が良いでしょうか?