22

Option[A] -> Option[B] 変換もできるように、オブジェクト A -> オブジェクト B が一般的な方法でオプションにラップされているときに暗黙的な変換を再利用する関数を作成しようとしています。仕事。

私が思いついたのは次のとおりです。

implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))

これは、値に Some(..) を割り当てると機能しますが、Option val; を割り当てると機能しません。次のコンソール出力を参照してください。

scala> trait T
defined trait T

scala> case class Foo(i: Int) extends T
defined class Foo

scala> case class Bar(i: Int) extends T
defined class Bar

scala> implicit def fromFooToBar(f: Foo):Bar = Bar(f.i)
fromFooToBar: (f: Foo)Bar

scala> implicit def fromBarToFoo(b: Bar):Foo = Foo(b.i)
fromBarToFoo: (b: Bar)Foo

scala> implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] = from.map(conversion(_))
fromOptionToOption: [A, B](from: Option[A])(implicit conversion: (A) => B)Option[B]

scala> val foo: Option[Foo] = Some(Bar(1))
foo: Option[Foo] = Some(Foo(1))
// THIS WORKS as expected

scala> val fooOpt = Some(Foo(4))
fooOpt: Some[Foo] = Some(Foo(4))

scala> val barOpt2: Option[Bar] = fooOpt
<console>:16: error: type mismatch;
 found   : Some[Foo]
 required: Option[Bar]
       val barOpt2: Option[Bar] = fooOpt
                                  ^
//THIS FAILS.

1 回目と 2 回目の変換の違いはよくわかりません。どういうわけか、後者では暗黙の変換が呼び出されません。型システムと関係があると思いますが、まだわかりません。何か案は?-Albert (私は scala 2.9.1 を使用しています)

4

5 に答える 5

13

あなたはそれに気づいていないかもしれませんが、そのためのフラグがあります:-Xlog-implicits。そしてこれはそれが言うことです:

scala> val barOpt2: Option[Bar] = fooOpt
fromOptionToOption is not a valid implicit value for Some[Foo] => Option[Bar] because:
incompatible: (from: Option[Foo])(implicit conversion: Foo => B)Option[B] does not match expected type Some[Foo] => Option[Bar]
<console>:16: error: type mismatch;
 found   : Some[Foo]
 required: Option[Bar]
       val barOpt2: Option[Bar] = fooOpt
                                  ^

そして、あなたは行き​​ます-それはどのタイプBでなければならないかを知りません。0__は、この問題は不変のコレクションでは発生しないと述べており、それは理にかなっています。不変コレクションでは、Bは正確Barにである必要がありますが、共変コレクションでは、の任意のサブタイプにすることができますBar

では、なぜ機能val foo: Option[Foo] = Some(Bar(1))するのでしょうか。ええと、そのための旗もあります-Ytyper-debug。ただし、極端な冗長性を考えると、弱者向けではありません。

とにかく、両方の場合に何が起こるかを比較して、私は手を加えました、そして答えはかなり単純です...それOptionはその場合に変換されているのではありませんが、Bar!からの暗黙的な変換を宣言したので、結果を!に渡す前にBar => Fooその変換を適用していることを忘れないでください。Some

于 2012-01-11T20:59:36.333 に答える
13

ここに手がかりがあります:

scala> val fooOpt: Option[Bar] = Option(Foo(1))
fooOpt: Option[Bar] = Some(Bar(1))

そしてもう一つ:

scala> implicit def foobar(x: String): Int = augmentString(x).toInt
foobar: (x: String)Int

scala> val y: Option[String] = Option(1)
y: Option[String] = Some(1)

scala> val y: Option[Int] = Option("1")
y: Option[Int] = Some(1)

合法的に奇妙なバグのように見えます。小さなテスト ケースを開いて問題を開きます (または JIRA で問題を検索します)。

余談として:

いくつかの圏論を使用して、さまざまな種類の「オプションっぽい」ものを処理できます。

package object fun {
  trait Functor[Container[_]] {
    def fmap[A,B](x: Container[A], f: A => B): Container[B]
  }
  object Functor {
     implicit object optionFunctor extends Functor[Option] {
       override def fmap[A,B](x: Option[A], f: A => B): Option[B] = x map f
     }
     // Note: With some CanBuildFrom magic, we can support Traversables here.
  }
  implicit def liftConversion[F[_], A, B](x: F[A])(implicit f: A => B, functor: Functor[F]): F[B] = 
    functor.fmap(x,f)

}

いくつかの圏論FPを問題にマッピングしているため、これはもう少し高度ですが、必要に応じて暗黙的な会話をコンテナーに持ち上げるのがより一般的な解決策です。より制限された暗黙的な引数を取る1 つの暗黙的な会話メソッドを使用して、それらがどのように連鎖するかに注意してください。

また、これにより例が機能するはずです。

scala> val tmp = Option(Foo(1))
tmp: Option[Foo] = Some(Foo(1))

scala> val y: Option[Bar] = tmp
y: Option[Bar] = Some(Bar(1))

そして、Someより危険な使い方をしてください:

scala> val tmp = Some(Foo(1))
tmp: Some[Foo] = Some(Foo(1))

scala> val y: Option[Bar] = tmp
<console>:25: error: could not find implicit value for parameter functor: fun.Functor[Some]
       val y: Option[Bar] = tmp
                            ^

これは、分散が重要であり、暗黙のものと相互作用することを示しています。私の推測では、他の手法を使用して回避できる、非常にまれな、おそらく修正が難しいバグに遭遇したのではないでしょうか。

于 2012-01-11T20:01:38.077 に答える
2

Scala言語仕様ではビューが次のように定義されているため、機能しません。

暗黙的なパラメータとメソッドは、ビューと呼ばれる暗黙的な変換を定義することもできます。タイプSからタイプTへのビューは、関数タイプS => Tまたは(=> S)=> Tを持つ暗黙の値、またはそのタイプの値に変換可能なメソッドによって定義されます。

fromOptionToOption暗黙のパラメータを取るため、3つのカテゴリに準拠していません。コンパイラーは、宛先とソースの両方がジェネリック型のコンバーターを見つけられないようです。

からのビューの定義は期待どおりOption[Foo]Option[Bar]機能します。

trait T
case class Foo(i: Int) extends T
case class Bar(i: Int) extends T

object Main {
  implicit def fromFooToBar(f: Foo):Bar = Bar(f.i)
  implicit def fromBarToFoo(b: Bar):Foo = Foo(b.i)
  // implicit def fromOptionToOption[A, B](from: Option[A])(implicit conversion: (A) => B): Option[B] =
  //  from.map(conversion(_))
  implicit def fromOptionFooToOptionBar(o: Option[Foo]): Option[Bar] = o map { foo => foo } 

  def test(): Option[Bar] = {
    val fooOpt = Some(Foo(4))
    val barOpt2: Option[Bar] = fooOpt

    barOpt2
  }
}

println(Main.test)

これを実行すると、次のように出力されます。

$ scala so.scala
Some(Bar(4))

ただし、すべてが失われるわけではありません。Option一般的なものほど良くはありませんが、ビューバウンドによってOptionに変わることができるもののようなことを行うことができますBarOption[Bar]

trait T
case class Foo(i: Int) extends T
case class Bar(i: Int) extends T

object Main {
  implicit def fromFooToBar(f: Foo):Bar = Bar(f.i)
  implicit def fromBarToFoo(b: Bar):Foo = Foo(b.i)
  implicit def fromOptionToOptionBar[A <% Bar](from: Option[A]): Option[Bar] =
    from map { foo => foo }

  def test(): Option[Bar] = {
    val fooOpt = Some(Foo(4))
    val barOpt2: Option[Bar] = fooOpt

    barOpt2
  }
}

println(Main.test)

Option一般的な目的で使用できるが、追加の呼び出しOptionが必要な別の回避策は次のとおりです。.convert

trait T
case class Foo(i: Int) extends T
case class Bar(i: Int) extends T

case class Converter[A](x: Option[A]) {
  def convert[B](implicit ev: Function1[A, B]): Option[B] = x map { a: A => ev(a) }
}

object Main {
  implicit def optionToConverter[A](x: Option[A]) = Converter(x)
  implicit def fooToBar(x: Foo) = Bar(x.i)

  def test(): Option[Bar] = {
    val fooOpt = Some(Foo(4))
    val barOpt: Option[Bar] = fooOpt.convert
    barOpt
  }
}

println(Main.test)
于 2012-01-11T21:00:56.580 に答える
1

確かに、これは非常に奇妙な問題です。以外の型を使用しようとしましたが、型パラメーターが共変でOptionあることが問題であることが判明しました。Optionこれはすべて機能します:

case class A[B](value: B)  // invariant in B

case class X()
case class Y()

implicit def xtoy(x: X): Y = Y()
implicit def ytox(x: Y): X = X()
implicit def movea[U, V](from: A[U])(implicit view: U => V): A[V] =  A[V](from.value)

def test(a: A[Y]) = "ok"
test(A(X()))   // (1)
val f = A(X())
test(f)        // (2)

しかし、代わりに次のように定義するA

case class A[+B](value: B)  // covariant in B

ケース (2) は失敗します。ケース (1) は常に成功します。これは、Scala がでラップする前に既に に変換Xしているためです。YA

問題の原因がわかったので、型の第一人者がこれが実際に問題である理由を説明するのを待つ必要があります...変換はまだ有効です。

askForY(movea(f))  // succeeds, even with A[+B]
于 2012-01-11T18:11:42.267 に答える