Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active April 2, 2023 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dacr/ffdb4e1c653717cbfef6f737509ef97c to your computer and use it in GitHub Desktop.
Save dacr/ffdb4e1c653717cbfef6f737509ef97c to your computer and use it in GitHub Desktop.
Playing with monads / published by https://github.com/dacr/code-examples-manager #05e12ec0-33a4-4e0e-bed1-bd734d951a5d/dd967e1e193b1d5aa061417ef11677f9e94e7225
// summary : Playing with monads
// keywords : scala, monads, pure-functional, pattern, etw, @testable
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 05e12ec0-33a4-4e0e-bed1-bd734d951a5d
// created-on : 2022-01-22T15:13:44+01:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// ---------------------
//> using scala "3.1.1"
// ---------------------
// ------------------------------------------------------------
// Inspired from "A Monads Approach for Beginners, in Scala | Rock the JVM"
// https://www.youtube.com/watch?v=d-dy1x33moA
// ------------------------------------------------------------
// Some definitions :
// - https://www.baeldung.com/scala/monads :
// Monads are nothing more than a mechanism to sequence computations around values augmented with some additional feature
// - https://en.wikipedia.org/wiki/Monad_(functional_programming) :
// A monad is a type that wraps another type and gives some form of quality to the underlying type
// -
// ------------------------------------------------------------
// A type is a Monad if it has a unit and bind functions and follow 3 composability laws
// Functions
// - the unit function : to wrap a value inside the monad
// - the flatmap (or bind) function : to sequence computations over a value
// PROPERTIES
// - left identity : Monad(x).flatMap(f) == f(x)
// - right identity : Monad(x).flatMap(y => Monad(y)) == Monad(x)
// - associativity : Monad(x).flatMap(f).flatMap(g) == Monad(x).flatMap(y => f(y).flatMap(g)) == f(x).flatMap(g)
// ------------------------------------------------------------
case class SafeValue[+T](private val hiddenValue: T): // THE UNIT FUNCTION (the constructor) (also CALLED THE PURE FUNCTION)
def get: T = synchronized {
hiddenValue
}
def flatMap[S](transformer: T => SafeValue[S]): SafeValue[S] = // THE FLATMAP FUNCTION (also CALLED THE BIND FUNCTION)
synchronized {
transformer(hiddenValue)
}
// -------------------------------------------------------
// The ETW pattern - Extract/Transform/Wrap - the legacy/classic pattern
val safeMessage = SafeValue("secret")
val message = safeMessage.get // EXTRACT
val capitalizedMessage = message.capitalize // TRANSFORM
val capitalizedSafeMessage = SafeValue(
capitalizedMessage // WRAP
)
// -------------------------------------------------------
println(SafeValue(40d).flatMap(v => SafeValue(v + 2)))
println(SafeValue("hello world").flatMap(v => SafeValue(v.capitalize)))
// -------------------------------------------------------
def capitalize(str: String): Option[String] = Some(str.capitalize)
def spacelize(str: String): Option[String] = Some(str.map(" " + _).mkString)
// -------------------------------------------------------
// MONAD LAW 1 : LEFT IDENTITY
// SYNTAX1 : Monad(x).flatMap(f) == f(x)
// SYNTAX2 : Monad.unit(x).flatMap(f) = f(x)
assert(Option("hello").flatMap(capitalize) == capitalize("hello"))
// -------------------------------------------------------
// MONAD LAW 2 : RIGHT IDENTITY
// SYNTAX1 : Monad(x).flatMap(y => Monad(y)) == Monad(x)
// SYNTAX2 : x.flatMap(y => Monad.unit(y)) = x
assert(Option("hello").flatMap(x => Option(x)) == Option("hello"))
assert(List(1, 2, 3).flatMap(x => List(x)) == List(1, 2, 3))
// -------------------------------------------------------
// MONAD LAW 3 : ASSOCIATIVITY ( or ETW-ETW )
// SYNTAX1 : Monad(x).flatMap(f).flatMap(g) == Monad(x).flatMap(y => f(y).flatMap(g)) == f(x).flatMap(g) (because of LAW1)
// SYNTAX2 : x.flatMap(f).flatMap(g) == x.flatMap(y => f(y).flapMap(g))
assert(Option("hello").flatMap(capitalize).flatMap(spacelize) == Option("hello").flatMap(x => capitalize(x).flatMap(spacelize)))
assert(Option("hello").flatMap(capitalize).flatMap(spacelize) == capitalize("hello").flatMap(spacelize))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment