非常に典型的な使用例: オブジェクト (またはクラス) は、関連する型のいくつかの public val を宣言し、それらすべてを含むコレクションを返すアクセサーを宣言したいと考えています。
case class Ball(dia :Int)
object Balls {
val tennis = Ball(7)
val football = Ball(22)
val basketball = Ball(24)
val balls = Seq(tennis, football, basketball)
}
この例は明らかに DRY に違反しており、エラーが発生しやすくなっています。変更可能な状態を使用して非常に簡単に解決できます (暗黙のBuilder[Ball, Seq[Ball]]
パラメーターをBall
コンストラクターに追加するなど)。しかし、その解決策にも問題がないわけではありません。特に、解決策を一般化しようとしたり、すべてのクラスが宣言するクラス階層を作成しようとすると、一部の値では、変更可能な部分的な要約から最終的な不変の値に切り替えるタイミングが明確ではありません。
知的な練習として、そして好奇心から、私は純粋に機能的な変種を考え出そうとしましたが、あまり成功しませんでした. 私が思いついた最高のものは
object Balls {
import shapeless.{::, HNil}
val (balls @
tennis ::football::basketball::HNil
) =
Ball(7)::Ball(22)::Ball(24)::HNil
}
これは非常に優れていますが、ボールまたはその初期化子の数が少なくなると、管理できなくなります。有効な代替手段は、すべてを HMap に変更することですが、私は通常、パブリック API で形状のない依存関係を回避しようとします。おそらくscalaの継続で実行できるように思えますが、リセットブロックに対して宣言を非ローカルにする方法がわかりません。
編集:私が以前に強調しなかったこと、およびscala.Enumeration
私のために仕事をしない理由は、実際のケースではオブジェクトが同一ではなく、実際には複合構造であり、コンストラクターが数行以上かかることです. したがって、最終的な型は同じかもしれませんが (または少なくとも詳細には興味がありません)、単純な列挙ではなく、読みやすさの理由から、宣言されたメンバー/キーの名前を次のようにできることが重要です。その定義と視覚的に簡単に結びつけることができます。したがって、ここでの形状のないソリューションと、提案されている形状のない Seq ベースのソリューションは、実際の識別子を間違えて間違った値に変更が加えられる、オフバイワン エラーの影響を非常に受けやすくなっています。
もちろん、実際のケースは現在、継承されたコンストラクター メソッドによって生成された値のシーケンスを維持することにより、scala.Enumeration と同様に実装されています。ただし、列挙型が行うすべての問題があり、実際のobject Balls
初期化子の外側でコンストラクターを呼び出したり、条件付きブロックの値を破棄したりすることによるエラーの可能性が増幅されます。その上、これが純粋関数型言語でどのように解決されるのか非常に興味がありました。
ケーキを持って食べる方法はありますか?