Skip to content

Instantly share code, notes, and snippets.

@ubourdon
Last active August 29, 2015 14:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ubourdon/0b17f0b43a68c89b74e1 to your computer and use it in GitHub Desktop.
Save ubourdon/0b17f0b43a68c89b74e1 to your computer and use it in GitHub Desktop.
Why have this compilation error & how to fix it
/** In fact I try to write parametric Eventsourcing apply function
* And which allow me to have a parametric function for CommandHandler[D] too.
*/
trait ApplyTo {
def applyTo[E, S](startingState: S)(events: List[E])(implicit de: DomainEvent.Aux[E, S]): S = {
events.foldLeft(startingState) { (currentState, event) => de.apply(currentState, event) }
}
}
@implicitNotFound("No member of type class DomainEvent found for type ${A}")
trait DomainEvent[A] {
type State
def apply(currentState: State, event: A): State
}
object DomainEvent {
//def apply[T: DomainEvent]: DomainEvent[T] = implicitly[DomainEvent[T]]
type Aux[A, S] = DomainEvent[A] { type State = S }
implicit object TicketEventTypeclass extends DomainEvent[TicketEvent] {
type State = TicketModel.State
override def apply(currentState: State, event: TicketEvent): State = ticket.models.apply(currentState, event)
}
}
trait TicketCommandHandler extends ApplyTo {
type DomainEither[DomainError, State] = EitherT[Future, DomainError, State]
type NextState = TicketModel.Ticket
type TicketEither[A] = DomainEither[TicketError, A] // <: TicketModel.State
def CommandHandler(command: TicketCommand)(implicit eventReader: EventStream.Id => Future[List[TicketEvent]] = Eventstore.read[TicketEvent],
eventWriter: (EventStream.Id, List[TicketEvent]) => Future[WriteEventsCompleted] = Eventstore.write[TicketEvent]): TicketEither[TicketModel.State] = {
val streamId = ticketEventStreamId(command)
eventReader(streamId).map { events => applyTo(TicketModel.EmptyTicket: TicketModel.State)(events) }
.map { state => (state, decide(state, command)) }
.flatMap { case (state, decideResult) =>
decideResult.traverse { events =>
eventWriter(streamId, events).map { _ => applyTo(state)(events) }
}
} |> fEitherT
}
private def ticketEventStreamId(command: TicketCommand): EventStream.Id = EventStream.Id(s"ticket-${command.aggregateUid.safeValue}")
}
object TicketCommandHandler extends TicketCommandHandler
object TicketModel {
sealed trait State
case object EmptyTicket extends State
case class Ticket(uid: SafeTicketUid,
agencyUid: SafeAgencyUid,
user: TicketUser,
operatorUid: SafeOperatorUid,
infos: TicketInfos,
openedDate: DateTime,
journal: List[OtherTicketEvent],
isClosed: Boolean = false) extends State
}
trait GenericTrait {
def generic[A: TypeClass](x: TypeClass[A]#Result)(y: List[A]): TypeClass[A]#Result =
y.foldLeft(x) { (a, b) => TypeClass[A].aMethod(a, b) }
}
import scala.annotation.implicitNotFound
@implicitNotFound("No member of type class TypeClass found for type ${A}")
trait TypeClass[A] {
type Result
def aMethod(x: Result, y: A): Result
}
object TypeClass {
def apply[T: TypeClass]: TypeClass[T] = implicitly[TypeClass[T]]
implicit object InstanceTypeclass extends TypeClass[AnInstance] {
type Result = AResult
override def aMethod(x: Result, y: AnInstance): Result = ???
}
}
case class AnInstance()
case class AResult()
/**
* [error] /../TypeClass.scala:3: type mismatch;
[error] found : a.type (with underlying type domain.service.TypeClass[A]#Result)
[error] required: _5.Result where val _5: domain.service.TypeClass[A]
[error] y.foldLeft(x) { (a, b) => TypeClass[A].aMethod(a, b) }
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
* /
@Sciss
Copy link

Sciss commented Aug 16, 2015

TypeClass[A]#Result is a type projection whereas the x: Result in aMethod is a dependent type. The latter is much more specific and constrained. You could make generic compile by relaxing the type as follows:

def aMethod(x: TypeClass[A]#Result, y: A): Result

But that's hardly what you want because your aMethod cannot do anything useful with x. Instead you probably want to change generic to use the dependent type:

trait GenericTrait {
  def generic[A,Res](x:Res)(y: List[A])(implicit tc: TypeClass[A] { type Result = Res }): Res =
    y.foldLeft(x) { (a, b) => tc.aMethod(a, b) }
}

@mandubian
Copy link

I would also advise using an Aux type to force scalac to resolve all types as following:

trait GenericTrait {
  def generic[A, R](x: R)(y: List[A])(implicit tc: TypeClass.Aux[A, R]): R =
    y.foldLeft(x) { (a, b) => tc.aMethod(a, b) }
}

import scala.annotation.implicitNotFound

@implicitNotFound("No member of type class TypeClass found for type ${A}")
trait TypeClass[A] {
  type Result
  def aMethod(x: Result, y: A): Result
}

object TypeClass {
  type Aux[A, R] = TypeClass[A] { type Result = R }

  implicit object InstanceTypeclass extends TypeClass[AnInstance] {
    type Result = AResult
    override def aMethod(x: Result, y: AnInstance): Result = ???
  }
}

case class AnInstance()

case class AResult()

@ubourdon
Copy link
Author

Thx both of you for your answer.
2 points :

  1. I don't understand what do TypeClass.Aux
  2. Because I use dependant type, I can't use this notation
    def generic[A: TypeClass] instead of implicit notation ?

@mandubian
Copy link

  1. TypeClass.Aux reifies the type Result for scalac because you will soon encounter cases where scalac won't be able to unify Result between 2 instances of TypeClass... scalac has limitations clearly in this domain so you have to help it by forcing it to identify the type dependent... Shapeless is all based on this idea...

  2. if you use the type Aux, no you won't be able to use this notation and actually I don't use it much because implicitly is ugly when you have to use it several times in the same function

@mandubian
Copy link

I have to leave, i'll answer later...
that's the last part of the question... your code compiles but how do you use it... your type-dependent is a bit weird and you need to show some usage code to see what is the right way to write it ;)

@ubourdon
Copy link
Author

No it's ok this compile. But I lost some message i post in gist.
Thx for your help.

@ahoy-jon
Copy link

Should events: List[E] be an HList instead ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment