2

我有两个案例类,我们称它们case class User为 & case class Ticket。这两个案例类都实现了成为相同成员所需的操作TypeClass,在本例中是 Argonaut 的EncodeJson

是否可以在不创建它们都扩展的空标记类型的情况下将这两种不同的类型视为相同?

trait Marker
case class User extends Marker
case class Ticket extends Marker

为了具体化,我们有两个单独的函数返回这些案例类:

case class GetUser(userId: Long) extends Service[Doesn't Matter, User] {
  def apply(req: Doesn't Matter): Future[User] = {
    magical and awesome business logic 
    return Future[User]
   }
}

case class GetTicket(ticketId: Long) extends Service[Doesn't Matter, Ticket] {
  def apply(req: Doesn't Matter): Future[Ticket] = {
    magical and awesome business logic 
    return Future[Ticket]
   }
}

我想组合这两个服务,以便它们返回相同的类型,在这种情况下argonaut.Json,但编译器对隐式转换的响应是“LOLNO”

implicit def anyToJson[A](a: A)(implicit e: EncodeJson[A]): Json = e(a)

有任何想法吗?谢谢!

4

1 回答 1

1

如果您有这些案例类:

case class User(id: Long, name: String)
case class Ticket(id: Long)

而这些实例:

import argonaut._, Argonaut._

implicit val encodeUser: EncodeJson[User] =
  jencode2L((u: User) => (u.id, u.name))("id", "name")

implicit val encodeTicket: EncodeJson[Ticket] = jencode1L((_: Ticket).id)("id")

以及以下服务(我使用 Finagle 的代表):

import com.twitter.finagle.Service
import com.twitter.util.Future

case class GetUser(id: Long) extends Service[String, User] {
  def apply(req: String): Future[User] = Future(User(id, req))
}

case class GetTicket(id: Long) extends Service[String, Ticket] {
  def apply(req: String): Future[Ticket] = Future(Ticket(id))
}

(这些都是废话,但这并不重要。)

然后,您可以编写一个方法来转换服务,而不是使用隐式转换来更改返回类型,如下所示:

def toJsonService[I, O: EncodeJson](s: Service[I, O]): Service[I, Json] =
  new Service[I, Json] {
    def apply(req: I) = s(req).map(_.asJson)
  }

然后将其应用于您的其他服务:

scala> toJsonService(GetTicket(100))
res7: com.twitter.finagle.Service[String,argonaut.Json] = <function1>

您还可以将此功能作为服务或过滤器提供,或者如果您不介意返回功能,则可以直接使用GetTicket(100).andThen(_.map(_.asJson))

关键思想是引入隐式转换应该是绝对的最后手段,而您应该直接使用类型类实例。

于 2015-05-01T17:32:17.480 回答