Skip to content

Instantly share code, notes, and snippets.

@drexin
Forked from tonymorris/RefactoringPuzzle.scala
Last active December 17, 2015 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drexin/5684773 to your computer and use it in GitHub Desktop.
Save drexin/5684773 to your computer and use it in GitHub Desktop.
object RefactorPuzzle {
case class IntRdr[+A](read: Int => A) {
def map[B](f: A => B): IntRdr[B] =
IntRdr(f compose read)
def flatMap[B](f: A => IntRdr[B]): IntRdr[B] =
IntRdr(n => f(read(n)).read(n))
}
object IntRdr {
def apply[A](a: A): IntRdr[A] =
IntRdr(_ => a)
}
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
trait Monad[M[_]] extends Functor[M] {
def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
def point[A](a: A): M[A]
}
implicit object OptionMonad extends Monad[Option] {
def map[A,B](x: Option[A])(f: A => B): Option[B] = x map f
def point[A](a: A): Option[A] = Some(a)
def flatMap[A,B](x: Option[A])(f: A => Option[B]) = x flatMap f
}
implicit object IntRdrMonad extends Monad[IntRdr] {
def map[A,B](x: IntRdr[A])(f: A => B): IntRdr[B] = x map f
def point[A](a: A): IntRdr[A] = IntRdr(a)
def flatMap[A,B](x: IntRdr[A])(f: A => IntRdr[B]) = x flatMap f
}
// Return all the Some values, or None if not all are Some.
def runOptions[A](x: List[Option[A]]): Option[List[A]] = run(x)
// Apply an Int to a list of int readers and return the list of return values.
def runIntRdrs[A](x: List[IntRdr[A]]): IntRdr[List[A]] = run(x)
def run[M[_], A](x: List[M[A]])(implicit monad: Monad[M]): M[List[A]] = {
import monad._
x.foldRight[M[List[A]]](point(Nil))((a, b) => flatMap(a)(aa => map(b)(aa :: _)))
}
/*
========================
THE REFACTORING PUZZLE
========================
The two functions `runOptions` and `runIntRdrs` share a lot of duplicated code.
This is demonstrated below.
How to refactor this code to remove the code duplication?
*/
//------- ------------- ------- -----------
//def runOptions[A](x: List[Option[A]]): Option[List[A]] =
//def runIntRdrs[A](x: List[IntRdr[A]]): IntRdr[List[A]] =
// ------------ ----------- -------------------------------------------------
// x.foldRight[Option[List[A]]](Option(Nil))((a, b) => a.flatMap(aa => b.map(aa :: _)))
// x.foldRight[IntRdr[List[A]]](IntRdr(Nil))((a, b) => a.flatMap(aa => b.map(aa :: _)))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment