gpt4 book ai didi

scala - 隐式转换不适用于类型安全的构建器模式

转载 作者:行者123 更新时间:2023-12-01 03:56:56 25 4
gpt4 key购买 nike

我正在使用 Scala 类型安全构建器模式来处理简单的休息请求。这作为一个流利的 api 非常有用。

sealed abstract class Method(name: String)

case object GET extends Method("GET")
case object POST extends Method("POST")

abstract class TRUE
abstract class FALSE

case class Builder[HasMethod, HasUri](
method: Option[Method],
uri: Option[String]) {

def withMethod(method: Method): Builder[TRUE, HasUri] = copy(method = Some(method))
def withUri(uri: String): Builder[HasMethod, TRUE] = copy(uri = Some(uri))
}

implicit val init: Builder[FALSE, FALSE] = Builder[FALSE, FALSE](None, None)

//Fluent examples
val b1: Builder[TRUE, FALSE] = init.withMethod(GET)
val b2: Builder[TRUE, TRUE] = init.withMethod(GET).withUri("bar")

我想通过允许 Method 使它更像 DSL实例被转换为 Builder例如,但是当我添加尝试隐式包含 init builder 隐式转换和类型参数的组合使编译器感到困惑。
implicit def toMethod[HasUri](m: Method)
(implicit builder: Builder[_, HasUri]): Builder[TRUE, HasUri] = builder.withMethod(m)

// ** ERROR **: could not find implicit value for parameter builder:
// Builder[_, HasUri]
val b3: Builder[TRUE, TRUE] = GET withUri "foo"

// However the implicit parameter is discovered fine when function is called directly
val b4: Builder[TRUE, FALSE] = toMethod(GET)
val b5: Builder[TRUE, TRUE] = toMethod(GET) withUri "foo"

除 b3 外,所有行都编译。当 toMethod函数被显式调用,builder 参数可以隐式找到。此外,如果我删除通用参数(和类型安全),代码将按预期工作。

这是 scala 隐式转换的限制吗?还是我错过了实现这一目标的正确语法?

我想隐式地发现初始构建器实例,以使用户能够为他们自己的初始构建器提供一些构建器字段的默认值。

更新

我遗漏了一些代码以使示例保持简单,因为它只是我要修复的隐式转换。

类型安全的构建器模式在这里得到了很好的概述: http://blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html

之后您只能调用 build方法一次 Builder有一个方法和一个uri。

我想发现builder作为隐式参数的原因是为了支持DSL中的以下情况。
url("http://api.service.org/person") apply { implicit b =>
GET assert(Ok and ValidJson)
GET / "john.doe" assert(NotFound)
POST body johnDoeData assert(Ok)
GET / "john.doe" assert(Ok and bodyIs(johnDoeData))
}

在这些情况下
  • url 使用指定的 uri 创建了一个新构建器
  • 然后在闭包中将其重用为implicit b =>
  • assert方法仅在 uri 和方法已指定
  • 时可用
  • /附加到当前 uri,这仅在构建器指定了 uri 时才可用。

  • 指定方法和 uri 的另一个示例
    GET url("http://api.service.org/secure/person") apply { implicit b =>
    auth basic("harry", "password") assert(Ok and ValidJson)
    auth basic("sally", "password") assert(PermissionDenied)
    }

    最佳答案

    我感觉您的隐式解析问题并非来自 Scala 类型系统中的任何限制,而是取决于您在此处指定的存在类型:

    implicit def toMethod[HasUri](m: Method)
    (implicit builder: Builder[_, HasUri]): Builder[TRUE, HasUri] = builder.withMethod(m)

    如果我没记错的话,在这种情况下,存在类型被视为 Nothing。 Nothing 是每个可能的 Scala 类的子类,因此您的方法实际上变为:
    implicit def toMethod[HasUri](m: Method)
    (implicit builder: Builder[Nothing, HasUri]): Builder[TRUE, HasUri] = builder.withMethod(m)

    然后,Scala 将在当前范围内查找 Builder[Nothing,HasUri] 的子类以提供给您的方法,并且除了 Builder[Nothing,HasUri] 之外没有可以匹配所需类型的类,因为您的构建器类是不变的,即 Builder[A,B]<:<Builder[C,D]如果 A=:=C & B=:=D
    因此,您有两种选择:
  • 给签名加一个参数,toMethod[HasUri]变成toMethod[A,HasUri]
  • 利用 Scala 正确实现类型差异

  • 由于您要强制您的 Builder[A,HasUri] 是 Builder[Nothing,HasUri] 的子类,并且
    Nothing <:< A for any A

    你想强制执行 Builder[A,HasUri] <:< Builder[B,HasUri]如果 B<:<A即 Builder 在其第一个类型参数中是逆变的。您可以通过在类型前面放置一个 - 符号来强制控制:
    Builder[-HasMethod, HasUri]在 HasMethod 中是协变的,在 HasUri 中是不变的

    结论

    类型系统很强大,但即使是简单的任务也不一定要使用复杂的模式:
  • HasUri 不是从 m 推断出来的,因为它是方法 toMethod
  • 的类型参数
  • 没有推断 HasMethod,因为您使用 _
  • 删除它

    如果参数不包含在您的解析中,那么使用带有两个泛型参数的隐式参数有什么意义?我只想写:
    case class DefaultBuilder(m:Method) extends Builder[True,HasUri]

    当你最终遇到这种情况时,正如有人已经说过的那样,那是因为你的设计对于问题是错误的。你能解释为什么构建器必须隐含在 toMethod 中吗?
    implicit def toMethod(m:Method) = DefaultBuilder(m)

    关于scala - 隐式转换不适用于类型安全的构建器模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16512374/

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