Skip to content

Instantly share code, notes, and snippets.

@jdegoes
Last active February 12, 2019 19:50
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 jdegoes/c0d923893ddb7039dcf44e8fe6012e0f to your computer and use it in GitHub Desktop.
Save jdegoes/c0d923893ddb7039dcf44e8fe6012e0f to your computer and use it in GitHub Desktop.
Example code from Applied Functional Programming with Scala
package lambdaconf.introfp2
// import scalaz._
// import Scalaz._
object functions {
object totality {
///def f[A]: A = null
def g[A]: A = throw new Error
def parseInt(v: String): Option[Int] =
??? //Try(v.toInt).toOption
type FileHandle = String
type FileError = String
def openFile(name: String): Either[FileError, FileHandle]
= ???
def identity[A]: A = identity[A]
}
object determinism {
val f = Math.random()
val g = Math.random()
}
object purity {
def println(line: String): Unit = ()
println("foo") // ()
def readLine: String = ???
}
object higher_order {
type Parser[E, A] =
String => Either[E, (String, A)]
def alt[E, A](
l: Parser[E, A],
r: Parser[E, A]): Parser[E, A] =
(i: String) => l(i).fold(_ => r(i), Right(_))
}
object polymorphic {
def square(x: Int): Int = x * x
object identity {
// [A] => ((a: A) => A)
def apply[A](a: A): A = a
}
def myMethod[A, B, C](a: A, b: B, c: C) = ???
// def myMethod[A: *, B: *, C: *](a: A, b: B, c: C) = ???
identity(1) // identity.apply[Int](1)
identity("foo") // identity.apply[String]("foo")
identity[String]("foo")
// second((1, 2)) // 2
// second((true, "false")) // "false"
object second {
def apply[A, B](t: (A, B)): B = t._2
}
}
}
object types {
object products {
type Point = (Int, Int)
case class Person(name: String, age: Int)
case class ServerLocation(host: String, port: Int)
type Currency = String
case class Amount1(total: BigDecimal, unit: Unit)
case class Amount2(total: BigDecimal)
def amount1ToAmount2(a: Amount1): Amount2 = Amount2(a.total)
def amount2ToAmount1(a: Amount2): Amount1 = Amount1(a.total, ())
}
object sums {
type Option[A] = Either[Unit, A]
// Either[Int, Int]
sealed trait Currency
object USD extends Currency
object EUR extends Currency
object NIS extends Currency
sealed trait JobTitle
case class Engineer(level: Int) extends JobTitle
final abstract class Zero {
def absurd[A]: A
}
def simplifyL[A](e: Either[A, Zero]): A =
e.fold(identity, _.absurd[A])
// IO[E, A] - Fail with an E, or compute an A
// IO[Throwable, A]
// IO[Nothing, A]
// List[Void]
// Future[Void]
case class Person(name: String, zero: Zero)
// Traffic light
}
object models {
type Email = String
val myEmail: Email = "ksljdf82938j9jfwdsifj892"
sealed trait Site
object Dungeon extends Site
object Forest extends Site
object University extends Site
val Topology : List[(Site, Site)] =
(Dungeon -> University) ::
(University -> Forest) :: Nil
case class Vitals(hp: Int, strength: Int, health: Int)
case class Character(category: Category, name: String, vitals: Vitals)
sealed trait Category
object Warrior extends Category
object Healer extends Category
object Engineer extends Category
sealed trait Vertical
case object Northsouth extends Vertical
case object Southnorth extends Vertical
sealed trait Lateral
case object Westeast extends Lateral
case object Eastwest extends Lateral
sealed trait Direction
case class Left(vertical: Vertical)
case class Right(lateral: Lateral)
case class Both(both: (Vertical, Lateral))
sealed trait Action
case class Move(site: Site)
}
object kinds {
// sealed trait List[+A]
// case class Cons[+A](head: A, tail: List[A]) extends List[A]
// case object Nil extends List[Nothing]
// val myList : List[Int] = ???
val f : Int => Int = (v: Int) => v + 1
f(1)
case class Person[A, B](name: A, age: B)
// List : * => *
// List[Int]
// f : Int => Int
// f(1)
// g : (Int, Int) => Int
// Either : [*, *] => *
// def myMethod[A, B, C](a: A, b: B, c: C) = ???
def myMethod1[F[_], A](fa: F[A]): F[A] = ???
def myMethod2[F[_, _], A, B](fab: F[A, B]): F[A, B] = ???
// String
// def method[A] -- *
// method[String]
// List
// def method[A[_]] -- * => *
// method[List]
// Option
// def method[X[_]] -- * => *
// method[Option]
// Either
// def method[A[_, _]] -- [*, *] => *
// method[Either]
// Tuple3
// def method[T[_, _, _]] -- [*, *, *] => *
// method[Tuple3]
trait StackModule[F[_]] {
def empty[A]: F[A]
def push[A](a: A, s: F[A]): F[A]
def pop[A](s: F[A]): Option[(A, F[A])]
}
// StackModule : (* => *) => *
// type T = StackModule[Future]
val ListStack = new StackModule[List] {
def empty[A] = Nil
def push[A](a: A, as: List[A]): List[A] = a :: as
def pop[A](s: List[A]): Option[(A, List[A])] = s match {
case Nil => None
case a :: as => Some((a, as))
}
}
trait Example1 // *
trait Example2[A] // * => *
trait Example3[F[_, _]] // ([*, *] => *) => *
trait Example4[F[_[_, _], _], G[_[_[_]]], H]
// trait Example4[X, Y, Z]
// [[[*, *] => *, *] => *, ((* => *) => *) => *, *] => *
// trait NaturalTransformation[F[_], G[_]]
// (* => *, * => *) => *
// Map -- F[_, _] -- [*, *] => *
// * => *
// [*, *] => *
// [* => *, *] => *
// ((* => *) => *) => *
trait FourthExercise[_[_[_]]]
}
object partial_application {
val add : (Int, Int) => Int = (a: Int, b: Int) => a + b
val increment: Int => Int = add(1, _)
(1 :: 2 :: Nil).map(increment)
trait Sized[F[_]] {
def size[A](fa: F[A]): Int
}
// ({type TupleA[B] = (A, B)})#TupleA
def SizedMap[K]: Sized[Map[K, ?]] = new Sized[Map[K, ?]] {
def size[A](fa: Map[K, A]): Int = fa.size
}
def SizedTuple2[A]: Sized[Tuple2[A, ?]] = new Sized[(A, ?)] {
def size[B](fb: (A, B)): Int = 1
}
val SizedList = new Sized[List] {
def size[A](fa: List[A]): Int = fa.length
}
}
}
object type_classes {
def repeat(n: Int, s: String): String =
if (n <= 0) "" else s + repeat(n - 1, s)
def foo(n: Int, s: String): String = ???
def bar1[A, B](t: (A, B)): B = ???
def bar2[A, B](a: A, e: Either[A, B]): A =
e.fold(identity, _ => a)
def bar3[A](a: A, f: A => A): A = ???
def bar4[A, B, C](l: List[A], r: List[B],
f: A => C, g: B => C, h: (A, B) => C): List[C] = ???
import algebra._
def repeat2[A: Monoid](n: Int, r: A): A =
if (n <= 0) empty[A] else r <> repeat2(n - 1, r)
object algebra {
trait Semigroup[A] {
// Associative Law:
// append(a, append(b, c)) == append(append(a, b), c)
def append(l: A, r: A): A
}
trait Monoid[A] extends Semigroup[A] {
// Identity laws:
// append(a, zero) == a
// append(zero, a) == a
def empty: A
}
object Monoid {
def apply[A](implicit M: Monoid[A]): Monoid[A] = M
}
implicit val MonoidString = new Monoid[String] {
def empty: String = ""
def append(l: String, r: String): String =
l + r
}
implicit def MonoidList[A] = new Monoid[List[A]] {
def empty: List[A] = Nil
def append(l: List[A], r: List[A]): List[A] = l ++ r
}
implicit def OptionMonoid[A: Semigroup] = new Monoid[Option[A]] {
def empty: Option[A] = None
def append(l: Option[A], r: Option[A]): Option[A] =
(for {
l <- l
r <- r
} yield l <> r).orElse(l).orElse(r)
}
implicit def MapSemigroup[K, V: Semigroup] = new Semigroup[Map[K, V]] {
def append(l: Map[K, V], r: Map[K, V]): Map[K, V] =
(l.toList ++ r.toList).foldLeft[Map[K, V]](Map()) {
case (map, (k, v)) =>
(map.get(k) <> Option(v)).fold(map)(map.updated(k, _))
}
}
def empty[A: Monoid]: A = Monoid[A].empty
object Semigroup {
def apply[A](implicit S: Semigroup[A]): Semigroup[A] = S
}
implicit class SemigroupSyntax[A](val l: A) extends AnyVal {
def <> (r: A)(implicit S: Semigroup[A]): A =
S.append(l, r)
}
empty[String] <> empty[String]
}
object ct {
trait Functor[F[_]] {
// Identity Law:
// fmap(fa, identity) == fa
// Composition Law:
// fmap(fmap(fa, g), f) == fmap(fa, f.compose(g))
def fmap[A, B](fa: F[A], f: A => B): F[B]
}
object Functor {
def apply[F[_]](implicit F: Functor[F]): Functor[F] = F
implicit val ListFunctor = new Functor[List] {
def fmap[A, B](fa: List[A], f: A => B): List[B] =
fa.map(f)
}
implicit val OptionFunctor = new Functor[Option] {
def fmap[A, B](fa: Option[A], f: A => B): Option[B] =
fa.map(f)
}
implicit def FunctionFunctor[K]: Functor[Function[K, ?]] =
new Functor[Function[K, ?]] {
def fmap[A, B](fa: K => A, f: A => B): K => B =
f.compose(fa)
}
}
trait Contravariant[F[_]] {
def contramap[A, B](fa: F[A], f: B => A): F[B]
}
implicit def ContravariantFunction[K] =
new Contravariant[Function[?, K]] {
def contramap[A, B](fa: A => K, f: B => A): B => K =
fa.compose(f)
}
trait Invariant[F[_]] {
def xmap[A, B](fa: F[A], ab: A => B, ba: B => A): F[B]
}
type Json = String
trait JsonCodec[A] {
def encode(a: A): Json
def decode(j: Json): A
}
trait Apply[F[_]] extends Functor[F] {
def ap[A, B](ff: F[A => B], fa: F[A]): F[B]
def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] =
ap(fmap(fa, (a: A) => (b: B) => (a, b)), fb)
}
object Apply {
def apply[F[_]](implicit F: Apply[F]): Apply[F] = F
}
implicit val OptionApply = new Apply[Option] {
def fmap[A, B](fa: Option[A], f: A => B): Option[B] =
fa.map(f)
def ap[A, B](ff: Option[A => B], fa: Option[A]): Option[B] =
(ff, fa) match {
case (Some(f), Some(a)) => Some(f(a))
case _ => None
}
}
implicit val ListApply = new Apply[List] {
def fmap[A, B](fa: List[A], f: A => B): List[B] =
fa.map(f)
def ap[A, B](ff: List[A => B], fa: List[A]): List[B] =
for {
f <- ff
a <- fa
} yield f(a)
}
case class Parser[E, A](
run: String => Either[E, (String, A)])
object Parser {
def success[E, A](a: A): Parser[E, A] =
Parser[E, A](i => Right((i, a)))
}
implicit def ParserApply[E] = new Apply[Parser[E, ?]] {
def ap[A, B](ff: Parser[E, A => B], fa: Parser[E, A]): Parser[E, B] =
Parser[E, B](i => ff.run(i) match {
case Left(e) => Left(e)
case Right((i, f)) =>
fa.run(i) match {
case Left(e) => Left(e)
case Right((i, a)) =>
Right((i, f(a)))
}
})
def fmap[A, B](fa: Parser[E, A], f: A => B): Parser[E, B] =
Parser[E, B](i => fa.run(i) match {
case Left(e) => Left(e)
case Right((i, a)) => Right((i, f(a)))
})
}
trait Applicative[F[_]] extends Apply[F] {
def point[A](a: A): F[A]
}
object Applicative {
def apply[F[_]](implicit F: Applicative[F]): Applicative[F] = F
}
trait Monad[F[_]] extends Applicative[F] {
def bind[A, B](fa: F[A], afb: A => F[B]): F[B]
}
object Monad {
def apply[F[_]](implicit F: Monad[F]): Monad[F] = F
implicit val MonadOption = new Monad[Option] {
def point[A](a: A): Option[A] = Some(a)
def bind[A, B](fa: Option[A], afb: A => Option[B]): Option[B] =
fa.fold[Option[B]](None)(afb)
def fmap[A, B](fa: Option[A], f: A => B): Option[B] =
fa.map(f)
def ap[A, B](ff: Option[A => B], fa: Option[A]): Option[B] =
Apply[Option].ap(ff, fa)
}
implicit def MonadParser[E] = new Monad[Parser[E, ?]] {
def point[A](a: A): Parser[E, A] = Parser.success(a)
def bind[A, B](fa: Parser[E, A], f: A => Parser[E, B]): Parser[E, B] =
Parser[E, B](i => fa.run(i) match {
case Left(e) => Left(e)
case Right((i, a)) => f(a).run(i)
})
def ap[A, B](ff: Parser[E, A => B], fa: Parser[E, A]): Parser[E, B] =
Apply[Parser[E, ?]].ap(ff, fa)
def fmap[A, B](fa: Parser[E, A], f: A => B): Parser[E, B] =
Apply[Parser[E, ?]].fmap(fa, f)
}
}
implicit class MonadSyntax[F[_], A](fa: F[A]) {
def map[B](f: A => B)(implicit F: Monad[F]): F[B] =
F.fmap(fa, f)
def flatMap[B](f: A => F[B])(implicit F: Monad[F]): F[B] =
F.bind(fa, f)
def zip[B](fb: F[B])(implicit F: Monad[F]): F[(A, B)] =
F.zip(fa, fb)
}
}
}
object effects {
import type_classes.ct._
final abstract class Void {
def absurd[A]: A
}
case class IO[E, A](unsafePerformIO: () => Either[E, A]) { self =>
def map[B](f: A => B): IO[E, B] =
IO[E, B](() => self.unsafePerformIO().map(f))
def flatMap[B](f: A => IO[E, B]): IO[E, B] =
IO[E, B](() => self.unsafePerformIO() match {
case Left(e) => Left(e)
case Right(a) => f(a).unsafePerformIO()
})
def attempt: IO[Void, Either[E, A]] =
IO[Void, Either[E, A]](() => Right(self.unsafePerformIO()))
}
object IO {
def point[E, A](a: A): IO[E, A] = IO[E, A](() => Right(a))
def fail[E, A](e: E): IO[E, A] = IO[E, A](() => Left(e))
implicit def MonadIO[E]: Monad[IO[E, ?]] = new Monad[IO[E, ?]] {
def point[A](a: A): IO[E, A] = IO.point(a)
def fmap[A, B](fa: IO[E, A], f: A => B): IO[E, B] = fa.map(f)
def ap[A, B](ff: IO[E, A => B], fa: IO[E, A]): IO[E, B] =
for {
f <- ff
a <- fa
} yield f(a)
def bind[A, B](fa: IO[E, A], f: A => IO[E, B]): IO[E, B] =
fa.flatMap(f)
}
}
object console2 {
def putStrLn[F[_]](line: String)(implicit F: Console[F]): F[Unit] =
F.putStrLn(line)
def getStrLn[F[_]](implicit F: Console[F]): F[String] =
F.getStrLn
}
trait Console[F[_]] {
def putStrLn(line: String): F[Unit]
val getStrLn: F[String]
}
object Console {
implicit val ConsoleIO: Console[IO[Void, ?]] = new Console[IO[Void, ?]] {
def putStrLn(line: String): IO[Void, Unit] =
IO(() => Right(println(line)))
val getStrLn: IO[Void, String] =
IO(() => Right(scala.io.StdIn.readLine()))
}
}
trait Random[F[_]] {
def nextInt(bound: Int): F[Int]
}
object Random {
implicit val RandomIO: Random[IO[Void, ?]] = new Random[IO[Void, ?]] {
def nextInt(bound: Int): IO[Void, Int] =
IO(() => Right(scala.util.Random.nextInt(bound)))
}
}
object rand {
def nextInt[F[_]](bound: Int)(implicit F: Random[F]): F[Int] =
F.nextInt(bound)
}
}
object app {
import type_classes.ct._
import effects._
import console2._
import rand._
sealed trait Selection
case object PlayGame extends Selection
case object Exit extends Selection
def parseSelection(input: String): Option[Selection] =
input.trim.toLowerCase match {
case "1" => Some(PlayGame)
case "2" => Some(Exit)
case _ => None
}
def invalidChoice[F[_]: Console]: F[Unit] =
putStrLn("Your choice was invalid.")
def chooseWord[F[_]: Functor: Random]: F[String] = {
val list = ("monad" :: "functor" :: "israel" :: "jalapeno" :: Nil)
Functor[F].fmap(nextInt[F](list.length), list)
}
def printGame[F[_]: Monad: Console](state: GameState): F[Unit] = {
val word =
state.word.toList.map(char =>
if (state.guessed.contains(char)) char else '_').mkString(" ")
for {
_ <- putStrLn("==================")
_ <- putStrLn(word)
_ <- putStrLn("You've guessed: " + state.guessed.mkString(", "))
} yield ()
}
case class GameState(guessed: Set[Char], word: String)
def parseLetter(l: String): Option[Char] = l.trim.toLowerCase.toList match {
case x :: Nil => Some(x)
case _ => None
}
def updateState[F[_]: Monad: Console](state: GameState, c: Char): F[Option[GameState]] = {
val state2 = state.copy(guessed = state.guessed + c)
for {
_ <- printGame(state2)
o <- if ((state2.word.toSet -- state2.guessed).isEmpty)
putStrLn("Congratulations, you won!!!").map(_ => None)
else if (state2.guessed.size > state2.word.length * 2)
putStrLn("It's not your day. You were hanged.").map(_ => None)
else putStrLn("You made a guess.").map(_ => Some(state2))
} yield o
}
def playGameLoop[F[_]: Monad: Console: Random](state: GameState): F[Unit] =
for {
_ <- printGame[F](state)
l <- getStrLn[F]
o <- parseLetter(l) match {
case None => putStrLn("Enter one letter.").map(_ => Some(state))
case Some(c) => updateState[F](state, c)
}
_ <- o.fold(putStrLn("Good game!"))(playGameLoop[F])
} yield ()
def playGame[F[_]: Monad: Console: Random]: F[Unit] =
chooseWord[F].flatMap(word => playGameLoop[F](GameState(Set(), word)))
def printMenu[F[_]: Console]: F[Unit] =
putStrLn("""
Please enter your selection:
1. Play a new game
2. Exit the game""")
def mainLoop[F[_]: Monad: Console: Random](name: String): F[Unit] =
for {
_ <- printMenu
choice <- getStrLn
_ <- parseSelection(choice).fold(invalidChoice.flatMap(_ => mainLoop(name))) {
case Exit => putStrLn("Goodbye, " + name + "!")
case PlayGame => playGame.flatMap(_ => mainLoop(name))
}
} yield ()
def run[F[_]: Monad: Console: Random]: F[Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn("Hello, " + name + ", welcome to the game!")
_ <- mainLoop(name)
} yield ()
case class TestState(
input: List[String],
output: List[String],
randos: List[Int]) {
def showResults: String = output.reverse.mkString("\n")
}
case class TestIO[A](run: TestState => (TestState, A)) { self =>
def map[B](f: A => B): TestIO[B] =
TestIO[B](s => {
val (s2, a) = self.run(s)
(s2, f(a))
})
def flatMap[B](f: A => TestIO[B]): TestIO[B] =
TestIO[B](s => {
val (s2, a) = self.run(s)
f(a).run(s2)
})
}
object TestIO {
def point[A](a: A): TestIO[A] =
TestIO[A](s => (s, a))
implicit val MonadTestIO = new Monad[TestIO] {
def point[A](a: A): TestIO[A] = TestIO.point(a)
def fmap[A, B](fa: TestIO[A], f: A => B): TestIO[B] = fa.map(f)
def ap[A, B](ff: TestIO[A => B], fa: TestIO[A]): TestIO[B] =
for {
f <- ff
a <- fa
} yield f(a)
def bind[A, B](fa: TestIO[A], f: A => TestIO[B]): TestIO[B] =
fa.flatMap(f)
}
implicit val ConsoleTestIO = new Console[TestIO] {
def putStrLn(line: String): TestIO[Unit] =
TestIO(s => (s.copy(output = line :: s.output), ()))
val getStrLn: TestIO[String] =
TestIO(s => (s.copy(input = s.input.drop(1)), s.input.head))
}
implicit val RandomTestIO = new Random[TestIO] {
def nextInt(bound: Int): TestIO[Int] =
TestIO(s => (s.copy(randos = s.randos.drop(1)), s.randos.head))
}
}
val MockRun: TestState =
TestState(
output = Nil,
input = List("John", "1", "m", "o", "n", "a", "d", "2"),
randos = List(0)
)
def runIO: IO[Void, Unit] = run[IO[Void, ?]]
def runTest: TestIO[Unit] = run[TestIO]
}
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment