【问题标题】:Aux-like pattern for path dependent types?路径相关类型的类似辅助模式?
【发布时间】:2018-04-30 05:27:10
【问题描述】:

目标:我想写

feedImplicitInstance[Cat](new CatFood())`

效果一样
feedExplicitInstance(Cat.CatInstance)(new CatFood())

我该怎么做? 有可能这样做吗?

这是我尝试过的(但它并没有真正起作用):

object DepType extends App{

  println("bla")

  def feedExplicitInstance[AnimalInstance]
      (animal:AnimalTypeClass[AnimalInstance])(food:animal.FoodThatAnimalLikes) = {
      animal.feed(food)
  }

 //  Does not compile:

  def feedImplicitInstance[AnimalInstance,Food](food:Food)
  (implicit animal:AnimalTypeClass[AnimalInstance],aux:Cat.Aux[Food,AnimalInstance]) = {
    animal.feed(food)
//    Error:(17, 17) type mismatch;
//    found   : food.type (with underlying type Food)
//    required: animal.FoodThatAnimalLikes
//    animal.feed(food)

  }
  feedExplicitInstance(Cat.CatInstance)(new CatFood())

}

trait Food{
  def eat():Unit
}

trait AnimalTypeClass [AnimalInstance] {
  type FoodThatAnimalLikes <: Food
  def feed(f:FoodThatAnimalLikes)=f.eat()
}

trait Cat

class CatFood extends Food{
  override def eat(): Unit = println("meow")
}

object Cat {
  type Aux[Food,Animal]= AnimalTypeClass[Animal] {type FoodThatAnimalLikes = Food}

  implicit object CatInstance extends AnimalTypeClass[Cat]{
    override type FoodThatAnimalLikes = CatFood
  }
}

【问题讨论】:

    标签: scala typeclass type-level-computation path-dependent-type


    【解决方案1】:

    首先,将Aux放在AnimalTypeClass伴生对象中更有意义,并切换其类型参数的顺序。尽管所有这些都不是必需的才能使其编译。

    为了启用您首选的feedImplicitInstance[Cat](new CatFood()) 调用约定feedImplicitInstance 只允许有一个类型参数。但是Food 必须是类型参数,因为在参数列表中不允许像animal.FoodThatAnimalLikes 这样的前向引用,您可能已经注意到了。这就是为什么您首先需要Aux。要协调这些冲突的约束,您应该手动实现一种类型参数柯里化。这就是以下完整示例中的 Feeder 类的用途:

    object DepType extends App {
    
      def feedExplicitInstance[AnimalInstance]
          (animal: AnimalTypeClass[AnimalInstance])(food: animal.FoodThatAnimalLikes) = {
          animal.feed(food)
      }
      class Feeder[AnimalInstance] { 
        def apply[F <: Food](food: F)(implicit animal: AnimalTypeClass.Aux[AnimalInstance, F]) =
          animal.feed(food)
      }
      def feedImplicitInstance[AnimalInstance] = new Feeder[AnimalInstance]
    
      feedExplicitInstance(Cat.CatInstance)(new CatFood())
      feedImplicitInstance[Cat](new CatFood())
    
    }
    
    trait Food{
      def eat(): Unit
    }
    class CatFood extends Food{
      override def eat(): Unit = println("meow")
    }
    
    trait AnimalTypeClass[AnimalInstance] {
      type FoodThatAnimalLikes <: Food
      def feed(f: FoodThatAnimalLikes) = f.eat()
    }
    object AnimalTypeClass {
      type Aux[A, F <: Food]= AnimalTypeClass[A] {type FoodThatAnimalLikes = F}
    }
    
    trait Cat
    object Cat {
      implicit object CatInstance extends AnimalTypeClass[Cat]{
        override type FoodThatAnimalLikes = CatFood
      }
    }
    

    【讨论】:

    • Jasper,同样感谢您,非常感谢您的回答!我很快就会完成它(并尝试掌握它),目前事情有点忙,他们分散了我很多注意力。
    【解决方案2】:

    如果我们这样定义Aux

    object AnimalTypeClass {
      type Aux[A, F] = AnimalTypeClass[A] { type FoodThatAnimalLikes = F }
    
      implicit object CatInstance extends AnimalTypeClass[Cat] {
        override type FoodThatAnimalLikes = CatFood
      }
    }
    

    然后我们可以通过一个方法调用正确的隐式类型类:

    def feed[A, F <: Food](f: F)(implicit animalTypeClass: AnimalTypeClass.Aux[A, F]) = {
      animalTypeClass.feed(f)
    }
    

    现在编译并运行:

    feed(new CatFood())
    

    我稍微更改了泛型类型参数的名称,但它们与您的示例中的基本相同。只需注意隐式实例定义的更改即可。

    【讨论】:

    • 哇,非常感谢您的详细回答,我将很快详细介绍(并尝试掌握它)-目前事情有点忙。
    • @jhegedus 当然可以。 LMK 如果您有任何问题
    • 我不太明白implicit def feedImplicitInstance[A, F &lt;: Food](...):Unit 在做什么。我的意思是,它返回Unit,那么它怎么能用于任何事情呢?它除了副作用没有别的。我很困惑。
    • ^ 您对此有何看法? @Yuvil
    • @jhegedus 你完全正确。没有必要,因为feed 方法几乎相同。
    【解决方案3】:

    使用 Yuval 的回答,这就是我修改后的代码的样子:

    object DepType2 extends App{
    
      println("bla")
    
      def feedExplicitInstance[AnimalInstance]
      (animal:AnimalTypeClass[AnimalInstance])(food:animal.FoodThatAnimalLikes) = {
        animal.feed(food)
      }
    
      def feedImplicitInstance[AnimalInstance,Food](food:Food)
                                                   (implicit animal:AnimalTypeClass[AnimalInstance],aux:AnimalTypeClass.Aux[Food,AnimalInstance]) = {
        aux.feed(food)
    
      }
      feedExplicitInstance(AnimalTypeClass.CatInstance)(new CatFood())
      feedImplicitInstance(new CatFood())
    
    }
    
    trait Food{
      def eat():Unit
    }
    
    trait AnimalTypeClass [AnimalInstance] {
      type FoodThatAnimalLikes <: Food
      def feed(f:FoodThatAnimalLikes)=f.eat()
    }
    
    trait Cat
    
    class CatFood extends Food{
      override def eat(): Unit = println("meow")
    }
    
    object AnimalTypeClass {
      type Aux[Food,Animal]= AnimalTypeClass[Animal] {type FoodThatAnimalLikes = Food}
    
      implicit object CatInstance extends AnimalTypeClass[Cat]{
        override type FoodThatAnimalLikes = CatFood
      }
    }
    

    所以我不得不将feedImplicitInstance 中的animal 更改为aux,并将object Cat 更改为object AnimalTypeClass,现在一切正常。

    当然,最初的问题有点棘手: 怎么写feedImplicitInstance[Cat](new CatFood())

    Jasper 的答案是原始问题的答案。我还没有完全理解他的答案——我需要找点时间仔细阅读,希望很快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-23
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-22
      • 2021-11-17
      • 1970-01-01
      相关资源
      最近更新 更多