Scala FunctionK的类型参数的界限

Scala FunctionK的类型参数的界限,scala,scala-cats,Scala,Scala Cats,我正在使用猫。下面是代数的简化版本: 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)) } 其中一个口译员将是第三方库的包装器,在这里称为Client,其get方法的签名类似于

我正在使用猫。下面是代数的简化版本:

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))
}
其中一个口译员将是第三方库的包装器,在这里称为
Client
,其
get
方法的签名类似于:

class Client {
    def get[O <: Resource](name: String)
        (implicit f: Format[O], d: Definition[O]): Future[O] = ???
}
我尝试了一些方法,比如给我的
apply
(比如
apply[A(我最初的回答包含了相同的想法,但显然没有提供足够的实现细节。这次,我编写了一份更详细的分步指南,其中讨论了每个中间步骤。每个部分都包含一个单独的可编译代码段。)


TL;DR

  • get[T]
    中出现的每种类型
    T
    都需要隐式,因此它们必须在构造DSL程序时插入并存储,而不是在执行DSL程序时。这就解决了隐式的问题

  • 有一种通用的策略,可以从几个受限制的自然转换中粘合自然转换
    ~>
    trait RNT[R,F[\up>我想我已经找到了一种解决问题的方法,将a与:

    导入scala.concurrent.Future
    进口猫。~>
    导入cats.data.ReaderT
    进口猫。免费。免费
    对象自由单体{
    密封件Op[A]
    对象操作{
    最后一个案例类Get[T](名称:String)扩展了Op[T]
    类型OpF[A]=自由[Op,A]
    def get[T](名称:String):OpF[T]=Free.liftF[Op,T](get[T](名称))
    }
    性状资源
    特征格式[A]
    特征定义[A]
    特质客户{
    def get[O结果){
    def应用[A](fa:Op[A]):结果[A]=
    足总杯比赛{
    case Op.Get(名称:String)=>
    里德尔{
    案例(格式、定义)=>
    //'Future[A]`类型归属使Intellij IDEA的类型
    //检查者接受代码。
    client.get(名称)(格式、定义):Future[A]
    }
    }
    }
    }
    
    其基本思想是从
    Op
    生成
    Reader
    ,并且
    Reader
    接收可用于隐式参数的值。这解决了类型
    O
    具有
    格式
    定义
    实例的问题

    另一个问题是
    O
    Resource
    的一个子类型。为了解决这个问题,我们只是说
    格式和
    定义
    实例不仅仅是任何
    a
    的实例,而是碰巧也是
    资源的任何
    a
    的实例


    如果您在使用
    FutureOp

    时遇到问题,请告诉我,一个想法是首先从
    Op[A]
    转到
    ({type L[A]=ReaderT[Future,(Format[A],Definition[A])L
    ,然后最后从那转到
    Future[A]
    。不确定它是否有效,但我会尝试这样做。换句话说,转换到读卡器以获得(隐式)params,然后调用
    客户机
    方法。我已经尝试过了,但不幸的是,由于
    资源
    子类型的限制,它不起作用:你能用一个如何使用
    Op
    DSL的示例更新这个问题吗?@ionuț-g-stan刚刚感谢你的回答,它真的很有帮助。现在我需要一个
    Monad
    例如,
    Result
    能够运行它,对吗?有一个类似于的运行方法:
    def run[F[\u]:Monad,a](intp:Op~>F,Op:OpF[a]):F[a]=Op.foldMap(intp)
    cats文档说“只要选择了F[\u]它就有一个Monad实例。”,而且由于F是
    未来
    ,我已经导入了
    猫.实例.期货.
    猫.隐式.
    但仍有错误。@racetrack您可能需要范围内的隐式
    执行上下文
    。@ionuțg-stan我有一个。@ionuțg.stan我是否正确理解此解决方案现在需要
    执行上下文
    必须以某种方式为所有可能的类型
    A
    提供rmat
    定义
    ?这将限制
    格式
    定义
    只适用于
    A
    中自然的事物,但我不确定OP希望从何处获得适用于所有类型的高度规则和良好的
    格式
    不知何故,在我看来,这个解决方案似乎是这样说的:“假设我们所有
    A
    都有
    格式
    s和
    定义
    s……”,只是将
    格式的问题推到更深一层?谢谢你的回答。我不明白的是,在DSL中更改
    get
    如何解决调用
    客户端的问题。使用
    [A]获取
    ?问题是
    A
    的边界没有在
    apply
    中指定。问题是如果我没有在
    client.get
    中指定类型参数,我将得到
    发散隐式扩展
    错误。如果我只说
    client.get[A]
    ,我将得到
    类型参数[A]不符合方法get的类型参数边界[O@racetrack为什么不在
    客户端中指定类型参数。get
    ?一旦
    格式和
    定义存储在
    中,get[A
    可以调用
    客户端。get[A](名称)(fmt,dfn)
    显式。问题是这样做行不通。它会抛出一个错误,即它不符合方法get的类型参数界限
    [O
    
    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)
            }
    }
    
    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)
    
    libraryDependencies += "org.typelevel" %% "cats-core" % "1.0.1"
    libraryDependencies += "org.typelevel" %% "cats-free" % "1.0.1"
    
    import scala.language.higherKinds
    
    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))
    
    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] = ???
    }
    
    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] = ???
    }
    
    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))
    
    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)
      }
    }
    
    import scala.concurrent.Future
    
    trait Resource
    object Client {
      def get[A <: Resource](name: String): Future[A] = ???
    }
    
    trait RestrictedNat[R, F[_ <: R], G[_]] {
      def apply[A <: R](fa: F[A]): G[A]
    }
    
    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)
        }
      }
    }
    
    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)
        }
      }
    }
    
    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's type
                  // checker accept the code.
                  client.get(name)(format, definition): Future[A]
              }
          }
      }
    }