Last active
July 29, 2018 21:08
-
-
Save afsalthaj/9a59ed8ae4b7b1c238759a85af5ccf45 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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