跟随Free Monads讲座实现解释器时遭遇编译错误求助
Hey there, let's walk through fixing this issue step by step. I looked at your code and spotted a couple of things causing the compile error and other potential issues:
1. Fix HTML Entity Symbols First
Your code uses HTML entities like <-, ~>, and => instead of actual Scala syntax (<-, ~>, =>). These symbols break the compiler's ability to parse code correctly, leading to misleading type errors like the one you're seeing.
Replace all those entities with proper Scala operators:
<-→<-~>→~>=>→=>
For example, your updatePost for-comprehension should look like this:
def updatePost(propertyId: Int, post: String): Script[Property] = { for { property <- fetchById(propertyId) p1 = property.copy(post = post) _ <- log("Updating") _ <- save(p1) } yield p1 }
2. Add a Functor Instance for AppAction
Free.liftF (used in your lift method) requires a Functor instance for AppAction, and foldMap also depends on this. Without it, the compiler will throw additional errors. You can either implement this manually or use Cats' automatic derivation (recommended for simplicity).
Option A: Automatic Derivation (with Cats Macros)
First, ensure you have the cats-macros dependency in your build. Then add the derivation to your code:
import cats.derive._ sealed trait AppAction[A] { def lift: Script[A] = Free.liftF(this) } // Add this implicit instance implicit val appActionFunctor: Functor[AppAction] = semiauto.functor[AppAction]
Option B: Manual Implementation
If you don't want to use macros, you can write the Functor manually (though this gets tedious as you add more actions):
implicit val appActionFunctor: Functor[AppAction] = new Functor[AppAction] { override def map[A, B](fa: AppAction[A])(f: A => B): AppAction[B] = fa match { case FetchById(id) => FetchById(id).asInstanceOf[AppAction[B]] case Save(p) => Save(p).asInstanceOf[AppAction[B]] case Log(msg) => Log(msg).asInstanceOf[AppAction[B]] } }
3. Verify the Interpreter's Return Types
The Id type in Cats is just a type alias for the identity type (type Id[A] = A), so returning Property(1, "2000") directly should be valid for Id[Property]. Once you fix the symbol issues and add the Functor instance, this type error should resolve itself.
If you still see issues, you can explicitly type the return value in the match case to help the compiler:
case FetchById(_) => Property(1, "2000"): Id[Property]
Full Fixed Code
Here's the complete corrected code with all fixes applied:
package object example { import cats._ import cats.data._ import cats.free._ import cats.implicits._ import cats.derive._ case class Property(Id: Int, post: String) type Script[A] = Free[AppAction, A] sealed trait AppAction[A] { def lift: Script[A] = Free.liftF(this) } implicit val appActionFunctor: Functor[AppAction] = semiauto.functor[AppAction] case class FetchById(propertyId: Int) extends AppAction[Property] case class Save(property: Property) extends AppAction[Unit] case class Log(message: String) extends AppAction[Unit] def fetchById(id: Int): Script[Property] = FetchById(id).lift def save(property: Property): Script[Unit] = Save(property).lift def log(message: String): Script[Unit] = Log(message).lift def updatePost(propertyId: Int, post: String): Script[Property] = { for { property <- fetchById(propertyId) p1 = property.copy(post = post) _ <- log("Updating") _ <- save(p1) } yield p1 } object TestInterp extends (AppAction ~> Id) { def apply[A](fa: AppAction[A]): Id[A] = fa match { case FetchById(_) => Property(1, "2000") case Save(_) => () case Log(msg) => println(msg) } def run[A](script: Script[A]) = script.foldMap(this) } }
Testing the Fix
You can test it by running:
TestInterp.run(updatePost(1, "Updated post"))
This should print "Updating" and return the updated Property instance.
内容的提问来源于stack exchange,提问作者Sam

