Skip to content

Instantly share code, notes, and snippets.

@xxxnell
Last active August 8, 2018 03:38
Show Gist options
  • Save xxxnell/34ec92b5ab216ca6d8d95d0ca81970a3 to your computer and use it in GitHub Desktop.
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).
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