package com.zibaldone.cats
package ch_02
import cats.data.State
// iterative computations without mutations
// S => (S, A)
def compute(num: Int): (String, String) =
var a = num
a += 1
val firstComputation = s"Added ${1} obtained $a"
a *= 5
val secondComputation = s"Multiplied with ${5}, obtained $a"
(firstComputation, secondComputation)
val compute: State[Int, (String, String)] =
for
firstComputation <- State[Int, String] { num => (num + 1, s"Added ${1} obtained ${num + 1}") }
secondComputation <- State[Int, String] { num => (num * 5, s"Multiplied with ${5}, obtained ${num * 5}") }
yield (firstComputation, secondComputation)
// ex. shopping cart
final case class ShoppingCart(items: List[String], total: Double)
object ShoppingCart:
val empty: ShoppingCart = ShoppingCart(Nil, 0.0)
def addToCart(item: String, price: Double): State[ShoppingCart, Double] = State { shoppingCart =>
(ShoppingCart(item :: shoppingCart.items, shoppingCart.total + price), shoppingCart.total + price)
}
// ex. mental gymnastic
def inspect[A, B](f: A => B): State[A, B] = State { s => (s, f(s)) }
def get[A]: State[A, A] = State { s => (s, s) }
def set[A](a: A): State[A, Unit] = State { _ => (a, ()) }
def modify[A](f: A => A): State[A, Unit] = State { s => (f(s), ()) }
// sequential imperative computation reduced to pure functional programming
def program: State[Int, (Int, Int, Int)] =
for
a <- get[Int]
_ <- set[Int](a + 10)
b <- get[Int]
_ <- modify[Int](_ + 43)
c <- inspect[Int, Int](_ * 2)
yield (a, b, c)