ProjectSeptemberInc / freek

Freek, a freaky simple Free to combine your DSL seamlessly
Other
198 stars 19 forks source link

Mixing up order when declaring Interpreter w/ type crashes the compiler #26

Open lloydmeta opened 7 years ago

lloydmeta commented 7 years ago

Suppose I've got a basic DSL defined and have a subprogram type using a couple of them.

import freek._
import cats.{Id, ~>}

object KVStore {
  sealed trait DSL[A]

  final case class Put[A](key: String, v: A) extends DSL[Unit]
  final case class Get[A](key: String)       extends DSL[Option[A]]

  object Interpreter extends (DSL ~> Id) {
    def apply[A](fa: DSL[A]): Id[A] = ???
  }
}

object WebIO {
  sealed trait DSL[A]

  final case class Fetch(key: String) extends DSL[String]

  object Interpreter extends (DSL ~> Id) {
    def apply[A](fa: DSL[A]): Id[A] = ???
  }
}

object FileIO {
  sealed trait DSL[A]

  final case class Lift[A](value: A) extends DSL[A]

  // Interpreter for Lifting.DSL is simple. Just use Applicative.pure
  object Interpreter extends (DSL ~> Id) {
    def apply[A](fa: DSL[A]): Id[A] = ???
  }
}

// The subprogram
object KVStoreWithWeb {
  type PRG = KVStore.DSL :|: WebIO.DSL :|: NilDSL
}

Later on, I define another program in which I use the aforementioned subprogram in addition to a yet-unused DSL (FileIO.DSL). I'd like to have a type for my App level `Interpreter, since I might use it in other parts of the app.

// Main program that uses subprogram
object AnotherProgram {

  type PRG = FileIO.DSL :|: KVStoreWithWeb.PRG
  val PRG = DSL.Make[PRG]

  type Interp[M[_]] = Interpreter[PRG.Cop, M]

}

If I try to instantiate an Interp[Id] with the order mixed up, the compiler blows up with an interesting error.

val interpreter: Interp[Id] = KVStore.Interpreter :&: WebIO.Interpreter :&: FileIO.Interpreter
[error]   last tree to typer: TypeTree
[error]        tree position: line 55 of /Users/Lloyd/Documents/scala_dev/freek-test/test/freeky/App.scala
[error]             tree tpe: <error>
[error]               symbol: <none>
[error]    symbol definition: <none> (a NoSymbol)
[error]       symbol package: <none>
[error]        symbol owners: 
[error]            call site: object App in package freeky in package freeky
[error] 
[error] == Source file context for tree position ==
[error] 
[error]     52   type Interp[M[_]] = Interpreter[PRG.Cop, M]
[error]     53 
[error]     54   val interpreter: Interp[Id] = KVStore.Interpreter :&: WebIO.Interpreter :&: Lifting.Interpreter
[error]     55 
[error]     56 }

If I fix the ordering to match the DSL type, everything works fine:

val interpreter: Interp[Id] = FileIO.Interpreter :&: KVStore.Interpreter :&: WebIO.Interpreter
mandubian commented 7 years ago

Interesting bug :D
scalac explosion... I'll try to study that one day! Thanks