Compare commits

..

No commits in common. "ed12f5c442fb391cdfb72ba6cd27097b8cec539a" and "439c28c1c767304ee32afdcbfa67ea8a556fb69c" have entirely different histories.

6 changed files with 65 additions and 126 deletions

View file

@ -1,60 +1,18 @@
package gs.fizzbuzz package gs.fizzbuzz
import cats.Applicative import cats.Applicative
import cats.Monoid
trait Condition[F[_], Input]: trait Condition[F[_], Input, Output]:
def matches(input: Input): F[Boolean] def matches(input: Input): F[Boolean]
def output: Output
end Condition end Condition
object Condition: object Condition:
def apply[F[_]: Applicative, Input]( final class Noop[F[_]: Applicative, Input] extends Condition[F, Input, Nothing]:
comparison: Input, override def matches(input: Input): F[Boolean] =
operator: Monoid[Input]
)(
using
CanEqual[Input, Input]
): Condition[F, Input] =
new DefaultCondition[F, Input](comparison, operator)
def forInt[F[_]: Applicative](
comparison: Int,
operator: (Int, Int) => Int
): Condition[F, Int] =
new DefaultCondition[F, Int](comparison, makeIntMonoid(operator))
def forLong[F[_]: Applicative](
comparison: Long,
operator: (Long, Long) => Long
): Condition[F, Long] =
new DefaultCondition[F, Long](comparison, makeLongMonoid(operator))
private def makeIntMonoid(op: (Int, Int) => Int): Monoid[Int] =
new Monoid[Int] {
override def empty: Int = 0
override def combine(
x: Int,
y: Int
): Int = op(x, y)
}
private def makeLongMonoid(op: (Long, Long) => Long): Monoid[Long] =
new Monoid[Long] {
override def empty: Long = 0
override def combine(
x: Long,
y: Long
): Long = op(x, y)
}
def noop[F[_]: Applicative, Input]: Condition[F, Input] = new Noop[F, Input]
final class Noop[F[_]: Applicative, Input] extends Condition[F, Input]:
override def matches(input: Input): F[Boolean] =
Applicative[F].pure(false) Applicative[F].pure(false)
override def output: Nothing =
throw new IllegalStateException("No-op condition cannot produce output.")
end Condition end Condition

View file

@ -3,15 +3,13 @@ package gs.fizzbuzz
import cats.Applicative import cats.Applicative
import cats.Monoid import cats.Monoid
final class DefaultCondition[F[_]: Applicative, Input]( final class DefaultCondition[F[_]: Applicative, Input, Out](
val comparison: Input, val comparison: Input,
val output: Out,
val operator: Monoid[Input] val operator: Monoid[Input]
)( )(using CanEqual[Input, Input]) extends Condition[F, Input, Out]:
using
CanEqual[Input, Input]
) extends Condition[F, Input]:
override def matches(input: Input): F[Boolean] = override def matches(input: Input): F[Boolean] =
Applicative[F].pure( Applicative[F].pure(
operator.combine(input, comparison) == operator.empty operator.combine(input, comparison) == operator.empty
) )

View file

@ -1,21 +1,26 @@
package gs.fizzbuzz package gs.fizzbuzz
import cats.FlatMap
import cats.syntax.all.* import cats.syntax.all.*
import fs2.Pipe
import cats.FlatMap
class DefaultFizzBuzz[F[_]: FlatMap, Input]( class DefaultFizzBuzz[F[_]: FlatMap, Input, Out](
val fizzCondition: Condition[F, Input], val fizzCondition: Condition[F, Input, Out],
val buzzCondition: Condition[F, Input] val buzzCondition: Condition[F, Input, Out]
) extends FizzBuzz[F, Input]: ) extends FizzBuzz[F, Input, Out]:
def evaluate(input: Input): F[Result[Out]] =
def evaluate(input: Input): F[Result] =
for for
fizz <- fizzCondition.matches(input) fizz <- fizzCondition.matches(input)
buzz <- buzzCondition.matches(input) buzz <- buzzCondition.matches(input)
yield yield
if fizz && buzz then Result.FizzBuzz if fizz && buzz then
else if fizz then Result.Fizz Result.FizzBuzz(fizzCondition.output, buzzCondition.output)
else if buzz then Result.Buzz else if fizz then
else Result.Unmatched Result.Fizz(fizzCondition.output)
else if buzz then
Result.Buzz(buzzCondition.output)
else
Result.Unmatched
def pipe(): Pipe[F, Input, Result[Out]] = in => in.evalMap(evaluate)
end DefaultFizzBuzz end DefaultFizzBuzz

