So I've been getting deeper into FP concepts and I liked the concept of purity enclosed in the IO monad. Then I read this, and thought that the IO monad is indeed not as decoupled(?) as using Free Monads.
So I started doing my stuff using those concepts and then I realized that traits achieve the same purpose of separating structure from execution. Even worse than that, using free monads has a lot of limitations, like error handling and passing context bounds and implicit parameters into the interpreter/implementation.
So my question is: what's the advantage of using them? how do I solve the problems I just mentioned (implicit params & error handling)? Does the use of Free Monands limit to the academic realm, or can it be used in the industry?
Edit: An example to explain my doubts
import cats.free.Free._
import cats.free.Free
import cats.{Id, ~>}
import scala.concurrent.Future
sealed trait AppOpF[+A]
case class Put[T](key: String, value: T) extends AppOpF[Unit]
case class Delete(key: String) extends AppOpF[Unit]
//I purposely had this extend AppOpF[T] and not AppOpF[Option[T]]
case class Get[T](key: String) extends AppOpF[T]
object AppOpF {
type AppOp[T] = Free[AppOpF, T]
def put[T](key: String, value: T): AppOp[Unit] = liftF[AppOpF, Unit](Put(key, value))
def delete(key: String): AppOp[Unit] = liftF[AppOpF, Unit](Delete(key))
def get[T](key: String): AppOp[T] = liftF[AppOpF, T](Get(key))
def update[T](key: String, func: T => T): Free[AppOpF, Unit] = for {
//How do I manage the error here, if there's nothing saved in that key?
t <- get[T](key)
_ <- put[T](key, func(t))
} yield ()
}
object AppOpInterpreter1 extends (AppOpF ~> Id) {
override def apply[A](fa: AppOpF[A]) = {
fa match {
case Put(key,value)=>
???
case Delete(key)=>
???
case Get(key) =>
???
}
}
}
//Another implementation, with a different context monad, ok, that's good
object AppOpInterpreter2 extends (AppOpF ~> Future) {
override def apply[A](fa: AppOpF[A]) = {
fa match {
case a@Put(key,value)=>
//What if I need a Json Writes or a ClassTag here??
???
case a@Delete(key)=>
???
case a@Get(key) =>
???
}
}
}