Scala:类型推断遗漏了一些东西
Scala: Type inference missing something
我想在以下情况下实现某种类型安全。
基本上,我有不同类型的请求存储在数据库中,它们的类型用一些字符串代码标识。出于商业原因,此代码 不 匹配 class 名称。
每种类型的请求都包含某种有效载荷,有效载荷的类型直接取决于请求的类型。
这是我目前所取得成就的简化版本:
trait Request[Payload] {
def metadata: String // Not relevant
def payload: Payload
}
case class RequestWithString(override val metadata: String, override val payload: String) extends Request[String]
case class AnotherTypeOfRequestWithString(override val metadata: String, override val payload: String) extends Request[String]
case class RequestWithInt(override val metadata: String, override val payload: Int) extends Request[Int]
object Request {
def apply(code: String)(metadata: String, payload: Any): Request[_] = code match {
case "S" => RequestWithString(metadata, payload.asInstanceOf[String])
case "S2" => AnotherTypeOfRequestWithString(metadata, payload.asInstanceOf[String])
case "I" => RequestWithInt(metadata, payload.asInstanceOf[Int])
}
}
这并不令人满意,因为我希望 Scala 推断有效负载的类型以避免强制转换,以及返回值的(参数化)类型。
我正在寻找的是这样的东西:
object Request {
def apply[P, R <: Request[P]](code: String)(metadata: String, payload: P): R = code match {
case "S" => RequestWithString(metadata, payload)
case "S2" => AnotherTypeOfRequestWithString(metadata, payload)
case "I" => RequestWithInt(metadata, payload)
}
}
但这似乎不起作用,我无法摆脱一些类型不匹配的错误:
found : P
required: String
case "S" => RequestWithString(metadata, payload)
^
在这种情况下,Scala 不应该推断 P 是 String 吗?我错过了什么?
我可以看到一些重大改进。让我们从头开始,首先我们从不在抽象成员的特征中使用 val
,看 here.
trait Request[Payload] {
def metadata: String // Not relevant
def payload: Payload
}
现在让我们看这里:
object Request {
def apply[P, R <: Request[P]](code: String)(metadata: String, payload: P): R = code match {
case "S" => RequestWithString(metadata, payload)
case "I" => RequestWithInt(metadata, payload)
}
}
你误解了 P <: Request[P]
的意思,这是一个 f-bounded 类型的多态参数,用于所谓的类型细化,例如 return 调用后最具体的包装器类型在类型上限上定义的方法,例如在 Request
return RequestWithInt
上有方法,而不仅仅是 Request
。就您而言,我认为您无论如何都没有选择正确的方法。
您可以将它用于将 RequestWithString
和 RequestWithInt
实例作为参数或类似参数的方法。
现在,对于您的情况,您应该做的是将 ADT 用于您的请求类型。类似于 RequestEncoder
.
trait RequestEncoder[T] {
def encode(obj: T): String
def decode(obj: String): T
}
object RequestEncoder {
implicit val intEncoder = new RequestEncoder[Int] {
def encode(obj: Int): String = obj.toString
def decode(source: String): Int = source.toInt
}
}
trait Request[Payload : RequestEncoder] {
def metadata: String // Not relevant
def payload(source: Payload): String = implicitly[RequestEncoder[Payload]].encode(source)
}
将匹配的决策逻辑移至类型类:
// this typeclass holds the logic for creating a `Request` for
// a particular payload
sealed abstract class RequestPayloadType[T](val create: (String, T) => Request[T])
object RequestPayloadType {
implicit object StringPayloadType extends RequestPayloadType[String] (RequestWithString.apply)
implicit object IntPayloadType extends RequestPayloadType[Int] (RequestWithInt.apply)
}
object Request {
def apply[P:RequestPayloadType](metadata: String, payload: P): Request[P] =
implicitly[RequestPayloadType[P]].create(metadata, payload)
}
scala 中的常见模式:将需要特定类型知识的代码移动到具有该知识的编译单元。
请记住,不有单独的请求类,只有一个参数化的请求可能更干净:
case class Request [P:RequestPayloadType](metadata: String, payload: P) {
// delegate any code that needs to know the type to `implicitly[RequestPayloadType[T]]...`
}
sealed trait RequestPayloadType[T] {
// specify here code that needs to know the actual type, i.e:
// def encode (value: T): String // abstract
// def decode (value: String): T // abstract
}
object RequestPayloadType {
implicit object StringPayloadType extends RequestPayloadType[String] {
// implement here any `String` specific code, .i.e:
// def encode (s: String) = s
// ...
}
implicit object IntPayloadType extends RequestPayloadType[Int] {
// implement here any `Int` specific code, .i.e:
// def encode (i: Int) = i.toString
// ...
}
}
我想在以下情况下实现某种类型安全。
基本上,我有不同类型的请求存储在数据库中,它们的类型用一些字符串代码标识。出于商业原因,此代码 不 匹配 class 名称。
每种类型的请求都包含某种有效载荷,有效载荷的类型直接取决于请求的类型。
这是我目前所取得成就的简化版本:
trait Request[Payload] {
def metadata: String // Not relevant
def payload: Payload
}
case class RequestWithString(override val metadata: String, override val payload: String) extends Request[String]
case class AnotherTypeOfRequestWithString(override val metadata: String, override val payload: String) extends Request[String]
case class RequestWithInt(override val metadata: String, override val payload: Int) extends Request[Int]
object Request {
def apply(code: String)(metadata: String, payload: Any): Request[_] = code match {
case "S" => RequestWithString(metadata, payload.asInstanceOf[String])
case "S2" => AnotherTypeOfRequestWithString(metadata, payload.asInstanceOf[String])
case "I" => RequestWithInt(metadata, payload.asInstanceOf[Int])
}
}
这并不令人满意,因为我希望 Scala 推断有效负载的类型以避免强制转换,以及返回值的(参数化)类型。
我正在寻找的是这样的东西:
object Request {
def apply[P, R <: Request[P]](code: String)(metadata: String, payload: P): R = code match {
case "S" => RequestWithString(metadata, payload)
case "S2" => AnotherTypeOfRequestWithString(metadata, payload)
case "I" => RequestWithInt(metadata, payload)
}
}
但这似乎不起作用,我无法摆脱一些类型不匹配的错误:
found : P
required: String
case "S" => RequestWithString(metadata, payload)
^
在这种情况下,Scala 不应该推断 P 是 String 吗?我错过了什么?
我可以看到一些重大改进。让我们从头开始,首先我们从不在抽象成员的特征中使用 val
,看 here.
trait Request[Payload] {
def metadata: String // Not relevant
def payload: Payload
}
现在让我们看这里:
object Request {
def apply[P, R <: Request[P]](code: String)(metadata: String, payload: P): R = code match {
case "S" => RequestWithString(metadata, payload)
case "I" => RequestWithInt(metadata, payload)
}
}
你误解了 P <: Request[P]
的意思,这是一个 f-bounded 类型的多态参数,用于所谓的类型细化,例如 return 调用后最具体的包装器类型在类型上限上定义的方法,例如在 Request
return RequestWithInt
上有方法,而不仅仅是 Request
。就您而言,我认为您无论如何都没有选择正确的方法。
您可以将它用于将 RequestWithString
和 RequestWithInt
实例作为参数或类似参数的方法。
现在,对于您的情况,您应该做的是将 ADT 用于您的请求类型。类似于 RequestEncoder
.
trait RequestEncoder[T] {
def encode(obj: T): String
def decode(obj: String): T
}
object RequestEncoder {
implicit val intEncoder = new RequestEncoder[Int] {
def encode(obj: Int): String = obj.toString
def decode(source: String): Int = source.toInt
}
}
trait Request[Payload : RequestEncoder] {
def metadata: String // Not relevant
def payload(source: Payload): String = implicitly[RequestEncoder[Payload]].encode(source)
}
将匹配的决策逻辑移至类型类:
// this typeclass holds the logic for creating a `Request` for
// a particular payload
sealed abstract class RequestPayloadType[T](val create: (String, T) => Request[T])
object RequestPayloadType {
implicit object StringPayloadType extends RequestPayloadType[String] (RequestWithString.apply)
implicit object IntPayloadType extends RequestPayloadType[Int] (RequestWithInt.apply)
}
object Request {
def apply[P:RequestPayloadType](metadata: String, payload: P): Request[P] =
implicitly[RequestPayloadType[P]].create(metadata, payload)
}
scala 中的常见模式:将需要特定类型知识的代码移动到具有该知识的编译单元。
请记住,不有单独的请求类,只有一个参数化的请求可能更干净:
case class Request [P:RequestPayloadType](metadata: String, payload: P) {
// delegate any code that needs to know the type to `implicitly[RequestPayloadType[T]]...`
}
sealed trait RequestPayloadType[T] {
// specify here code that needs to know the actual type, i.e:
// def encode (value: T): String // abstract
// def decode (value: String): T // abstract
}
object RequestPayloadType {
implicit object StringPayloadType extends RequestPayloadType[String] {
// implement here any `String` specific code, .i.e:
// def encode (s: String) = s
// ...
}
implicit object IntPayloadType extends RequestPayloadType[Int] {
// implement here any `Int` specific code, .i.e:
// def encode (i: Int) = i.toString
// ...
}
}