Estimates for a FunctionK Type Parameter

I am using cats FreeMonad . Here's a simplified version of algebra:

sealed trait Op[A]

object Op {
    final case class Get[T](name: String) extends Op[T]

    type OpF[A] = Free[Op, A]

    def get[T](name: String): OpF[T] = liftF[Op, T](Get[T](name))
}

One of the interpreters will be a wrapper around a third-party library called Clienthere, where its method signature getis similar to:

class Client {
    def get[O <: Resource](name: String)
        (implicit f: Format[O], d: Definition[O]): Future[O] = ???
}

My question is: how can I code this requirement in my implementation?

class FutureOp extends (Op ~> Future) {
    val client = new Client()

    def apply[A](fa: Op[A]): Future[A] =
        fa match {
            case Get(name: String) =>
                client.get[A](name)
        }
}

I tried things like imposing restrictions on my apply(e.g. apply[A <: Resource : Format : Definition]), which did not work.

I understand that it FunctionKconsists in converting the values ​​of type types of a first-order variety, but is there anyway in which I can code the requirements of a type parameter?

I intend to use it like:

def run[F[_]: Monad, A](intp: Op ~> F, op: OpF[A]): F[A] = op.foldMap(intp)

val p: Op.OpF[Foo] = Op.get[Foo]("foo")

val i = new FutureOp()

run(i, d)
+3
source share
2 answers

( , , -, . . .)


TL; DR

  • T, get[T], DSL-, . implicits.
  • ~> trait RNT[R, F[_ <: R], G[_]]{ def apply[A <: R](x: F[A]): G[A] } . A <: Resource. .

:

  • Format Definition
  • <: Resource -type bound

. .

:

  • , .
  • , implicits, .
  • , implicits.
  • , .

, scalaVersion 2.12.4,

libraryDependencies += "org.typelevel" %% "cats-core" % "1.0.1"
libraryDependencies += "org.typelevel" %% "cats-free" % "1.0.1"

import scala.language.higherKinds

. , scala cats.


- , , . Resource, Format, Client .., .

, , Free. DSL, ( DslOp Dsl ):

import cats.free.Free
import cats.free.Free.liftF

sealed trait DslOp[A]
case class Get[A](name: String) extends DslOp[A]

type Dsl[A] = Free[DslOp, A]
def get[A](name: String): Dsl[A] = liftF[DslOp, A](Get[A](name))

get, A .

DSL, get, Client :

import scala.concurrent.Future

trait Resource
trait Format[A <: Resource]
trait Definition[A <: Resource]

object Client {
  def get[A <: Resource](name: String)
    (implicit f: Format[A], d: Definition[A]): Future[A] = ???
}

, get Client , .

implicits Free monad

, get implicits, :

import scala.concurrent.Future

trait Format[A]
trait Definition[A]

object Client {
  def get[A](name: String)(implicit f: Format[A], d: Definition[A])
  : Future[A] = ???
}

, , , apply ~>.

  • foldMap apply of FunctionK Dsl[X] Future[X].

  • Dsl[X] get[T1],..., get[Tn] T1,..., Tn.

  • T1,..., Tn Format[T_i] Definition[T_i].

  • .

  • Dsl[X], X, T1,..., Tn Definition Format .

  • Definition Format get[T_i] Dsl -program, .

, Format[A] Definition[A] case Get[A] Get[A] lift[DslOp, A] :

import cats.free.Free
import cats.free.Free.liftF
import cats.~>

sealed trait DslOp[A]
case class Get[A](name: String, f: Format[A], d: Definition[A]) 
  extends DslOp[A]

type Dsl[A] = Free[DslOp, A]
def get[A](name: String)(implicit f: Format[A], d: Definition[A])
: Dsl[A] = liftF[DslOp, A](Get[A](name, f, d))

~> -interpreter, , , :

val clientInterpreter_1: (DslOp ~> Future) = new (DslOp ~> Future) {
  def apply[A](op: DslOp[A]): Future[A] = op match {
    case Get(name, f, d) => Client.get(name)(f, d)
  }
}

case, DSL-

. , Client - , A:

import scala.concurrent.Future

trait Resource
object Client {
  def get[A <: Resource](name: String): Future[A] = ???
}

clientInterpreter , , A , Get[A] Client.get. , A <: Resource . - accept get. ~> accept . , :

trait RestrictedNat[R, F[_ <: R], G[_]] {
  def apply[A <: R](fa: F[A]): G[A]
}

~>, A <: R. accept get:

import cats.free.Free
import cats.free.Free.liftF
import cats.~>

sealed trait DslOp[A]
case class Get[A <: Resource](name: String) extends DslOp[A] {
  def accept[G[_]](f: RestrictedNat[Resource, Get, G]): G[A] = f(this)
}

type Dsl[A] = Free[DslOp, A]
def get[A <: Resource](name: String): Dsl[A] = 
  liftF[DslOp, A](Get[A](name))

, - -:

val clientInterpreter_2: (DslOp ~> Future) = new (DslOp ~> Future) {
  def apply[A](op: DslOp[A]): Future[A] = op match {
    case g @ Get(name) => {
      val f = new RestrictedNat[Resource, Get, Future] {
        def apply[X <: Resource](g: Get[X]): Future[X] = Client.get(g.name)
      }
      g.accept(f)
    }
  }
}

Get_1,..., Get_N, R1,..., RN. , .

:

import scala.concurrent.Future
import cats.free.Free
import cats.free.Free.liftF
import cats.~>

// Client-definition with both obstacles: implicits + type bound
trait Resource
trait Format[A <: Resource]
trait Definition[A <: Resource]

object Client {
  def get[A <: Resource](name: String)
    (implicit fmt: Format[A], dfn: Definition[A])
  : Future[A] = ???
}


// Solution:
trait RestrictedNat[R, F[_ <: R], G[_]] {
  def apply[A <: R](fa: F[A]): G[A]
}

sealed trait DslOp[A]
case class Get[A <: Resource](
  name: String,
  fmt: Format[A],
  dfn: Definition[A]
) extends DslOp[A] {
  def accept[G[_]](f: RestrictedNat[Resource, Get, G]): G[A] = f(this)
}

type Dsl[A] = Free[DslOp, A]
def get[A <: Resource]
  (name: String)
  (implicit fmt: Format[A], dfn: Definition[A])
: Dsl[A] = liftF[DslOp, A](Get[A](name, fmt, dfn))


val clientInterpreter_3: (DslOp ~> Future) = new (DslOp ~> Future) {
  def apply[A](op: DslOp[A]): Future[A] = op match {
    case g: Get[A] => {
      val f = new RestrictedNat[Resource, Get, Future] {
        def apply[X <: Resource](g: Get[X]): Future[X] = 
          Client.get(g.name)(g.fmt, g.dfn)
      }
      g.accept(f)
    }
  }
}

clientInterpreter_3 : a RestrictedNat case, , implicits DSL get -.

+2

, , ReaderT monad transformer :

import scala.concurrent.Future
import cats.~>
import cats.data.ReaderT
import cats.free.Free

object FreeMonads {
  sealed trait Op[A]

  object Op {
    final case class Get[T](name: String) extends Op[T]
    type OpF[A] = Free[Op, A]
    def get[T](name: String): OpF[T] = Free.liftF[Op, T](Get[T](name))
  }

  trait Resource
  trait Format[A]
  trait Definition[A]

  trait Client {
    def get[O <: Resource](name: String)
      (implicit f: Format[O], d: Definition[O]): Future[O]
  }

  type Result[A] = ReaderT[
    Future,
    (Format[A with Resource], Definition[A with Resource]),
    A,
  ]

  class FutureOp(client: Client) extends (Op ~> Result) {
    def apply[A](fa: Op[A]): Result[A] =
      fa match {
        case Op.Get(name: String) =>
          ReaderT {
            case (format, definition) =>
              // The `Future[A]` type ascription makes Intellij IDEA type
              // checker accept the code.
              client.get(name)(format, definition): Future[A]
          }
      }
  }
}

, Reader Op Reader , . O Format Definition.

, O Resource. , , Format Definition - A, A, Resource.

, FutureOp.

+1

Source: https://habr.com/ru/post/1694786/


All Articles