Skip to content

Instantly share code, notes, and snippets.

@afsalthaj
Last active July 29, 2018 21:08
Show Gist options
  • Save afsalthaj/9a59ed8ae4b7b1c238759a85af5ccf45 to your computer and use it in GitHub Desktop.
Save afsalthaj/9a59ed8ae4b7b1c238759a85af5ccf45 to your computer and use it in GitHub Desktop.
import java.io.File
import scalaz.{Id, Monad}
// Finally tagless approach
trait FileOperation[F[_], A] {
def makeDir(d: File, x: A): F[A]
def createFile(d: File, x: A): F[A]
// Remember, here we have an F which is a monad, but this is conceptually different from the `Free` monad
// discussed in the `Free` solution of the same problem. https://gist.github.com/afsalthaj/110e01322cc05ef6c7a1e6fa45e95dea.
// In Free, we had a monad, but the client never wanted to create/select an effect F (that has a monad instance)
// when calling `myProgram` function.
// On the other hand, in finally tagless, in order to call this function `myProgram`, I have
// to start worrying about creating/selecting an effect `F` that has a monad instance.
// The difference is subtle but really important.
// Furthermore, most of the times this F is always the same allowing someone to ask you why did you abstract.
def myProgram(a: A)(implicit F: Monad[F]): String =
for {
_ <- makeDir(new File(""), a)
_ <- createFile(new File(""), a)
} yield ()
}
object Main {
val dummyFileOperation: FileOperation[scalaz.Id.Id, String] =
// I selected an effect F by now. i.e, scalaz.Id.Id.
// Checkout https://gist.github.com/afsalthaj/110e01322cc05ef6c7a1e6fa45e95dea
// to see if Free monad pattern resulted in any effect at client site.
// This discards the justification that Free monad is same
// as `F` in finally tagless with a monad instance.
// Also, note that I am creating a dummy FilOperation instance to call
// a function which is inherently part of the algebra. This is conceptually a constraint
// in pure FP world.
new FileOperation[scalaz.Id.Id, String] {
override def createFile(d: File, x: String): Id.Id[String] = x
override def makeDir(d: File, x: String): Id.Id[String] = x
}
def main(args: Array[String]): Unit = {
val s: String = dummyFileOperation.myProgram("afsal")
// Hmm.. I am not sure. How can I test if `myProgram` create a file and then create a directory.
// I have to test the execution by inspecting the result of execution.
// This is different from testing the actual logic.
// Or in other words, I couldn't test the correctness of the logic.
// This was possible in Free because it did the actual deferring of execution.!
// In Free, I could peel off the Free wrapper over the program, look through the nodes of the program
// and assert what is in there!
// We can get over this constraint using Pre-interpreting to Const as mentioned in Lukes video
assert(s == "afsal")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment