gpt4 book ai didi

Scalac 无法推断归纳构建的路径相关类型

转载 作者:行者123 更新时间:2023-12-01 09:17:41 27 4
gpt4 key购买 nike

我正在为 Scala 开发 servant-server 端口。这个想法是使用类型类解析来归纳构建可以处理请求的函数。我遇到了一些我不太明白的奇怪推理问题。

object Servant {                                                                     

class :>[Path, A]

trait HasServer[A] {
type ServerT

def route(a: ServerT): String
}

implicit val unitServer = new HasServer[Unit] {
type ServerT = String

def route(str: ServerT): String = str
}

implicit def subServer[A, Sub](implicit sub: HasServer[Sub]) = new HasServer[A :> Sub] {
type ServerT = A => sub.ServerT

def route(handler: ServerT): String = "handler"
}

}

有了上面的内容,下面的编译就失败了:

val foo = implicitly[HasServer[Int :> Unit]]
implicitly[=:=[Int => String, foo.ServerT]]

错误是:

servant.scala:33: error: 
Cannot prove that Int => String =:= Main.$anon.Servant.foo.ServerT.

但是,如果我通过以下方式直接实例化 HasServer[Int :> Unit],它编译:

  val foo = new HasServer[Int :> Unit] {                                             
type ServerT = Int => unitServer.ServerT

def route(handler: ServerT): String = handler(10)
}

我怎样才能编译它?谢谢!

最佳答案

问题都出在implicitly的定义上...

def implicitly[T](implicit e: T) = e

implicitly[T]只会给你一个输入为 T 的值, 没有比这更精确的了。在上面的例子中,这是 HasServer[Int :> Unit]其中,至关重要的是,留下成员类型 ServerT不受约束。

这通常通过为每个类型类定义一个伴生对象来解决 apply保留所需细化的方法,例如,

object HasServer {
def apply[T](implicit hs: HasServer[T]):
HasServer[T] { type ServerT = hs.ServerT } = hs
}

这里的结果类型有点笨拙,所以将它与“Aux”模式结合起来也很常见,

object HasServer {
type Aux[T, S] = HasServer[T] { type ServerT = S }
def apply[T](implicit hs: HasServer[T]): Aux[T, hs.ServerT] = hs
}

无论如何,这在其他地方可能会派上用场。

我们可以看到这对 REPL 上的推断类型产生的影响,

scala> implicitly[HasServer[Int :> Unit]]
res0: Servant.HasServer[Servant.:>[Int,Unit]] = ...

scala> HasServer[Int :> Unit]
res1: Servant.HasServer[Servant.:>[Int,Unit]]{type ServerT = Int => String} = ...

此细化将被推断为 val 定义的类型,因此现在您将获得所需的结果,

scala> val foo = HasServer[Int :> Unit]
foo: Servant.HasServer[Servant.:>[Int,Unit]]{type ServerT = Int => String} = ...

scala> implicitly[=:=[Int => String, foo.ServerT]]
res2: =:=[Int => String,foo.ServerT] = <function1>

implicitly的定义有多种方式可以改进以避免这个问题。下面是最直接的引用类型,

def implicitly[T <: AnyRef](implicit t: T): t.type = t

如果literal types启用后我们可以删除 <: AnyRef为所有类型绑定(bind)并定义它,

def implicitly[T](implicit t: T): t.type = t

shapeless 提供了一个 the[T]运算符,其行为与后者通过宏类似。

关于Scalac 无法推断归纳构建的路径相关类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39954029/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com