Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active October 23, 2021 10:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fancellu/fcfccc5195e4d70a215b7ced6cab570b to your computer and use it in GitHub Desktop.
Save fancellu/fcfccc5195e4d70a215b7ced6cab570b to your computer and use it in GitHub Desktop.
Simple example of an IO effect Monad, no Cats, no ZIO
import scala.util.Random
// Simple example of an IO effect Monad, no Cats
object MyIO extends App{
// IO encapsulates a side effecting operation
final class IO[A](val run: () => A) {
def map[B](f: A => B): IO[B] = IO(f(run()))
def flatMap[B](f: A => IO[B]): IO[B] = IO(f(run()).run())
}
object IO {
def apply[A](a: =>A): IO[A] = new IO(() => a)
}
// Some example type classes, without regard to context
trait Console[C[_]] {
def read: C[String]
def readBoolean: C[Boolean]
def write(st: String): C[Unit]
}
trait Random[C[_]] {
def randomString: C[String]
}
// we implement IOs for above type classes, which is where we perform our side effects
object ConsoleIO extends Console[IO] {
def read: IO[String] = IO { io.StdIn.readLine }
def readBoolean: IO[Boolean] = IO { io.StdIn.readBoolean }
def write(st: String): IO[Unit] = IO { println(st) }
}
object RandomIO extends Random[IO] {
def randomString: IO[String]= IO {Random.alphanumeric.take(8).mkString}
}
// composing an IO with for comprehensions
def prog(consoleIO: Console[IO], randomIO: Random[IO]): IO[String] ={
import consoleIO._
import randomIO._
println("running"*10)
for {
_<- write("please enter a word")
readWord <- read.map(_+" !!!")
_<- write("do you like cats?")
boolean <- readBoolean
randomString1 <- randomString
randomString2 <- randomString
str=s"$readWord,$boolean,$randomString1,$randomString2"
_<- write(str)
} yield str
}
println("Nothing is run yet, running now")
val out=prog(ConsoleIO,RandomIO).run()
println(s"out=$out")
object FakeConsoleIO extends Console[IO] {
def read: IO[String] = IO { "fake" }
def readBoolean: IO[Boolean] = IO { true }
def write(st: String): IO[Unit] = IO { () }
}
object FakeRandomIO extends Random[IO] {
def randomString: IO[String]= IO {"fakefake"}
}
// here we run again but with canned IOs, nothing comes from real console or real random source
val out2=prog(FakeConsoleIO,FakeRandomIO).run()
println(s"out2=$out2")
// we create 2 random numbers here, and output them
val rnd: IO[Unit] =for {
randomString1 <- RandomIO.randomString
randomString2 <- RandomIO.randomString
str=s"$randomString1,$randomString2"
_<- ConsoleIO.write(str)
} yield ()
// note, both runs create different numbers, nothing is memoized
rnd.run()
rnd.run()
}
Nothing is run yet, running now
runningrunningrunningrunningrunningrunningrunningrunningrunningrunning
please enter a word
woof
do you like cats?
n
woof !!!,false,MisEM4ds,qkdC6hIR
out=woof !!!,false,MisEM4ds,qkdC6hIR
runningrunningrunningrunningrunningrunningrunningrunningrunningrunning
out2=fake !!!,true,fakefake,fakefake
qOA3J11v,pNhxfc2o
igrcGfC9,YizkSMkv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment