Skip to content

Instantly share code, notes, and snippets.

@noelmarkham
Last active December 18, 2015 10:49
Show Gist options
  • Save noelmarkham/5771197 to your computer and use it in GitHub Desktop.
Save noelmarkham/5771197 to your computer and use it in GitHub Desktop.
My attempt at Tony Morris's refactoring puzzle - see http://tmorris.net/posts/refactoring-puzzle/index.html
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)
}
// Return all the Some values, or None if not all are Some.
def runOptions[A](x: List[Option[A]]): Option[List[A]] =
x.foldRight[Option[List[A]]](Option(Nil))((a, b) => a.flatMap(aa => b.map(aa :: _)))
// 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]] =
x.foldRight[IntRdr[List[A]]](IntRdr(Nil))((a, b) => a.flatMap(aa => b.map(aa :: _)))
// 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 :: _)))
//////////////////////////
// My refactoring below //
//////////////////////////
trait RefactorPuzzleMonad[C[_]] {
def point[A](a: A): C[A]
def flatMap[A, B](m: C[A])(f: A => C[B]): C[B]
def map[A, B](m: C[A])(f: A => B): C[B] = flatMap(m)(a => point(f(a)))
}
implicit val optMon: RefactorPuzzleMonad[Option] = new RefactorPuzzleMonad[Option] {
def point[A](a: A): Option[A] = Some(a)
def flatMap[A, B](m: Option[A])(f: (A) => Option[B]): Option[B] = m flatMap f
}
implicit val intRdrMon: RefactorPuzzleMonad[IntRdr] = new RefactorPuzzleMonad[IntRdr] {
def point[A](a: A): IntRdr[A] = IntRdr(a)
def flatMap[A, B](m: IntRdr[A])(f: (A) => IntRdr[B]): IntRdr[B] = m flatMap f
}
def runFoldR[A, C[_]](x: List[C[A]])(implicit m: RefactorPuzzleMonad[C]): C[List[A]] =
x.foldRight(m.point(Nil: List[A]))((elem, acc) => m.flatMap(elem)(aa => m.map(acc)(aa :: _)))
}
@etorreborre
Copy link

That works but it doesn't need to be a Monad. An Applicative is enough.

@noelmarkham
Copy link
Author

Updated following Tony's comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment