【问题标题】:Typeclass with multiple parameters. How to summon an instance having only one parameter?具有多个参数的类型类。如何召唤一个只有一个参数的实例?
【发布时间】:2019-11-13 23:26:00
【问题描述】:

我一直在尝试创建某种类型类,但遇到了隐式解析的问题。 基本上我想生成随机数据并调整生成参数 - 数字的间隔,数组的长度和间隔等。 这是我现在得到的:

import scala.util.Random

trait PrimitiveGenerator[T <: AnyVal, Bounds] {

  def generate(bounds: Bounds): T

  def generatePossibly(bounds: Bounds): Option[T] = if (Random.nextDouble() < 0.5) None else Some(generate(bounds))

}

object PrimitiveGenerator {
  def apply[A <: AnyVal](implicit impl: PrimitiveGenerator[A, _]) = impl
}

object Generators {

  implicit object IntGenerator extends PrimitiveGenerator[Int, (Int, Int)] {
    override def generate(bounds: (Int, Int) = (0, 1)): Int = Random.nextInt((bounds._2 - bounds._1) + 1) + bounds._1
  }

}


object Test extends App {

  import Generators._
  PrimitiveGenerator[Int].generate((0, 1)) //error is here
}

在 Intellij Idea 中这是失败的:

 found   : (Int, Int)
 required: _$1 where type _$1
  PrimitiveGenerator[Int].generate((0, 1))

我知道在这里使用下划线是错误的,但是我如何重写它以仅基于第一个类型参数 (A) 来调用 DataGenerator 的实例。从编译的角度来看,它似乎非常安全: 如果编译器找到 Int 的 DataGenerator,仅在一个示例中,它会立即使用它,如果它找到其中几个具有不同 Bounds 的,则返回错误。这个错误我可能会解决将此参数添加到隐式解析中。那你怎么看?

编辑 1 感谢@Dmytro Mitin 解决了这个问题! 进一步看,我想为 Array 做一个实现,这样如果我们想为一个数组调用一个实例,我们应该有一个 Array 参数类型的实例,如下所示:

import scala.reflect.ClassTag
import scala.util.Random

trait PrimitiveGenerator[T] {
  type Bounds
  def generate(bounds: Bounds): T
  def generatePossibly(bounds: Bounds): Option[T] = if (Random.nextDouble() < 0.5) None else Some(generate(bounds))
}

object PrimitiveGenerator {
  type Aux[T, Bounds0] = PrimitiveGenerator[T] { type Bounds = Bounds0 }
  def apply[A](implicit impl: PrimitiveGenerator[A]): Aux[A, impl.Bounds] = impl
}

object Generators {
  implicit object IntGenerator extends PrimitiveGenerator[Int] {
    override type Bounds = (Int, Int)
    override def generate(bounds: (Int, Int) = (0, 1)): Int = Random.nextInt((bounds._2 - bounds._1) + 1) + bounds._1
  }

  implicit def toGenericArrayGenerator[A](implicit generator: PrimitiveGenerator[A],
                                          classTag: ClassTag[A]): PrimitiveGenerator[Array[A]] = new PrimitiveGenerator[Array[A]] {
    override type Bounds = ((Int, Int), generator.Bounds) //It means generate array of length from n to m where elements comply to Bounds of base generator

    override def generate(bounds: Bounds): Array[A] = {
      Array[A]()
    }
  }
}

object Test extends App {
  import Generators._
  println(PrimitiveGenerator[Int].generate((0, 42)))
  PrimitiveGenerator[Array[Int]].generate((1 -> 10, 0 -> 42))
}

现在我正在尝试动态创建数组生成器,并且边界会根据基本生成器边界(按计划)略有变化。 但是出了点问题,现在我有:

Error: type mismatch;
 found   : ((Int, Int), (Int, Int))
 required: impl.Bounds
  PrimitiveGenerator[Array[Int]].generate((1 -> 10, 0 -> 42))

如何解决?

【问题讨论】:

    标签: scala typeclass implicit


    【解决方案1】:

    例如试试

    object PrimitiveGenerator {
      def apply[A <: AnyVal] = new PartiallyApplied[A]
    
      class PartiallyApplied[A <: AnyVal] {
        def apply[B]()(implicit impl: PrimitiveGenerator[A, B]) = impl
      }
    }    
    
    object Test extends App {
      import Generators._
      PrimitiveGenerator[Int]().generate((0, 1))
    }
    

    object PrimitiveGenerator {
      def generate[A <: AnyVal, B](b: B)(implicit impl: PrimitiveGenerator[A, B]) = impl.generate(b)
    }
    
    object Test extends App { 
      import Generators._
      PrimitiveGenerator.generate((0, 1))
    }
    

    trait PrimitiveGenerator[T <: AnyVal] {
      type Bounds
      def generate(bounds: Bounds): T
      def generatePossibly(bounds: Bounds): Option[T] = if (Random.nextDouble() < 0.5) None else Some(generate(bounds))
    }
    
    object PrimitiveGenerator {
      type Aux[T <: AnyVal, Bounds0] = PrimitiveGenerator[T] { type Bounds = Bounds0 }
      def apply[A <: AnyVal](implicit impl: PrimitiveGenerator[A]): Aux[A, impl.Bounds] = impl
    }
    
    object Generators {    
      implicit object IntGenerator extends PrimitiveGenerator[Int] {
        override type Bounds = (Int, Int)
        override def generate(bounds: (Int, Int) = (0, 1)): Int = Random.nextInt((bounds._2 - bounds._1) + 1) + bounds._1
      }    
    }
    
    object Test extends App { 
      import Generators._
      PrimitiveGenerator[Int].generate((0, 1))
    }
    

    trait PrimitiveGenerator[T <: AnyVal] {
      type Bounds = (T, T)
      def generate(bounds: Bounds): T
      def generatePossibly(bounds: Bounds): Option[T] = if (Random.nextDouble() < 0.5) None else Some(generate(bounds))
    }
    
    object PrimitiveGenerator {
      def apply[A <: AnyVal](implicit impl: PrimitiveGenerator[A]): PrimitiveGenerator[A] = impl
    }
    
    object Generators {
      implicit object IntGenerator extends PrimitiveGenerator[Int] {
        override def generate(bounds: (Int, Int) = (0, 1)): Int = Random.nextInt((bounds._2 - bounds._1) + 1) + bounds._1
      }
    }
    
    object Test extends App {
      import Generators._
      PrimitiveGenerator[Int].generate((0, 1))
    }
    

    【讨论】:

    • 非常感谢您提供的解决方案!你能看看这个问题的编辑部分吗?
    • @D.Silvest toGenericArrayGenerator 的返回类型应该是PrimitiveGenerator.Aux[Array[A], ((Int, Int), generator.Bounds)](即精炼类型),而不仅仅是PrimitiveGenerator[Array[A]]
    猜你喜欢
    • 1970-01-01
    • 2016-11-08
    • 2016-08-10
    • 1970-01-01
    • 2021-03-22
    • 2011-12-25
    • 2020-02-20
    • 1970-01-01
    • 2017-04-10
    相关资源
    最近更新 更多