Compare commits
No commits in common. "ed12f5c442fb391cdfb72ba6cd27097b8cec539a" and "439c28c1c767304ee32afdcbfa67ea8a556fb69c" have entirely different histories.
ed12f5c442
...
439c28c1c7
6 changed files with 65 additions and 126 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue