Skip to content

Instantly share code, notes, and snippets.

@steinybot
Created December 22, 2015 22:32
Show Gist options
  • Save steinybot/4fe5626f9f0800f0748f to your computer and use it in GitHub Desktop.
Save steinybot/4fe5626f9f0800f0748f to your computer and use it in GitHub Desktop.
My attempt at learning Monads in Scala
/**
* I wrote this while trying to figure out what a monad was and how you would write one. No guarantees that this is
* actually a monad or good code at all.
*
* I wanted this monad to encapsulate side effects and apply them lazily. One interesting observation that I found
* from all the println statements was that in order for the bind (flatMap) to not evaluate the side effect was to use
* by-name parameters. Not sure if this is really a unit function anymore...
*/
object StringOpsMonad extends App {
println("-----")
val a = NoOp("hello")
println("a")
println(a.op)
println("-----")
val b = a flatMap { CapitaliseOp.apply(_) }
println("b")
println(b.op)
println("-----")
val c = b flatMap { DuplicateOp.apply(_) }
println("c")
println(c.op)
println("-----")
val d = for {
a <- NoOp("hello")
b <- CapitaliseOp(a)
c <- DuplicateOp(b)
} yield c
println("d")
println(d.op)
println("-----")
val e = NoOp("hello") flatMap { str =>
CapitaliseOp(str) flatMap { str =>
DuplicateOp(str) map { str =>
str
}
}
}
println("e")
println(e.op)
println("-----")
for {
a <- NoOp("hello")
b <- CapitaliseOp(a)
c <- DuplicateOp(b)
} {
println("f")
println(c)
}
}
sealed abstract class StringOp {
def op: String
def map(func: String => String): StringOp = NoOp(func(op))
def flatMap(func: String => StringOp): StringOp = ComposedOp(op, func)
def foreach[T](func: String => T): Unit = func(op)
private object ComposedOp {
def apply(otherOp: => String, func: String => StringOp): StringOp = new StringOp {
def op: String = func(otherOp).op
}
}
}
object NoOp {
def apply(str: => String): StringOp = new NoOp(str)
}
class NoOp(str: => String) extends StringOp {
def op: String = {
println("NoOp operator")
str
}
}
object CapitaliseOp {
def apply(str: => String): StringOp = new CapitaliseOp(str)
}
class CapitaliseOp(str: => String) extends StringOp {
def op: String = {
println("CapitaliseOp operator")
str.capitalize
}
}
object DuplicateOp {
def apply(str: => String): StringOp = new DuplicateOp(str)
}
class DuplicateOp(str: => String) extends StringOp {
def op: String = {
println("DuplicateOp operator")
str + str
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment