Я пытаюсь схватить higher-order-polymophism в scala путем реализации очень простого интерфейса, который описывает монаду, но я сталкиваюсь с проблемой, которую я действительно не понимаю.
Я реализовал то же с C++, и код похож на это:
#include <iostream>
template <typename T>
class Value {
private:
T value;
public:
Value(const T& t) {
this->value = t;
}
T get() {
return this->value;
}
};
template < template <typename> class Container >
class Monad {
public:
template <typename A> Container<A> pure(const A& a);
};
template <template <typename> class Container>
template <typename A>
Container<A> Monad<Container>::pure(const A& a) {
return Container<A>(a);
}
int main() {
Monad<Value> m;
std::cout << m.pure(1).get() << std::endl;
return 0;
}
При попытке сделать то же с scala я перестал работать:
class Value[T](val value: T)
class Monad[Container[T]] {
def pure[A](a: A): Container[A] =
Container[A](a)
}
object Main {
def main(args: Array[String]): Unit = {
val m = new Monad[Value]
m.pure(1)
}
}
Компилятор жалуется на:
[raichoo@lain:Scala]:434> scalac highorder.scala
highorder.scala:5: error: not found: value Container
Container[A](a)
^
one error found
Что я делаю неправильно здесь? Кажется, существует фундаментальное понятие, которое я, кажется, не понимаю о scala typeconstructors.
С уважением, raichoo
Типаж Monad
в Scala будет объявлен следующим образом:
trait Monad[M[_]] {
def pure[A](a: => A): M[A]
def bind[A,B](a: M[A], f: A => M[B]): M[B]
}
Обратите внимание, что он параметризован конструктором типа M [_]
. Заключенное в квадратные скобки подчеркивание означает, что M
является конструктором типа вида (* -> *)
(то есть M
принимает некоторый тип A
для построения типа M [A]
). Тогда ваш экземпляр монады идентичности будет записан следующим образом:
class Value[A](a: => A) { lazy val value = a }
implicit val identityMonad = new Monad[Value] {
def pure[A](a: => A) = new Value(a)
def bind[A,B](a: Value[A], f: A => Value[B]) = new Value(f(a.value).value)
}
Это определение использует параметры по имени для достижения ленивой семантики.
Монада и другие полезные классы типов более высокого порядка предоставляются библиотекой Scalaz вместе с множеством экземпляров для стандартных библиотек Java / Scala.
Если вы измените определение класса Monad
на следующее
class Monad[Container[_]] {
def pure[A <% Container[A]](a: A): Container[A] = a
}
Синтаксис Container [_]
- это то, как высшие типы выражаются в Scala. A <% Container [A]
- это «граница представления», которая выражает, что A
неявно конвертируется в Container [A]
. В теле метода используется это неявное преобразование. Чтобы использовать этот класс, вам необходимо иметь неявное преобразование в области (в вашем примере) Int
в Value [Int]
implicit def toValue[T](t:T) = new Value(t)
Затем вы можете сделать следующее
scala> val m = new Monad[Value]
m: Monad[Value] = Monad@781fb069
scala> m.pure(1).value
res3: Int = 1
Не уверен, что было бы лучшим решением, но в определении чистого в вашем коде:
class Monad[Container[T]] {
def pure[A](a: A): Container[A] = Container[A](a)
}
Что должен делать Контейнер [A] (a)
? До сих пор вы определили контейнер как универсальный тип XXX, и у вас нет информации о том, как создать новый объект. Вам необходимо передать объект "строитель" как неявный параметр.
Посмотрите, как библиотеки коллекций реализованы в Scala 2.8 или определение Monad в Scalaz