19

tl;dr : 以下の構成されたコードのようなことを行うにはどうすればよいですか:

def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"

Not[Functor]」は、ここで構成されている部分です。
提供された 'm' が Functor でない場合は成功し、それ以外の場合はコンパイラを失敗させたいです。

解決済み: 残りの質問をスキップして、すぐに下の回答に進んでください。


私が達成しようとしているのは、大まかに言えば「否定的な証拠」です。

擬似コードは次のようになります。

// type class for obtaining serialization size in bytes.
trait SizeOf[A] { def sizeOf(a: A): Long }

// type class specialized for types whose size may vary between instances
trait VarSizeOf[A] extends SizeOf[A]

// type class specialized for types whose elements share the same size (e.g. Int)
trait FixedSizeOf[A] extends SizeOf[A] {
  def fixedSize: Long
  def sizeOf(a: A) = fixedSize
}

// SizeOf for container with fixed-sized elements and Length (using scalaz.Length)
implicit def fixedSizeOf[T[_] : Length, A : FixedSizeOf] = new VarSizeOf[T[A]] {
  def sizeOf(as: T[A]) = ... // length(as) * sizeOf[A]
}

// SizeOf for container with scalaz.Foldable, and elements with VarSizeOf
implicit def foldSizeOf[T[_] : Foldable, A : SizeOf] = new VarSizeOf[T[A]] {
  def sizeOf(as: T[A]) = ... // foldMap(a => sizeOf(a))
}

fixedSizeOf()コレクションのトラバーサルを節約できるため、関連する場合はこれが望ましいことに注意してください。

Lengthこのようにして、 のみが定義されている (ただし は定義されていない)コンテナー タイプFoldableと、 a が定義されている要素の場合、FixedSizeOfパフォーマンスが向上します。

残りのケースでは、コレクションを調べて個々のサイズを合計します。

私の問題は、 と の両方LengthFoldableコンテナーに対してFixedSizeOf定義され、 が要素に対して定義されている場合です。これはここでは非常に一般的なケースです (例: List[Int]has both defined)。

例:

scala> implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3))
<console>:24: error: ambiguous implicit values:
 both method foldSizeOf of type [T[_], A](implicit evidence$1: scalaz.Foldable[T], implicit evidence$2: SizeOf[A])VarSizeOf[T[A]]
 and method fixedSizeOf of type [T[_], A](implicit evidence$1: scalaz.Length[T], implicit evidence$2: FixedSizeOf[A])VarSizeOf[T[A]]
 match expected type SizeOf[List[Int]]
              implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3))

私が望むのは、 +の組み合わせが適用されないFoldable場合にのみ、型クラスに依存できるようにすることです。LengthFixedSizeOf

その目的のために、要素foldSizeOf()を受け入れるように の定義を変更できます。VarSizeOf

implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...

そして、要素が含まれていて定義されていないFoldableコンテナーをカバーする問題のある部分を埋める必要があります。これにアプローチする方法はわかりませんが、擬似コードは次のようになります。FixedSizeOfLength

implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...

Not[Length]」は、明らかに、ここで構成された部分です。

私が知っている部分的な解決策

1) ' ' に示すように、優先度の低い暗黙のクラスを定義し、それを拡張しobject Predef extends LowPriorityImplicitsます。最後の暗黙的な ( foldSizeOfFixed()) は親クラスで定義でき、子孫クラスの代替によってオーバーライドされます。

最終的に の再帰的な使用をサポートできるようにしたいので、このオプションには興味がありません。これによりSizeOf、優先度の低い基本クラスの暗黙的なものがサブクラスのものに依存するのを防ぐことができます (ここでの私の理解は正しいですか? 編集: 違います! サブクラスのコンテキストから暗黙のルックアップが機能します。これは実行可能な解決策です!)

