Alexey Shuksto
Self-documented.
final class StatefulLFSR(var seed: Int, val mask: Int = 0x002D) {
import java.lang.Integer.bitCount
def next(): Int = {
seed = (seed >> 1) | ((bitCount(seed & mask) & 1) << 15)
seed
}
}
def three(seed: Int): Unit = {
val lfsr = new StatefulLFSR(seed)
val i1 = lfsr.next()
val i2 = lfsr.next()
val i3 = lfsr.next()
println(s"i1 = $i1, i2 = $i2, i3 = $i3")
}
scala> StatefulLFSR.three(0xACE1)
i1 = 22128, i2 = 43832, i3 = 21916
def period(seed: Int): Unit = {
val lfsr = new StatefulLFSR(seed)
var period = 0
while (seed != lfsr.next()) period += 1
println(s"period = $period")
}
scala> StatefulLFSR.period(0xACE1)
period = 65534
final case class StatelessLFSR(seed: Int, mask: Int = 0x002D)
object StatelessLFSR {
import java.lang.Integer.bitCount
def next(lfsr: StatelessLFSR): StatelessLFSR = {
import lfsr._
lfsr.copy(seed = (seed >> 1) | ((bitCount(seed & mask) & 1) << 15))
}
}
def three(seed: Int): (Int, Int, Int) = {
val lfsr = StatelessLFSR(seed)
(next(lfsr).seed, next(lfsr).seed, next(lfsr).seed)
}
scala> StatelessLFSR.three(0xACE1)
res2: (Int, Int, Int) = (22128,22128,22128) // WTF?!
final case class StatelessLFSR(seed: Int, mask: Int = 0x002D)
object StatelessLFSR {
import java.lang.Integer.bitCount
def next(lfsr: StatelessLFSR): (StatelessLFSR, Int) = {
import lfsr._
val nxt = lfsr.copy(seed = (seed >> 1) | ((bitCount(seed & mask) & 1) << 15))
(nxt, nxt.seed)
}
}
def three(seed: Int): (Int, Int, Int) = {
val lfsr0 = StatelessLFSR(seed)
val (lfsr1, i1) = next(lfsr0)
val (lfsr2, i2) = next(lfsr1)
val (_, i3) = next(lfsr2)
(i1, i2, i3)
}
scala> StatelessLFSR.three(0xACE1)
res3: (Int, Int, Int) = (22128,43832,21916)
def period(seed: Int): Int = {
var (lfsr, nxt) = next(StatelessLFSR(seed))
var period = 0
while (seed != nxt) {
// (lfsr, nxt) = next(lfsr) would be so cool
val (l, n) = next(lfsr)
period += 1
lfsr = l
nxt = n
}
period
}
scala> StatelessLFSR.period(0xACE1)
res4: Int = 65534
def next(lfsr: StatelessLFSR): (StatelesLFSR, Int) = ...
val next: (StatelessLFSR) => (StatelessLFSR, Int) = ...
def run[S, A]: (S) => (S, A) = ...
class State[S, A](run: (S) => (S, A)) { ??? }
final class State[S, A](val run: (S) => (S, A)) { self =>
val runA: (S) => A = run(_)._2
val runS: (S) => S = run(_)._1
def map[B](f: (A) => B): State[S, B] = State[S, B] { s0 =>
val (s1, a) = self.run(s0)
(s1, f(a))
}
def flatMap[B](f: (A) => State[S, B]): State[S, B] = State[S, B] { s0 =>
val (s1, a) = self.run(s0)
val (s2, b) = f(a).run(s1)
(s2, b)
}
}
object State {
def apply[S, A](f: (S) => (S, A)): State[S, A] = new State[S, A](f)
def inspect[S, A](f: (S) => A): State[S, A] = State(s => (s, f(s)))
def modify[S](f: (S) => S): State[S, Unit] = State(s => (f(s), ()))
def get[S]: State[S, S] = inspect(identity)
def set[S](s: S): State[S, Unit] = modify(_ => s)
def pure[S, A](a: A): State[S, A] = inspect(_ => a)
}
final case class StatelessLFSR(seed: Int, mask: Int = 0x002D)
object StatelessLFSR {
import cats.data.State
import java.lang.Integer.bitCount
type LFSR[A] = State[StatelessLFSR, A]
val shift: LFSR[Unit] = State.modify { lfsr =>
import lfsr._
lfsr.copy(seed = (seed >> 1) | ((bitCount(seed & mask) & 1) << 15))
}
val next: LFSR[Int] = {
for {
_ <- shift
n <- State.inspect(_.seed)
} yield n
}
}
def three(seed: Int) = { // return type?!
for {
i1 <- next
i2 <- next
i3 <- next
} yield (i1, i2, i3)
}
scala> StatelessLFSR.three(0xACE1)
res5: cats.data.IndexedStateT[
cats.Eval,lfsr.StatelessLFSR,lfsr.StatelessLFSR,(Int, Int, Int)
] = cats.data.IndexedStateT@7585fa7 // ?!?!?!?!
val three: LFSR[Int] = for {
i1 <- next // val i1 = lfsr.next()
i2 <- next // val i2 = lfsr.next()
i3 <- next // val i3 = lfsr.next()
} yield (i1, i2, i3)
def runThree(seed: Int): (Int, Int, Int) =
three.runA(StatelessLFSR(seed)).value
// val lfsr = new StatefulLFSR(seed)
scala> StatelessLFSR.three(0xACE1)
res6: (Int, Int, Int) = (22128,43832,21916)
def period(seed: Int): Int = {
var (lfsr, nxt) = next.run(StatelessLFSR(seed)).value
var period = 0
while (seed != nxt) {
val (l, n) = next.run(lfsr).value
period += 1
lfsr = l
nxt = n
}
period
}
scala> StatelessLFSR.period(0xACE1)
res7: Int = 65534
def period(seed: Int): Int = {
import cats.Monad
import cats.implicits._
val period: LFSR[Int] = Monad[LFSR].tailRecM(none[Int] -> 0) {
case (Some(`seed`), p) => State.pure(Right(p))
case (s, p) => next.map { n =>
Left(n.some -> s.fold(p)(_ => p + 1))
}
}
period.runA(StatelessLFSR(seed)).value
}
trait Monad[F[_]] {
// ...
// calls f(a) till Right(b)
def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] = ...
}
package fs2
abstract class Stream[F[_], +O] {
def map[O2](f: O => O2): Stream[F, O2] = ...
def flatMap[O2](f: O => Stream[F, O2]): Stream[F, O2] = ...
// ...
def mapAccumulate[S, O2](init: S)(
f: (S, O) => (S, O2)
): Stream[F, (S, O2)] = ...
}
type Pipe[F[_], -I, +O] = Stream[F, I] => Stream[F, O]
type Sink[F[_], -I] = Pipe[F, I, Unit]
def three(seed: Int): List[Int] = Stream
.constant(next)
.through(runState(StatelessLFSR(seed)))
.take(3)
.toList
scala> StatelessLFSR.three(0xACE1)
res8: List[Int] = List(22128, 43832, 21916)
def runState[F[_], S, A](init: S): Pipe[F, State[S, A], A] = { in =>
in.mapAccumulate(init)((init, sa) => sa.run(init).value)
.map(_._2)
}
def period(seed: Int): Int = Stream
.constant(next)
.through(runState(StatelessLFSR(seed)))
.takeWhile(_ != seed)
.foldMap(_ => 1)
.lastOr(0)
.toList
.head
scala> StatelessLFSR.period(0xACE1)
res9: Int = 65534
def runState[F[_], S, A](init: S): Pipe[F, State[S, A], A] = { in =>
in.mapAccumulate(init)((init, sa) => sa.run(init).value)
.map(_._2)
}
package cats
type State[S, A] = StateT[Eval, S, A] // StateT[F[_], S, A]
trait Eval[A] { def value: A }
object Eval {
def now[A](a: A): Eval[A] = ...
def later[A](a: => A): Eval[A] = ...
def defer[A](a: => Eval[A]): Eval[A] = ...
}
package cats.effect
class IO[A] { .. } // ain't no simple `value: A` :(
object IO {
def pure[A](a: A): IO[A] = ... // Eval.now
def apply[A](a: => A): IO[A] = ... // Eval.later
def suspend[A](a: => IO[A]): IO[A] = ... // Eval.defer
def async[A](k: (Either[Throwable, A] => Unit) => Unit): IO[A] = ...
def eval[A](fa: Eval[A]): IO[A] = ...
}
scala> next.runA(StatelessLFSR(0xACE1)).value // .value?!
res10: Int = 22128
scala> val state = next
state: lfsr.StatelessLFSR.LFSR[Int] = cats.data.IndexedStateT@7d035352
scala> val eval = state.run(StatelessLFSR(0xACE1))
eval: cats.Eval[(lfsr.StatelessLFSR, Int)] = cats.Eval$$anon$6@7956cb16
scala> eval.value
res10: (lfsr.StatelessLFSR, Int) = (StatelessLFSR(22128,45),22128)
def runState[F[_]: FlatMap, S, A](init: S): Pipe[F, StateT[F, S, A], A] = {
def go(str: Stream[F, StateT[F, S, A]], init: S): Pull[F, A, Unit] =
str.pull.uncons1.flatMap {
case None => Pull.done
case Some((sa, rst)) =>
Pull.eval(sa.run(init)).flatMap {
case (s, a) => Pull.output1(a).flatMap(_ => go(rst, s))
}
}
in => go(in, init).stream
}
type Pipe[F[_], A, B] = Stream[F[_], A] => Stream[F[_], B]
type State[S, A] = ...
def runState[F[_], S, A](init: S): Pipe[F, State[S, A], A] = ...
type StateT[F[_], S, A] = ...
def runState[F[_], S, A](init: S): Pipe[F, StateT[F, S, A], A] = ???
final case class Account(amount: Int)
object Account {
type State[A] = StateT[IO, Account, A]
implicit val BalanceEq: Eq[Account] = Eq.fromUniversalEquals
implicit val BalanceShow: Show[Account] = Show.fromToString
}
// object Account {
private def _deposit(amount: Int): State[Unit] = StateT.modifyF { b =>
if (amount >= 0) IO(b.copy(amount = b.amount + amount))
else IO.raiseError {
new RuntimeException(s"Could not deposit negative amount: $amount.")
}
}
def deposit(amount: Int): State[Account] =
for {
_ <- _deposit(amount)
b <- StateT.get[IO, Account]
} yield b
// }
// object Account {
private def _interest(rate: Int): State[Unit] = StateT.modifyF { b =>
if (rate > 0) IO(b.copy(amount = (b.amount * rate) / 100))
else IO.raiseError {
new RuntimeException(
s"Could not accrual interest of non positive rate: $rate."
)
}
}
def interest(rate: Int): State[Account] = _interest(rate) *> StateT.get
// for {
// _ <- _interest(rate)
// b <- StateT.get[IO, Account]
// } yield b
// }
// object Account {
private def _withdraw(amount: Int): State[Unit] = StateT.modifyF { b =>
if (amount < 0) IO.raiseError {
new RuntimeException(s"Could not withdraw negative amount: $amount.")
} else if (amount > b.amount) IO.raiseError {
new RuntimeException(s"Insufficient funds: ${amount - b.amount}.")
} else IO {
b.copy(amount = b.amount - amount))
}
}
def withdraw(amount: Int): State[Int] = _withdraw(amount).as(amount)
// for {
// _ <- withdraw(amount)
// a <- State.pure[IO, Account, Int](amount)
// } yield a
// }
object Finances extends StreamApp[IO] {
import Account._
import scala.concurrent.ExecutionContext.Implicits.global
def stream(args: List[String], requestShutdown: IO[Unit])
: Stream[IO, StreamApp.ExitCode] = Scheduler[IO](3)
.flatMap { sch =>
val deposits = sch.fixedRate[IO](30.millis).as {
deposit(100).map(_.asLeft[Int])
}
val percents = sch.fixedRate[IO](30.millis).as {
interest(101).map(_.asLeft[Int])
}
val withdraws = sch.fixedRate[IO](7.millis).as {
withdraw(20).map(_.asRight[Account])
}
deposits.merge(percents).merge(withdraws)
}
.through(runState(Account(1000)))
.mergeHaltR {
Stream
.duration[IO]
.takeWhile(_ <= 365.millis)
.drain
}
.to(Sink.showLinesStdOut)
.last
.as(StreamApp.ExitCode.Success)
}
scala> Finances.main(Array())
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(929))
Right(20)
Left(Account(1009))
Right(20)
Right(20)
Right(20)
Left(Account(958))
Left(Account(1058))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1078))
Right(20)
Left(Account(1068))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1088))
Left(Account(1098))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1118))
Left(Account(1129))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1149))
Left(Account(1160))
Right(20)
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1070))
Left(Account(1170))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1190))
Left(Account(1201))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1221))
Left(Account(1233))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1253))
Right(20)
Left(Account(1245))
Right(20)
Right(20)
Right(20)
Right(20)
Left(Account(1265))
Left(Account(1277))
Right(20)
Right(20)
By Alexey Shuksto
Stateful Streams of Stateless State functions