View file

@ -1,45 +1,8 @@
package gs.fizzbuzz package gs.fizzbuzz
import cats.Applicative
import cats.FlatMap
import fs2.Pipe import fs2.Pipe
trait FizzBuzz[F[_], Input]: trait FizzBuzz[F[_], Input, Out]:
def evaluate(input: Input): F[Result] def evaluate(input: Input): F[Result[Out]]
def pipe(): Pipe[F, Input, Result] = in => in.evalMap(evaluate) def pipe(): Pipe[F, Input, Result[Out]]
end FizzBuzz
object FizzBuzz:
def apply[F[_]: FlatMap, Input](
fizzCondition: Condition[F, Input],
buzzCondition: Condition[F, Input]
): FizzBuzz[F, Input] =
new DefaultFizzBuzz[F, Input](fizzCondition, buzzCondition)
def forInt[F[_]: FlatMap: Applicative](
fizz: Int,
buzz: Int,
f: (Int, Int) => Int
): FizzBuzz[F, Int] = new DefaultFizzBuzz[F, Int](
fizzCondition = Condition.forInt[F](fizz, f),
buzzCondition = Condition.forInt[F](buzz, f)
)
def forLong[F[_]: FlatMap: Applicative](
fizz: Long,
buzz: Long,
f: (Long, Long) => Long
): FizzBuzz[F, Long] = new DefaultFizzBuzz[F, Long](
fizzCondition = Condition.forLong[F](fizz, f),
buzzCondition = Condition.forLong[F](buzz, f)
)
def noop[F[_]: Applicative, Input]: FizzBuzz[F, Input] = new Noop[F, Input]
final class Noop[F[_]: Applicative, Input] extends FizzBuzz[F, Input]:
override def evaluate(input: Input): F[Result] =
Applicative[F].pure(Result.Unmatched)
end FizzBuzz end FizzBuzz

View file

@ -1,28 +1,42 @@
package gs.fizzbuzz package gs.fizzbuzz
import cats.effect.ExitCode import cats.Monoid
import cats.effect.IO import cats.effect.IO
import cats.effect.IOApp import cats.effect.IOApp
import cats.effect.ExitCode
object Main extends IOApp: object Main extends IOApp:
override def run(args: List[String]): IO[ExitCode] =
val op: Monoid[Long] = new Monoid[Long] {
override def empty: Long = 0L
override def combine(x: Long, y: Long): Long = x % y
}
override def run(args: List[String]): IO[ExitCode] = val fizzCond: Condition[IO, Long, String] = new DefaultCondition(
val fb = FizzBuzz.forLong[IO]( comparison = 3L,
fizz = 3, output = "Fizz",
buzz = 5, operator = op
f = (
x,
y
) => x % y
) )
for val buzzCond: Condition[IO, Long, String] = new DefaultCondition(
comparison = 5L,
output = "Buzz",
operator = op
)
val fb: FizzBuzz[IO, Long, String] = new DefaultFizzBuzz[IO, Long, String](
fizzCondition = fizzCond,
buzzCondition = buzzCond
)
for
ex1 <- fb.evaluate(15) ex1 <- fb.evaluate(15)
ex2 <- fb.evaluate(9) ex2 <- fb.evaluate(9)
ex3 <- fb.evaluate(20) ex3 <- fb.evaluate(20)
ex4 <- fb.evaluate(13) ex4 <- fb.evaluate(13)
_ <- IO.println(ex1) _ <- IO.println(ex1)
_ <- IO.println(ex2) _ <- IO.println(ex2)
_ <- IO.println(ex3) _ <- IO.println(ex3)
_ <- IO.println(ex4) _ <- IO.println(ex4)
yield ExitCode.Success yield
ExitCode.Success

View file

@ -1,12 +1,13 @@
package gs.fizzbuzz package gs.fizzbuzz
sealed trait Result sealed trait Result[+Out]
object Result: object Result:
case object Fizz extends Result case class Fizz[Out](fizz: Out) extends Result[Out]
case object Buzz extends Result case class Buzz[Out](buzz: Out) extends Result[Out]
case object FizzBuzz extends Result case class FizzBuzz[Out](fizz: Out, buzz: Out) extends Result[Out]
case object Unmatched extends Result case object Unmatched extends Result[Nothing]:
override def toString(): String = "unmatched"
end Result end Result