2) より大まかなアプローチは依存していますOption[TypeClass](例: . それらのいくつかと私は、andを必須およびオプションとして選択し、利用可能な場合は後者に依存するOption[Length[List]]1 つの大きな古い暗黙的なものを書くことができます。 (ソース:ここでFoldableSizeOfLengthFixedSizeOf

ここでの 2 つの問題は、モジュール性の欠如と、関連する型クラスのインスタンスが見つからない場合の実行時例外へのフォールバックです (この例は、おそらくこのソリューションで動作するように作成できますが、常に可能であるとは限りません)。

編集:これは、オプションの暗黙的なもので取得できた最高のものです。まだありません:

implicit def optionalTypeClass[TC](implicit tc: TC = null) = Option(tc)
type OptionalLength[T[_]] = Option[Length[T]]
type OptionalFixedSizeOf[T[_]] = Option[FixedSizeOf[T]]

implicit def sizeOfContainer[
    T[_] : Foldable : OptionalLength,
    A : SizeOf : OptionalFixedSizeOf]: SizeOf[T[A]] = new SizeOf[T[A]] {
  def sizeOf(as: T[A]) = {

    // optionally calculate using Length + FixedSizeOf is possible
    val fixedLength = for {
      lengthOf <- implicitly[OptionalLength[T]]
      sizeOf <- implicitly[OptionalFixedSizeOf[A]]
    } yield lengthOf.length(as) * sizeOf.fixedSize

    // otherwise fall back to Foldable
    fixedLength.getOrElse { 
      val foldable = implicitly[Foldable[T]]
      val sizeOf = implicitly[SizeOf[A]]
      foldable.foldMap(as)(a => sizeOf.sizeOf(a))
    }
  }
}

fixedSizeOf()これが以前から衝突することを除いて、これはまだ必要です。

助けや視点をありがとう:-)

4

1 に答える 1

16

私は最終的に、継承を使用して優先順位を付ける必要のないあいまいさベースのソリューションを使用してこれを解決しました。

これを一般化する私の試みは次のとおりです。

型を使用して、Not[A]負の型クラスを構築します。

import scala.language.higherKinds

trait Not[A]

trait Monoid[_] // or import scalaz._, Scalaz._
type NotMonoid[A] = Not[Monoid[A]] 

trait Functor[_[_]] // or import scalaz._, Scalaz._
type NotFunctor[M[_]] = Not[Functor[M]]

...これは、コンテキスト境界として使用できます。

def foo[T: NotMonoid] = ...

Not[A] のすべての有効な式が少なくとも 1 つの暗黙的なインスタンスを取得することを保証することによって続行します。

implicit def notA[A, TC[_]] = new Not[TC[A]] {}

インスタンスは 'notA' -- 'not' と呼ばれます。これが 'Not[TC[A]]' に対して見つかった唯一のインスタンスである場合、否定型クラスが適用されることが判明したためです。'A' は通常、フラット型 (例: Int) を処理するメソッドに追加されます。

望ましくない型クラスが適用されるケースを回避するために、あいまいさを導入します。

implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {}

これは 'NotA' とほぼ同じですが、'TC' で指定された型クラスのインスタンスが暗黙のスコープに存在する型のみに関心がある点が異なります。インスタンスは 'notNotA' と名付けられています。これは、ルックアップされる暗黙的なものと一致させるだけでは、'notA' とあいまいさが生じ、暗黙的な検索 (これが私たちの目標です) に失敗するためです。

使用例を見てみましょう。上記の「NotMonoid」ネガティブ型クラスを使用します。

implicitly[NotMonoid[java.io.File]] // succeeds
implicitly[NotMonoid[Int]] // fails

def showIfNotMonoid[A: NotMonoid](a: A) = a.toString

showIfNotMonoid(3) // fails, good!
showIfNotMonoid(scala.Console) // succeeds for anything that isn't a Monoid

ここまでは順調ですね!ただし、M[_] の形をした型と TC[_[_]] の形をした型クラスは、上記のスキームではまだサポートされていません。それらにも暗黙を追加しましょう。

implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {}
implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {}

implicitly[NotFunctor[List]] // fails
implicitly[NotFunctor[Class]] // succeeds

十分に単純です。Scalaz には、いくつかの型形状を扱うことから生じる定型文に対する回避策があることに注意してください。'Unapply' を探してください。TC[_[_]] (Functor など) では魅力的に動作したにもかかわらず、基本的なケース (Monoid などの形状 TC[_] の型クラス) では使用できませんでした。したがって、この回答はそれをカバーしていません。

興味のある方は、1 つのスニペットで必要なすべてを以下に示します。

import scala.language.higherKinds

trait Not[A]

object Not {
  implicit def notA[A, TC[_]] = new Not[TC[A]] {}
  implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {}

  implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {}
  implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {}
}

import Not._

type NotNumeric[A] = Not[Numeric[A]]
implicitly[NotNumeric[String]] // succeeds
implicitly[NotNumeric[Int]] // fails

質問で求めた疑似コードは次のようになります(実際のコード):

// NotFunctor[M[_]] declared above
def notFunctor[M[_] : NotFunctor](m: M[_]) = s"$m is not a functor"

更新:暗黙的な変換に適用される同様の手法:

import scala.language.higherKinds

trait Not[A]

object Not {
  implicit def not[V[_], A](a: A) = new Not[V[A]] {}
  implicit def notNot[V[_], A <% V[A]](a: A) = new Not[V[A]] {}
}

(例) 型が Ordered として表示できない場合にのみ値を受け入れる関数を定義できるようになりました。

def unordered[A <% Not[Ordered[A]]](a: A) = a
于 2013-04-13T01:10:17.590 に答える