Javaのような変更可能な世界で簡単にできることをScalaで手に入れようと苦労しています。プログラムとして取得する新しい情報を追加する方法
私はList
のインスタンスを持っています。 (List
のクラスItem
、またはList[Item]
としましょう)プログラムが進行するにつれて、それぞれについてItem
についての詳細情報が得られます。 (これらのクラスをItemAttributeA
、ItemAttributeB
などと呼ぼう)ItemAttributeA
がどのItem
に対応しているかを忘れないように、私はそれらをList
のようにList[(Item, ItemAttributeA, ItemAttributeB)]
のようにしておきたいと思います。
このタプルのリストは既にかなり醜いように見えますが、いくつかのケースクラスがあれば、はるかにひどいことになります。 (のような変更可能な世界ではList[(Item, ItemAttributeA, ItemAttributeB, ItemAttributeC, ItemAttributeD, ItemAttributeE, ItemAttributeF, ...)]
、私たちは以下のようなクリーンなコードを書くことができます。
case class Item(id: Int) {
var attrA: ItemAttributeA = null //
var attrB: ItemAttributeB = null // fill these fields later
}
val getItemAttributeA: List[Item] => List[ItemAttributeA]
val getItemAttributeB: List[Item] => List[ItemAttributeB]
val items = List(Item(1))
items.zip(getItemAttributeA(items)).foreach { case (item, itemAttrA) =>
item.attrA = itemAttrA
}
items.zip(getItemAttributeB(items)).foreach { case (item, itemAttrB) =>
item.attrB = itemAttrB
}
たぶん、あなたはOption
とcopy
を使用する場合は、変更可能なフィールドを必要としないが、それは少し厄介になってきた。そして、まだ、それはいくつかの問題を抱えている。
case class Item(
id: Int,
attrA: Option[ItemAttributeA],
attrB: Option[ItemAttributeB]
)
val getItemAttributeA: List[Item] => List[ItemAttributeA] // I get list of information at once for a performance reason.
val getItemAttributeB: List[Item] => List[ItemAttributeB]
val items = List(Item(1, None, None))
val itemsWithA = items.zip(getItemAttributeA(items)).map { case (item, itemAttrA) =>
item.copy(attrA = Some(itemAttrA))
}
val itemsWithAandB = itemsWithA.zip(getItemAttributeB(itemsWithA)).map { case (item, itemAttrB) =>
item.copy(attrB = Some(itemAttrB))
}
// All item, itemWithAttrA, itemWithAttrB have same type Item.
// This doesn't sound good because we can't know which instance have the information we want by just looking at the type.
私の現在の最善の解決策は、フィールドとしてのみ1ケースクラスを持っている特性を利用している。
trait HasItem { val item: Item }
trait HasItemAttributeA { val itemAttrA: ItemAttributeA }
trait HasItemAttributeB { val itemAttrB: ItemAttributeB }
val getItemAttributeA: List[HasItem] => List[HasItem with HasItemAttributeA]
val getItemAttributeB: List[HasItem with HasItemAttributeA] => List[HasItem with HasItemAttributeA with HasItemAttributeB]
val hasItem = new HasItem { val item = Item(1) }
val hasItemWithA = getItemWithA(hasItem)
val hasItemWithAandB = getItemAttributeB(hasItemWithA)
まあ、それほど気にならない。しかし、少なくともそれはあなたがタプルで満足できないいくつかのニーズを満たしています。たとえば、このようなケース。
// you want to add ItemAttributeX to each Item in the list.
// but you only need Item and ItemAttributeB to get ItemAttributeX
// you can express that by using type parameters
def getItemAttributeX[I < HasItem with HasItemAttributeB](listI: List[I]): List[I with HasItemAttributeX]
val itemsWithManyAttributes: List[HasItem with HasItemAttributeA with HasItemAttributeB with HasItemAttributeC with HasItemAttributeD with HasItemAttributeE]
val itemsWithManyAttributesAndX = getItemAttributeX(itemsWithManyAttributes)
これは何とか動作しますが、他にも多くの問題があります。 (可読性、多くの定型文、新しいインスタンスを何度も作成するなど)
私の質問は、うまくいけばうまくいけば、機能的なやり方でこれらの問題を解決する最良の方法は何ですか?