I need to write free monad interpreter which only perform effectfull actions but does not return any result. For free applicative I used interpreter with MyAction ~> Const[Unit, ?]
signature and called it using foldMap
function. However Const does not have monad instance. Id
does not suit because it requires to return actual value (evaluate embedded language). Should I write my own monad instance, similar to Id but without underlying value or there is a more convient way to create interpreter which does not produce any values?
Asked
Active
Viewed 41 times
0

schernichkin
- 1,013
- 7
- 15
-
What do you mean by "not producing any value"? If any function is called, it has to return at some point. If function does not return a meaningful value, its declaration would be: `def myFunc(): Unit`. So for your `F[A]`, result of your free program will be `F[Unit]`, do the job, but no value returned. – muradm Oct 27 '18 at 02:45
-
I guess question was ill formed. When using monad each next action will depend on result of the previous action. I.e. since bind has signature `m a -> (a -> m b) -> m b` there is no way to know `m b` without supplying `a` value. So answer should be "not possible by definition". With only one exception - when we using singletons as values (such as Unit). In this case each next action will not depend on `a` value since `a` will always be same. – schernichkin Oct 31 '18 at 14:02
-
To be correct, `Free` is all about sequencing actions, actions may or may not depend on previous values, but sequencing will be. Because in terms of `Free`, `Unit` returned by action would be also value. Can you elaborate on "exception - when we using singletons as values"? What else can be other than `Unit`? – muradm Oct 31 '18 at 14:29
1 Answers
0
What do you mean by "not producing any value"? Here is Free
monad example with interpreters that return nothing, but do something.
import cats.{Id, ~>}
import cats.effect.{ExitCode, IO, IOApp}
import cats.free.Free
import cats.free.Free.liftF
object Main extends IOApp {
// define algebra
sealed trait LaunchMissilesA[A]
case class LaunchMissileType1(arg: Int) extends LaunchMissilesA[Unit]
case class LaunchMissileType2(arg: String) extends LaunchMissilesA[Unit]
// define Free type
type LaunchMissiles[A] = Free[LaunchMissilesA, A]
// smart constructors
def launchMissileType1(arg: Int): LaunchMissiles[Unit] = liftF[LaunchMissilesA, Unit](LaunchMissileType1(arg))
def launchMissileType2(arg: String): LaunchMissiles[Unit] = liftF[LaunchMissilesA, Unit](LaunchMissileType2(arg))
// final program
def launchVariousMissiles: LaunchMissiles[Unit] =
for {
_ <- launchMissileType1(1)
_ <- launchMissileType2("str2")
} yield ()
// interpreter #1: IO
def ioMissileLauncher: LaunchMissilesA ~> IO = new (LaunchMissilesA ~> IO) {
override def apply[A](fa: LaunchMissilesA[A]): IO[A] = fa match {
case LaunchMissileType1(arg) =>
IO(println(s"[IO] launching Int missile of type 1: $arg").asInstanceOf[A])
case LaunchMissileType2(arg) =>
IO(println(s"[IO] launching String missile of type 2: $arg").asInstanceOf[A])
}
}
// interpreter #2: Id
def idMissileLauncher: LaunchMissilesA ~> Id = new (LaunchMissilesA ~> Id) {
override def apply[A](fa: LaunchMissilesA[A]): Id[A] = fa match {
case LaunchMissileType1(arg) =>
println(s"[Id] launching Int missile of type 1: $arg").asInstanceOf[A]
case LaunchMissileType2(arg) =>
println(s"[Id] launching String missile of type 2: $arg").asInstanceOf[A]
}
}
// end of the world
override def run(args: List[String]): IO[ExitCode] = for {
_ <- launchVariousMissiles.foldMap(ioMissileLauncher)
_ <- IO(launchVariousMissiles.foldMap(idMissileLauncher))
} yield ExitCode(0)
}

muradm
- 1,973
- 19
- 30