Скалаз, цель * синтаксических классов - PullRequest
0 голосов
/ 11 июня 2018

В 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)

Код немного изменен.Но идея заключается в том, что мы определяем:

  1. Абстракция и ее API (алгебра)
  2. Определение вспомогательных универсальных функций, которые используют имплики и сами имплициты
  3. Расширение существующих типовчтобы иметь возможность использовать нашу новую абстракцию.Для этого используется неявное преобразование.В 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 черта может использоваться клиентом.Точно это не ясно.

Прямо сейчас я вижу попытки объяснить другие темы, но не оригинал:

  1. Как создавать неявные классы вместо неявных функций.
  2. Разница между финальным * классом Ops и чертой, включая их видимость.Здесь мы сравниваем 2 черты с одинаковой видимостью.
  3. Объясняя в общих чертах метод внедрения, как они помогают.Эта функциональность предоставляется уже с ToFunctorOps.

Ребята, еще раз, пожалуйста, покажите сообществу USE CASES via CODE of FunctorSyntax.Сам код всегда лучшая документация.

С уважением

Ответы [ 2 ]

0 голосов
/ 10 августа 2018

Вот пример использования, в котором вы должны использовать functorSyntax:

import org.scalatest.{FreeSpec, Matchers}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scalaz._
import Scalaz._

import scala.language.postfixOps

class ScalazTest extends FreeSpec with Matchers {
  "compose functors" in {
    val composedFunctor = Functor[Future] compose Functor[List] compose Functor[Option]
    import composedFunctor.functorSyntax._

    val actual = Future.successful(List(Some(1), Some(2), None, Some(4))) fmap (x => x * 2)
    Await.result(actual, 10 seconds) shouldBe List(Some(2), Some(4), None, Some(8))
  }
}

Идея состоит в том, что вы можете создать несколько наборов функторов и импортировать конечный составной экземпляр функтора в область действия и работать с ним.Обратите внимание, что fmap разрешается в composedFunctor.functorSyntax в этом случае, и оно работает на 3 уровнях вложенности (Future[List[Option[Integer]]]), все еще принимая функцию, которая работает с примитивными типами.

0 голосов
/ 12 июня 2018

Из того, что я вижу в кодовой базе скалаза, я думаю, что FunctorSyntax подразумевается как альтернативный способ включения синтаксиса.Они определяют Functor следующим образом (упрощенно):

trait Functor {
  def map[A, B](fa: F[A])(f: A => B): F[B]

  val functorSyntax = new FunctorSyntax[F] { def F = Functor.this }
}

Это позволяет работать следующим образом:

def foo[F[_]](f: F[String])(implicit F: Functor[F]): F[Int] = {
  import F.functorSyntax._
  f.map(_.length)
}

Сравните с тем, как ToFunctorOps добавляет синтаксис:

package scalaz.syntax { // simplified version of the scalaz codebase
  object functor extends ToFunctorOps 
}

import scalaz.syntax.functor._
def foo[F[_]: Functor](f: F[String]): F[Int] = f.map(_.length)
...