Scala - 将实现常见TypeClass的单​​独类型视为相同

时间:2015-05-01 12:30:33

标签: scala types finagle argonaut

我有两个案例类,我们称之为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)

有什么想法吗?谢谢!

1 个答案:

答案 0 :(得分:1)

如果你有这些案例类:

r

这些情况:

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

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

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")

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

然后,您可以编写一个方法来转换像这样的服务,而不是使用隐式转换来更改返回类型:

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>

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