Skip to content

Instantly share code, notes, and snippets.

@tifletcher
Created November 9, 2017 06:42
Show Gist options
  • Save tifletcher/cf2a0185a7b81b4dc936d8da404f755e to your computer and use it in GitHub Desktop.
Save tifletcher/cf2a0185a7b81b4dc936d8da404f755e to your computer and use it in GitHub Desktop.
Arbitrary type projections as extension methods callable on the <From> type parameterized by the desired <To> type
import java.util.UUID
// given this machinery
trait Translator[-I, +O] {
def translate(i: I): O
}
object TypeProjector {
implicit class Translatable[T](i: T) {
def project[U](implicit translator: Translator[T, U]): U = translator.translate(i)
}
}
// and a set of layer models like this
type DAORep = String
type PretendJson = String
case class FooDAO(
id: DAORep
)
case class FooModel(
id: UUID
)
case class FooDTO(
id: PretendJson
)
// aside: often translation between layers requires some service classes
trait UuidService {
def fromDAORep(s: DAORep): UUID
def toHumanReadable(id: UUID): PretendJson
}
class DefaultUuidService() extends UuidService {
override def fromDAORep(s: DAORep): UUID = UUID.fromString(s)
override def toHumanReadable(id: UUID): PretendJson = s"<< ${id.toString} >>"
}
// define layer translators with service dependencies
// each available translation is defined by an implicit object extending Translator[FromType, ToType]
class DAO2Model(uuidService: UuidService) {
implicit object FooModelFromDAO extends Translator[FooDAO, FooModel] {
override def translate(i: FooDAO) = FooModel(
id = uuidService.fromDAORep(i.id)
)
}
}
// translators may of course have private internal translators, if desired
class Model2DTO(uuidService: UuidService) {
import TypeProjector._
private implicit object UUID2PretendJson extends Translator[UUID, PretendJson] {
override def translate(i: UUID) = uuidService.toHumanReadable(i)
}
implicit object FooDTOFromModel extends Translator[FooModel, FooDTO] {
override def translate(i: FooModel) = FooDTO(
id = i.id.project[PretendJson]
)
}
}
// maybe gather layer translators into a nice unified class
class Translators(uuidService: UuidService) {
val dao = new DAO2Model(uuidService)
val dto = new Model2DTO(uuidService)
}
// At the point of usage:
// 0. import the type projector
// 1. initialize and import the translators
// 2. project at will, with the compiler ensuring you have defined and imported each required translator
import TypeProjector._
val translators = new Translators(new DefaultUuidService)
import translators.dao._
import translators.dto._
val dao = FooDAO(UUID.randomUUID().toString)
dao.project[FooModel].project[FooDTO]
// FooDTO(<< some uuid >>)
dao.project[FooModel].project[String]
// Error:(91, 31) could not find implicit value for parameter translator: A$A917.this.Translator[A$A917.this.FooModel,String]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment