Last active
August 8, 2018 03:38
-
-
Save xxxnell/34ec92b5ab216ca6d8d95d0ca81970a3 to your computer and use it in GitHub Desktop.
A solution of FizzBuzz test using a type class customized for Scala. For more detail, see https://blog.xxxnell.com/ko/posts/formalization (Korean).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Msg[+A] | |
trait EmptyMsg extends Msg[Nothing] | |
trait MsgOps extends MsgLaws { | |
def point[A](a: A): Msg[A] | |
def flatMap[A, B](msg: Msg[A], f: A => Msg[B]): Msg[B] | |
def show[A](msg: Msg[A]): String | |
} | |
trait MsgLaws { self: MsgOps => | |
def map[A, B](msg: Msg[A], f: A => B): Msg[B] = | |
flatMap(msg, (a: A) => point(f(a))) | |
def filter[A](msg: Msg[A], f: A => Boolean): Msg[A] = | |
flatMap(msg, (a: A) => if(f(a)) msg else Msg.empty) | |
def isEmpty[A](msg: Msg[A]): Boolean = | |
msg match { | |
case _: EmptyMsg => true | |
case _ => false | |
} | |
def orElse[A, B >: A](msg1: Msg[A], msg2: Msg[B]): Msg[B] = | |
if(!isEmpty(msg1)) msg1 else msg2 | |
} | |
trait MsgSyntax { | |
implicit class MsgSyntaxImpl[A](msg: Msg[A]) { | |
def flatMap[B](f: A => Msg[B]): Msg[B] = Msg.flatMap(msg, f) | |
def show: String = Msg.show(msg) | |
def map[B](f: A => B): Msg[B] = Msg.map(msg, f) | |
def filter(f: A => Boolean): Msg[A] = Msg.filter(msg, f) | |
def orElse[B >: A](msg2: Msg[B]): Msg[B] = Msg.orElse(msg, msg2) | |
} | |
} | |
object Msg extends MsgOps { | |
private case class MsgImpl[A](a: A) extends Msg[A] | |
private case object EmptyMsgImpl extends EmptyMsg | |
def apply[A](a: A): Msg[A] = MsgImpl(a) | |
def empty: EmptyMsg = EmptyMsgImpl | |
def point[A](a: A): Msg[A] = apply(a) | |
def flatMap[A, B](msg: Msg[A], f: A => Msg[B]): Msg[B] = | |
msg match { | |
case MsgImpl(a) => f(a) | |
case _ => empty | |
} | |
def show[A](msg: Msg[A]): String = | |
msg match { | |
case MsgImpl(a) => a.toString | |
case _ => "empty" | |
} | |
object syntax extends MsgSyntax | |
} | |
import Msg.syntax._ | |
val cd1: Int => Boolean = i => i % 3 == 0 | |
val cd2: Int => Boolean = i => i % 5 == 0 | |
val cd3: Int => Boolean = i => cd1(i) && cd2(i) | |
val tf1: Int => String = i => "Fizz" | |
val tf2: Int => String = i => "Buzz" | |
val tf3: Int => String = i => "FizzBuzz" | |
val inputs = (1 to 100).toList.map(raw => Msg(raw)) | |
val outputs = inputs | |
.map(i => | |
i.filter(cd3).map(tf3) orElse | |
i.filter(cd1).map(tf1) orElse | |
i.filter(cd2).map(tf2) orElse i) | |
.map(msg => msg.show) | |
println(outputs.mkString(", ")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment