9

Scala コレクションのいくつかの拡張メソッドを作成しようとしていますが、それらを完全に生成するのに問題が発生しています。

tailOption の最初の試行では、次のような結果が得られます。

implicit class TailOption[A, Repr <: GenTraversableLike[A, Repr]](val repr: Repr) {
  def tailOption: Option[Repr] = 
    if (repr.isEmpty) None 
    else Some(repr.tail)
}

残念ながら、これは機能しません:

scala> List(1,2,3).tailOption
<console>:19: error: value tailOption is not a member of List[Int]
              List(1,2,3).tailOption

Scala 2.10 は IsTraversableLike 型クラスを提供して、この種のものをすべてのコレクション (文字列などの奇妙なものを含む) に適応させるのに役立ちます。

これにより、たとえば、tailOption を非常に簡単に実装できます。

implicit class TailOption[Repr](val r: Repr)(implicit fr: IsTraversableLike[Repr]) {
  def tailOption: Option[Repr] = { 
    val repr = fr.conversion(r)
    if (repr.isEmpty) None 
    else Some(repr.tail)
  }
}

scala> List(1,2,3).tailOption
res12: Option[List[Int]] = Some(List(2, 3))

scala> "one".tailOption
res13: Option[String] = Some(ne)

結果は正しいタイプです: Option[<input-type>]. 具体的には、 「 Reprtail Repr.

残念ながら、このトリックを使用してコレクションの要素の型を保持することはできないようです。要素を返すメソッドを呼び出すことができません。

IsTraversableLikeにはメンバー A がありますが、あまり役に立ちません。特に、元の要素の型を再構築できず、メンバーの型が同等ではありません。たとえば、さらに作業を行わないと、headTailOption次のようになります。

implicit class HeadTailOption[Repr](val r: Repr)(implicit val fr: IsTraversableLike[Repr]) { 
  def headTailOption: Option[(fr.A, Repr)] = { 
    val repr = fr.conversion(r)
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail)
  }
}

scala> val Some((c, _)) = "one".headTailOption
c: _1.fr.A forSome { val _1: HeadTailOption[String] } = o

ご覧のとおり、c は見事なバロック型を持っています。ただし、この型はChar と同等ではありません。

scala> val fr = implicitly[IsTraversableLike[String]]
fr: scala.collection.generic.IsTraversableLike[String] = scala.collection.generic.IsTraversableLike$$anon$1@60ab6a84

scala> implicitly[fr.A <:< Char]
<console>:25: error: Cannot prove that fr.A <:< Char.
              implicitly[fr.A <:< Char]

私は、どれも役に立たないことを含め、あらゆる種類のトリックを試しRepr[A] <: GenTraversableLike[A, Repr[A]]ました。誰でもheadTailOption適切な型を返すための魔法のソースを考え出すことができますか?

val headTailString: Option[(Char, String)] = "one".headTailOption
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption
4

2 に答える 2

3

部分的な答え。おそらく、 の scaladoc の例から始めているでしょうIsTraversableLike。暗黙的なクラスを 1 ステップで処理するのではなく、暗黙的な変換を分離してラッパー クラスをインスタンス化するという「古いアプローチ」を引き続き使用します。「古いアプローチ」が機能することがわかりました。

import collection.GenTraversableLike
import collection.generic.IsTraversableLike

final class HeadTailOptionImpl[A, Repr](repr: GenTraversableLike[A, Repr]) { 
  def headTailOption: Option[(A, Repr)] = { 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail)
  }
}

implicit def headTailOption[Repr](r: Repr)(implicit fr: IsTraversableLike[Repr]):
  HeadTailOptionImpl[fr.A,Repr] = new HeadTailOptionImpl(fr.conversion(r))

// `c` looks still weird: `scala.collection.generic.IsTraversableLike.stringRepr.A`
val Some((c, _)) = "one".headTailOption
val d: Char = c  // ...but it really is a `Char`!

val headTailString: Option[(Char, String)] = "one".headTailOption
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption

Miles が指摘しているように、これが暗黙の検索と型推論で機能するには、分割が不可欠のようです。

別の解決策は、もちろんエレガントではありませんが、文字列とコレクションの統合をあきらめることです。

trait HeadTailOptionLike[A, Repr] {
  def headTailOption: Option[(A, Repr)]
}

implicit class GenHeadTailOption[A, Repr](repr: GenTraversableLike[A, Repr])
extends HeadTailOptionLike[A, Repr] {
  def headTailOption = 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail)
}

implicit class StringHeadTailOption(repr: String)
extends HeadTailOptionLike[Char, String] {
  def headTailOption = 
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail) // could use repr.charAt(0) -> repr.substring(1)
}

List(1,2,3).headTailOption
"one".headTailOption
于 2013-01-06T09:51:42.940 に答える
0

Aから型を抽出すると、暗黙のクラスとして記述できますIsTraversableLike

import collection.generic.IsTraversableLike

implicit class HeadTailOption[Repr,A0](val r: Repr)(implicit val fr: IsTraversableLike[Repr]{ type A = A0 }) { 
  def headTailOption: Option[(A0, Repr)] = { 
    val repr = fr.conversion(r)
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail)
  }
}

または同等:

import collection.generic.IsTraversableLike

type IsTraversableLikeAux[Repr,A0] = IsTraversableLike[Repr]{ type A = A0 }

implicit class HeadTailOption[Repr,A](val r: Repr)(implicit val fr: IsTraversableLikeAux[Repr,A]) { 
  def headTailOption: Option[(A, Repr)] = { 
    val repr = fr.conversion(r)
    if (repr.isEmpty) None 
    else Some(repr.head -> repr.tail)
  }
}

そして、すべてがうまくいきます。

scala> val Some((c, _)) = "one".headTailOption
c: scala.collection.generic.IsTraversableLike.stringRepr.A = o

scala> c.isSpaceChar
res0: Boolean = false

コンパイラscala.collection.generic.IsTraversableLike.stringRepr.Aは、 が と同じであることを認識していCharます。

于 2016-06-29T14:59:44.943 に答える