Skip to content

Instantly share code, notes, and snippets.

@blast-hardcheese
Last active July 4, 2016 05:29
Show Gist options
  • Save blast-hardcheese/0d4a65493810c7cc47bbe998f3af28ce to your computer and use it in GitHub Desktop.
Save blast-hardcheese/0d4a65493810c7cc47bbe998f3af28ce to your computer and use it in GitHub Desktop.
Type inference question in Cats
sealed trait ControlPanel[T]
case object ButtonPressed extends ControlPanel[Button]
case object StopEnabled extends ControlPanel[Unit]
case object StopDisabled extends ControlPanel[Unit]
case object CurrentFloor extends ControlPanel[Int]
sealed trait ElevatorControl[T]
case object GetFloor extends ElevatorControl[Int]
case class QueueFloor(x: Int) extends ElevatorControl[Unit]
trait FreeCTest1Interp {
implicit val controlPanelInterp = new NaturalTransformation[ControlPanel, Id] {
def apply[A](fa: ControlPanel[A]): Id[A] = fa match {
case ButtonPressed => Button("Floor 2")
case StopEnabled => println("Stop enabled")
case StopDisabled => println("Stop disabled")
case CurrentFloor => 2
}
}
implicit val elevatorControlInterp = new NaturalTransformation[ElevatorControl, Id] {
def apply[A](fa: ElevatorControl[A]): Id[A] = fa match {
case GetFloor => 2
case QueueFloor(floor) => ()
}
}
}
name := "free-experiments"
scalaVersion := "2.11.8"
libraryDependencies += "org.typelevel" %% "cats" % "0.6.0"
import cats.Id
import cats.arrow.NaturalTransformation
import cats.data.Coproduct
import cats.free.{ Coyoneda, Free }
import cats.implicits._
import scala.language.higherKinds
import Coproduct.{ leftc, rightc }
case class Button(label: String)
trait FreeCSupport {
type FreeC[F[_], A] = Free[({ type CoF[T] = Coyoneda[F,T] })#CoF,A]
def liftFC[F[_], A](value: F[A]): FreeC[F, A] = Free.liftF[({ type CoF[α] = Coyoneda[F, α] })#CoF, A](Coyoneda.lift[F, A](value))
def runFC[F[_], A](prog: FreeC[F, A])(implicit interp: NaturalTransformation[F, Id]): A = {
prog.go { sfsa: Coyoneda[F, FreeC[F,A]] =>
sfsa.transform(interp).run
}
}
implicit def combineNT[F[_], G[_], H[_]](implicit f: NaturalTransformation[F, H], g: NaturalTransformation[G, H]) = new NaturalTransformation[({ type FG[A] = Coproduct[F, G, A] })#FG, H] {
def apply[A](fga: Coproduct[F, G, A]): H[A] = {
fga.fold(f, g)
}
}
implicit def liftXorL[F[_], G[_], T](x: F[T]): F[T] Xor G[T] = Xor.left(x)
implicit def liftXorR[F[_], G[_], T](x: G[T]): F[T] Xor G[T] = Xor.right(x)
implicit def liftCoL[F[_], G[_], T](x: F[T]): Coproduct[F, G, T] = Coproduct(x)
implicit def liftCoR[F[_], G[_], T](x: G[T]): Coproduct[F, G, T] = Coproduct(x)
}
object FreeCTest1 extends App with FreeCSupport with FreeCTest1Interp {
// Works, using FreeC only, no Coproduct
val prog1: FreeC[ControlPanel, Int] = for {
button <- liftFC(ButtonPressed)
_ <- liftFC(StopEnabled)
_ <- liftFC(StopDisabled)
currentFloor <- liftFC(CurrentFloor)
} yield { println(button); currentFloor }
// Works when specifying all types everywhere.
def prog(interp: NaturalTransformation[({ type CE[A] = Coproduct[ControlPanel, ElevatorControl, A] })#CE, Id]): Int = {
implicit def liftCP[T](value: ControlPanel[T]) = liftFC[({ type CE[A] = Coproduct[ControlPanel, ElevatorControl, A] })#CE, T](value)
implicit def liftEC[T](value: ElevatorControl[T]) = liftFC[({ type CE[A] = Coproduct[ControlPanel, ElevatorControl, A] })#CE, T](value)
implicit def lift[F[_], T](x: F[T])(implicit lifter: F[T] => FreeC[({ type CE[A] = Coproduct[ControlPanel, ElevatorControl, A] })#CE, T]) = lifter(x)
val _prog = for {
button <- ButtonPressed
currentFloor2 <- GetFloor
currentFloor <- CurrentFloor
} yield { println(button); currentFloor2 }
runFC[({ type CE[A] = Coproduct[ControlPanel, ElevatorControl, A] })#CE, Int](_prog)(interp)
}
println(prog(combineNT[ControlPanel, ElevatorControl, Id]))
// Simple example of the above
val coValue = Coyoneda.lift[({ type FG[A] = Coproduct[ControlPanel, ElevatorControl, A] })#FG, Button](leftc(ButtonPressed))
val res = coValue.transform(combineNT).run
println(res)
}
@blast-hardcheese
Copy link
Author

blast-hardcheese commented Jul 3, 2016

(This was a compilation error for revision 2)

[error] .../src/main/scala/App.scala:91: type mismatch;
[error]  found   : cats.data.Coproduct[ControlPanel,Nothing,Button]
[error]  required: ?F[?A]
[error] Note that implicit conversions are not applicable because they are ambiguous:
[error]  both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
[error]  and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
[error]  are possible conversion functions from cats.data.Coproduct[ControlPanel,Nothing,Button] to ?F[?A]
[error]       button <- liftFC(leftc(ButtonPressed))
[error]              ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Jul 3, 2016 3:01:18 PM

@blast-hardcheese
Copy link
Author

(This is a compilation error for revision 4)

[error] .../src/main/scala/App.scala:115: no type parameters for method runFC: (prog: FreeCTest1.FreeC[F,A])(implicit interp: cats.arrow.NaturalTransformation[F,cats.Id])A exist so that it can be applied to arguments (cats.free.Free[[T]cats.free.Coyoneda[[A]cats.data.Coproduct[ControlPanel,ElevatorControl,A],T],Int])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : cats.free.Free[[T]cats.free.Coyoneda[[A]cats.data.Coproduct[ControlPanel,ElevatorControl,A],T],Int]
[error]  required: FreeCTest1.FreeC[?F,?A]
[error]     (which expands to)  cats.free.Free[[T]cats.free.Coyoneda[?F,T],?A]
[error]     runFC(_prog)(interp)
[error]     ^
[error] .../src/main/scala/App.scala:115: type mismatch;
[error]  found   : cats.free.Free[[T]cats.free.Coyoneda[[A(in type CE)]cats.data.Coproduct[ControlPanel,ElevatorControl,A(in type CE)],T],Int]
[error]  required: FreeCTest1.FreeC[F,A(in method runFC)]
[error]     (which expands to)  cats.free.Free[[T]cats.free.Coyoneda[F,T],A(in method runFC)]
[error]     runFC(_prog)(interp)
[error]           ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 1 s, completed Jul 3, 2016 3:38:40 PM

@blast-hardcheese
Copy link
Author

Turns out the issue was SI-2712, and https://github.com/milessabin/si2712fix-plugin made short work of it.

Experiments are continued at https://github.com/blast-hardcheese/scala-free-playground.

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