0

I have a function with signature currently like so:

  def runInSystem[J](job: J)
  (implicit ev: Job[J] {type M <: SysJobMetaData},
   asys: ActorSystem, jobContextService: JobContextService
  ): IO[Nothing, RunResult] = ...

When I call this function like this:

  case cmd: OneShot =>
    memStorage(cmd.id) = JobWithResult(cmd, runIO(runInSystem(cmd)(jobOneShot, asys, jobContextService)))
    cmd

I get an error:

[error] /home/brandon/workspace/CCRS/web-server/src/main/scala/org/xsede/jobrunner/server/InMemService.scala:65: type mismatch;
[error]  found   : org.xsede.jobrunner.model.ModelApi.OneShot.jobOneShot.type (with underlying type org.xsede.jobrunner.model.ModelApi.Job[org.xsede.jobrunner.model.ModelApi.OneShot])
[error]  required: org.xsede.jobrunner.model.ModelApi.Job[org.xsede.jobrunner.model.ModelApi.OneShot]{type M <: org.xsede.jobrunner.model.ModelApi.SysJobMetaData}
[error]         memStorage(cmd.id) = JobWithResult(cmd, runIO(runInSystem(cmd)(jobOneShot, asys, jobContextService)))

This error doesn't make a lot of sense to me, so I wonder if I've hit a limitation in Scala's type system. The reason is that OneShot and its typeclass instance are defined as:

  final case class OneShot(id: JobId, cmd: String, meta: SysJobMetaData) {
    type M = SysJobMetaData
  }

implicit val jobOneShot: Job[OneShot] = new Job[OneShot] {
  def id(jb: OneShot): JobId = jb.id
  def cmd(jb: OneShot): String = jb.cmd
  override type M = SysJobMetaData
  def meta(jb: OneShot): M = jb.meta
}

So by definition,the constraint type M <: SysJobMetaData should be satisfied, I would think.

bbarker
  • 11,636
  • 9
  • 38
  • 62

1 Answers1

1

You should declare type of implicit val correctly.

Try

implicit val jobOneShot: Job[OneShot] { type M = SysJobMetaData } = new Job[OneShot] {
  def id(jb: OneShot): JobId = jb.id
  def cmd(jb: OneShot): String = jb.cmd
  override type M = SysJobMetaData
  def meta(jb: OneShot): M = jb.meta
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Interesting, this definitely gets me past that particular error. I'm surprised it has to be defined in two places like this but I guess public members aren't normally part of the type signature, even implicitly. – bbarker Aug 23 '18 at 21:10
  • 1
    You should read about Aux-pattern http://gigiigig.github.io/posts/2015/09/13/aux-pattern.html http://www.vlachjosef.com/aux-pattern-evolution/ https://stackoverflow.com/questions/43900674/understanding-the-aux-pattern-in-scala-type-system – Dmytro Mitin Aug 23 '18 at 21:48
  • 1
    Normally `Job[OneShot] { type M = SysJobMetaData }` is written as `Job.Aux[OneShot, SysJobMetaData]`. – Dmytro Mitin Aug 23 '18 at 21:49
  • 1
    Types `Job[OneShot] { type M = SysJobMetaData }` and `Job[OneShot]` are different. The former is a subtype of the latter. Also `Job[OneShot]` is existential type `Job.Aux[OneShot, _]`. https://dzone.com/articles/existential-types-in-scala – Dmytro Mitin Aug 23 '18 at 21:53
  • Thanks Dmytro! I recall coming across `Aux` over a year ago - at the time it seemed horribly complicated but that is a great article and I can now appreciate it being useful (though I probably don't yet appreciate all the ways it is useful, but the use with `Monoid` in the first linked article is another example). – bbarker Aug 24 '18 at 13:45