に取り組んでScalaで製品タイプを実装し、Iは、化合物の値を表す製品タイプ&
、例えば:ジェネリック更新機能は、Scalaでその部品
val and: String & Int & User & ... = ???
すなわちを作成する必要がありますand
は、String
部とInt
部とUser
部を含む必要があります。私はいくつかの製品の値に適用し、変更されたA
一部でバックその製品を取得し、機能A => A
を持つ、への道を必要とするような製品の種類を持つ
val and: String with Int with User with ... = ???
:これはScalaのwith
キーワードに似ています。これは、製品の各タイプが一意でなければならないことを意味します。
重要な制限の1つは、製品にA => A
という機能を適用すると、その製品の内部にはA
が含まれていることがわかりますが、他のタイプの情報はありません。しかし、関数の呼び出し元として、完全な型の情報を持つ製品を渡し、この完全型を関数の署名の一部として返すことを期待しています。擬似コードで
:型崩れまたは他の難解なものを使用して
def update[A, Rest](product: A & Rest, f: A => A): A & Rest
が私のために大丈夫です。私はHList
を使ってみましたが、それらは順序付けされていますが、異種セットのようなものはA & Rest
部分を再送信する方がここではより適切でしょう。
UPDATE:
object product {
/** Product of `left` and `right` values. */
case class &[L, R](left: L, right: R)
implicit class AndPimp[L](val left: L) extends AnyVal {
/** Make a product of `this` (as left) and `right`. */
def &[R](right: R): L & R = new &(left, right)
}
/* Updater. */
/** Product updater able to update value of type `A`. */
trait ProductUpdater[P, A] {
/** Update product value of type `A`.
* @return updated product */
def update(product: P, f: A ⇒ A): P
}
trait LowPriorityProductUpdater {
/** Non-product value updater. */
implicit def valueUpdater[A]: ProductUpdater[A, A] = new ProductUpdater[A, A] {
override def update(product: A, f: A ⇒ A): A = f(product)
}
}
object ProductUpdater extends LowPriorityProductUpdater {
/** Left-biased product value updater. */
implicit def leftProductUpdater[L, R, A](implicit leftUpdater: ProductUpdater[L, A]): ProductUpdater[L & R, A] =
new ProductUpdater[L & R, A] {
override def update(product: L & R, f: A ⇒ A): L & R =
leftUpdater.update(product.left, f) & product.right
}
/** Right-biased product value updater. */
implicit def rightProductUpdater[L, R, A](implicit rightUpdater: ProductUpdater[R, A]): ProductUpdater[L & R, A] =
new ProductUpdater[L & R, A] {
override def update(product: L & R, f: A ⇒ A): L & R =
product.left & rightUpdater.update(product.right, f)
}
}
/** Update product value of type `A` with function `f`.
* Won't compile if product contains multiple `A` values.
* @return updated product */
def update[P, A](product: P)(f: A ⇒ A)(implicit updater: ProductUpdater[P, A]): P =
updater.update(product, f)
/* Reader. */
/** Product reader able to read value of type `A`. */
trait ProductReader[P, A] {
/** Read product value of type `A`. */
def read(product: P): A
}
trait LowPriorityProductReader {
/** Non-product value reader. */
implicit def valueReader[A]: ProductReader[A, A] = new ProductReader[A, A] {
override def read(product: A): A = product
}
}
object ProductReader extends LowPriorityProductReader {
/** Left-biased product value reader. */
implicit def leftProductReader[L, R, A](implicit leftReader: ProductReader[L, A]): ProductReader[L & R, A] =
new ProductReader[L & R, A] {
override def read(product: L & R): A =
leftReader.read(product.left)
}
/** Right-biased product value reader. */
implicit def rightProductReader[L, R, A](implicit rightReader: ProductReader[R, A]): ProductReader[L & R, A] =
new ProductReader[L & R, A] {
override def read(product: L & R): A =
rightReader.read(product.right)
}
}
/** Read product value of type `A`.
* Won't compile if product contains multiple `A` values.
* @return value of type `A` */
def read[P, A](product: P)(implicit productReader: ProductReader[P, A]): A =
productReader.read(product)
// let's test it
val p = 1 & 2.0 & "three"
read[Int & Double & String, Int](p) // 1
read[Int & Double & String, Double](p) // 2.0
read[Int & Double & String, String](p) // three
update[Int & Double & String, Int](p)(_ * 2) // 2 & 2.0 & three
update[Int & Double & String, Double](p)(_ * 2) // 1 & 4.0 & three
update[Int & Double & String, String](p)(_ * 2) // 1 & 2.0 & threethree
}
をd:これは決して固定することはできません。 2つの製品を比較するメソッド/タイプクラスを定義して、順序に関係なく同じタイプであるかどうかを伝えることができますが、タイプシステム自体には不運があります。あなたはすべての魔法を試すことができます、あなたは '&[Int、String]'が '&[String、Int]'と同じであるとコンパイラが考えることは決してできません(完全に引き継ぐコンパイラプラグインこれらの型の型チェック、それが可能であれば)。 –
@RégisJean-Gilles、ありがとう、ありがとう。 – Tvaroh