Created
November 9, 2017 06:42
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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