Аргументы типа Scala не соответствуют границам параметров типа метода eval

#scala #generics

#scala #generics

Вопрос:

Я разрабатываю Monad в Scala без классов типов.

У меня есть эти черты для monad:

 trait Monad[M[_], A] {
  def bind[B](f: A => M[B]): M[B]
}
trait MonadCompanion[M[_]] {
  def unit[A](a: A): M[A]
}
 

Затем я реализовал identity monad:

 case class Id[A](a: A) extends Monad[Id, A] {
  override def bind[B](f: A => Id[B]): Id[B] = f(a)
}
implicit object Id extends MonadCompanion[Id] {
  def unit[A](a: A): Id[A] = Id(a)
}
 

Все работает правильно. Наконец, я должен создать эту универсальную функцию в Monad (следуя статье Wadler):

 sealed trait Term
case class Constant(c: Int) extends Term
case class Div(t1: Term, t2: Term) extends Term

lazy val answer = Div(Div(Constant(1972), Constant(2)), Constant(23))

def eval[M[_] <: Monad[M,Int]](e : Term)(implicit companion: MonadCompanion[M]) : M[Int] = e match {
  case Constant(a) => companion.unit(a)
  case Div(t, u) => eval(t).bind((a  : Int) => eval(u).bind((b : Int) => companion.unit(a / b)))
}
 

Но вот проблема: когда я пытаюсь вызвать eval, компилятор говорит

 type arguments [Id] do not conform to method eval's type parameter bounds [M[_] <: Monad[M,Int]]
      println(Id.unit(answer).bind(eval[Id](_)(Id)))
 

Более того, у меня проблема с неявным:

 could not find implicit value for parameter companion: MonadCompanion[M]
      println(Id.unit(answer).bind(eval)) 
 

Как я могу их обойти?

Комментарии:

1. Предоставьте определения Term , Constant , Div .

2. На самом деле невозможно воспроизвести. Код компилируется без ошибок scastie.scala-lang.org/DmytroMitin/YJXijFYpR863aVymvlTJYA

3. Ага, я вижу. Проблемная строка Id.unit(answer).bind(eval[Id](_)(Id)) . Что такое answer ?

4. Извините: lazy val answer = Div(Div(Constant(1972), Constant(2)), Constant(23))

5.«Я разрабатываю Monad в Scala без классов типов» в чем вообще смысл этого? Просто усложняет код, нарушает базовое понимание этих понятий; Id не является a Monad , Id образует a Monad (вместе с правильной реализацией функций и формальными доказательствами законов) или обычно выражается, Id имеет связанный с ним экземпляр Monad . — А также, в конце вы все еще используете класс типов для получения unit , потому что полиморфизм подтипов не может выражать операции над самим типом.

Ответ №1:

Привязка M[_] <: Monad[M, Int] означает «независимо от того, какой аргумент X я даю M , M[X] <: Monad[M, Int] . Это неправильно. Привязка должна быть M[X] <: Monad[M, X] : «независимо от того, какой аргумент X я даю M M[X] <: Monad[M, X] «. X это локальная переменная, которая объявляется просто путем указания имени в привязке и выходит за пределы области видимости после привязки. Кроме того, рекурсивные вызовы должны M быть указаны:

 def eval[M[X] <: Monad[M, X]](e : Term)(implicit companion: MonadCompanion[M]) : M[Int] = e match {
  case Constant(a) => companion.unit(a)
  case Div(t, u) => eval[M](t).bind(a => eval[M](u).bind(b => companion.unit(a / b)))
}
 

Ответ №2:

Например, вы можете изменить привязку типа

 def eval[M[A] <: Monad[M,A]]...