#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
не является aMonad
,Id
образует aMonad
(вместе с правильной реализацией функций и формальными доказательствами законов) или обычно выражается,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]]...