7mind / izumi

Productivity-oriented collection of lightweight fancy stuff for Scala toolchain
https://izumi.7mind.io
BSD 2-Clause "Simplified" License
613 stars 66 forks source link

Type parametrized version of Services #71

Closed dnaumenko closed 6 years ago

dnaumenko commented 6 years ago

Currently, the codegen for Service domain produces a service when each method accepts an InputType and returns an OutputType.

I propose to make it more general by introducing a higher-kinded type F and make all service methods return not OutputType, but F[OutputType] instead.

An example for generated code:

trait CampaignService[F[_]] extends runtime.model.IDLService {
  def _info: runtime.model.IDLTypeInfo = {
    runtime.model.IDLTypeInfo(common.TypeId.ServiceId(Seq(), "CampaignService"), izumi.idealingua.domains.Common)
  }
  import CampaignService._
  override type InputType = CampaignService.InCampaignService
  override type OutputType = CampaignService.OutCampaignService
  override def inputTag: scala.reflect.ClassTag[CampaignService.InCampaignService] = scala.reflect.classTag[CampaignService.InCampaignService]
  override def outputTag: scala.reflect.ClassTag[CampaignService.OutCampaignService] = scala.reflect.classTag[CampaignService.OutCampaignService]
  def create(input: CampaignService.InCreate): F[CampaignService.OutCreate]
  def update(input: CampaignService.InUpdate): F[CampaignService.OutUpdate]
  def getById(input: CampaignService.InGetById): F[CampaignService.OutGetById]
  def getAll(input: CampaignService.InGetAll): F[CampaignService.OutGetAll]
  def deleteById(input: CampaignService.InDeleteById): F[CampaignService.OutDeleteById]
}

We could probably generate a default version which will use an Identity monad for convenience, so the current behavior will look like:

trait CampaignServiceSync extends CampaignService[Id] {
  def create(input: CampaignService.InCreate): Id[CampaignService.OutCreate] = Id(createImpl(input)
  def createImpl(input: CampaignService.InCreate): CampaignService.OutCreate
}

Motivation behind this is that many useful FP libraries by default expects to work with some general kind, so they don't return simple an A, but return instead a Either[A, Error].

neko-kai commented 6 years ago

We could probably generate a default version which will use an Identity monad for convenience, so the current behavior will look like:

You can use Id alias to recover original definition without needing a wrapper.

type Id[A] = A

trait Noop[F[_]] {
  def noop: F[Unit]
}

class NoopImpl extends Noop[Id] {
  def noop: Unit = ()
}