В scalaz, когда мы определяем модуль, мы дополнительно определяем неявные вспомогательные функции.Вот пример определения и того, как он может использоваться клиентом:
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
object Functor {
def fmap[F[_], A,B](as:F[A])(f:A=>B)
(implicit ff:Functor[F]):F[B] =
ff.map(as)(f)
implicit val listFunctor = new Functor[List] {
def map[A,B](as: List[A])(f: A => B): List[B] = as map f
}
}
...
import com.savdev.NewLibrary._
val r = fmap(List(1,2))(_.toString)
final class FunctorOps[F[_], A](self: F[A])(implicit ff:Functor[F]){
def qmap[B](f:A=>B):F[B] = ff.map(self)(f)
}
trait ToFunctorOps {
implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
new FunctorOps[F,A](v)
}
object NewLibrary extends ToFunctorOps
...
import com.savdev.NewLibrary._
val r2 = List(1, 4) qmap (x=>x.toString)
Код немного изменен.Но идея заключается в том, что мы определяем:
- Абстракция и ее API (алгебра)
- Определение вспомогательных универсальных функций, которые используют имплики и сами имплициты
- Расширение существующих типовчтобы иметь возможность использовать нашу новую абстракцию.Для этого используется неявное преобразование.В scalaz мы определяем конечный класс для обертки и неявных преобразователей в чертах
Все вышеизложенное мотивирует его и то, как он может использоваться клиентом.Но в scalaz
с каждым таким определением модуля есть также связанный класс *Syntax
.Я не могу понять цель этого.Не могли бы вы объяснить, почему это необходимо и КАК его можно использовать в клиентском коде.
В Scalaz это определяется как:
trait FunctorSyntax[F[_]] {
implicit def ToFunctorOps[A](v: F[A]): FunctorOps[F, A] =
new FunctorOps[F, A](v)(FunctorSyntax.this.F)
def F: Functor[F]
}
ОБНОВЛЕНО:
Ребята, кажется, я не достаточно ясен, или тема более сложна для всех нас.
Мне нужно понять разницу между двумя чертами:
trait ToFunctorOps {
implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
new FunctorOps[F,A](v)
}
против
trait FunctorSyntax[F[_]] {
implicit def ToFunctorOps[A](v: F[A]): FunctorOps[F, A] =
new FunctorOps[F, A](v)(FunctorSyntax.this.F)
def F: Functor[F]
}
Обе черты определяют общий метод, который создает FunctorOps
, у обоих одинаковые правила видимости.Первая черта ToFunctorOps
, сама по себе не является универсальной, она определяет только универсальный метод с [F[_],A]
.В результате я могу объединить множество таких признаков в один объект и импортировать их все сразу.Я привел пример того, как клиент может использовать такие черты:
object NewLibrary extends ToFunctorOps
...
import com.savdev.NewLibrary._
val r2 = List(1, 4) qmap (x=>x.toString)
Эта черта уже дает клиентам возможность вводить методы неявно.Зачем нам нужно FunctorSyntax
?Эта черта FunctorSyntax является общей для [F[_]]
.Когда я расширяю его, я должен предоставить тип в определении.Поскольку F[_]
теперь используется в определении черты, функция имеет менее общие параметры, только [A]
.
Я прошу вас, ребята, если вы можете помочь и понять, дайте мне пример кода, как это FunctorSyntax
черта может использоваться клиентом.Точно это не ясно.
Прямо сейчас я вижу попытки объяснить другие темы, но не оригинал:
- Как создавать неявные классы вместо неявных функций.
- Разница между финальным * классом Ops и чертой, включая их видимость.Здесь мы сравниваем 2 черты с одинаковой видимостью.
- Объясняя в общих чертах метод внедрения, как они помогают.Эта функциональность предоставляется уже с
ToFunctorOps
.
Ребята, еще раз, пожалуйста, покажите сообществу USE CASES via CODE of FunctorSyntax
.Сам код всегда лучшая документация.
С уважением