Created
August 23, 2010 07:40
-
-
Save jrudolph/545019 to your computer and use it in GitHub Desktop.
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
// Tony Morris' Optional Exercises | |
// see http://scala-programming-language.1934581.n4.nabble.com/Further-understanding-scala-Option-td2334540.html | |
// Scala version 2.8.0.final | |
// http://scalacheck.googlecode.com/files/scalacheck_2.8.0-1.8-SNAPSHOT.jar | |
/* | |
Below are 15 exercises. The task is to emulate the scala.Option API | |
without using Some/None subtypes, but instead using a fold (called a | |
catamorphism). | |
A couple of functions are already done (map, get) | |
to be used as an example. ScalaCheck tests are given below to | |
verify the work. The desired result is to have all tests passing. | |
The 15th exercise is not available in the existing Scala API so | |
instructions are given in the comments. | |
Revision History | |
================ | |
23/08/2010 | |
Initial revision | |
---------------- | |
*/ | |
trait Optional[A] { | |
// single abstract method | |
def fold[X](some: A => X, none: => X): X | |
import Optional._ | |
// Done for you. | |
def map[B](f: A => B): Optional[B] = | |
fold(f andThen some, none[B]) | |
// Done for you. | |
// WARNING: undefined for None | |
def get: A = | |
fold(a => a, error("None.get")) | |
// Exercise 1 | |
def flatMap[B](f: A => Optional[B]): Optional[B] = | |
error("todo") | |
// Exercise 2 | |
// Rewrite map but use flatMap, not fold. | |
def mapAgain[B](f: A => B): Optional[B] = | |
error("todo") | |
// Exercise 3 | |
def getOrElse(e: => A): A = | |
error("todo") | |
// Exercise 4 | |
def filter(p: A => Boolean): Optional[A] = | |
error("todo") | |
// Exercise 5 | |
def exists(p: A => Boolean): Boolean = | |
error("todo") | |
// Exercise 6 | |
def forall(p: A => Boolean): Boolean = | |
error("todo") | |
// Exercise 7 | |
def foreach(f: A => Unit): Unit = | |
error("todo") | |
// Exercise 8 | |
def isDefined: Boolean = | |
error("todo") | |
// Exercise 9 | |
def isEmpty: Boolean = | |
error("todo") | |
// Exercise 10 | |
def orElse(o: Optional[A]): Optional[A] = | |
error("todo") | |
// Exercise 11 | |
def toLeft[X](right: => X): Either[A, X] = | |
error("todo") | |
// Exercise 12 | |
def toRight[X](left: => X): Either[X, A] = | |
error("todo") | |
// Exercise 13 | |
def toList: List[A] = | |
error("todo") | |
// Exercise 14 | |
def iterator: Iterator[A] = | |
error("todo") | |
// Exercise 15 The Clincher! | |
// Return a none value if either this or the argument is none. | |
// Otherwise apply the function to the argument in some. | |
// Don't be afraid to use functions you have written. | |
// Better style, more points! | |
def applic[B](f: Optional[A => B]): Optional[B] = | |
error("todo") | |
// Utility | |
def toOption: Option[A] = fold(Some(_), None) | |
} | |
object Optional { | |
// Done for you | |
def none[A]: Optional[A] = new Optional[A] { | |
def fold[X](some: A => X, none: => X) = none | |
} | |
// Done for you | |
def some[A](a: A): Optional[A] = new Optional[A] { | |
def fold[X](some: A => X, none: => X) = some(a) | |
} | |
// Utility | |
def fromOption[A](o: Option[A]): Optional[A] = o match { | |
case None => none | |
case Some(a) => some(a) | |
} | |
} | |
import org.scalacheck._ | |
import Arbitrary.arbitrary | |
import Prop._ | |
object TestOptional extends Properties("Optional"){ | |
import Optional._ | |
implicit def ArbitraryOptional[A](implicit a: Arbitrary[A]): Arbitrary[Optional[A]] = | |
Arbitrary(arbitrary[Option[A]] map fromOption) | |
property("map") = forAll ((o: Optional[Int], f: Int => String) => | |
(o map f).toOption == (o.toOption map f)) | |
property("get") = forAll((o: Optional[Int]) => | |
o.isDefined ==> | |
(o.get == o.toOption.get)) | |
property("flatMap") = forAll((o: Optional[Int], f: Int => Optional[String]) => | |
(o flatMap f).toOption == (o.toOption flatMap (f(_).toOption))) | |
property("mapAgain") = forAll ((o: Optional[Int], f: Int => String) => | |
(o mapAgain f).toOption == (o map f).toOption) | |
property("getOrElse") = forAll ((o: Optional[Int], n: Int) => | |
(o getOrElse n) == (o getOrElse n)) | |
property("filter") = forAll ((o: Optional[Int], f: Int => Boolean) => | |
(o filter f).toOption == (o.toOption filter f)) | |
property("exists") = forAll ((o: Optional[Int], f: Int => Boolean) => | |
(o exists f) == (o.toOption exists f)) | |
property("forall") = forAll ((o: Optional[Int], f: Int => Boolean) => | |
(o forall f) == (o.toOption forall f)) | |
property("foreach") = forAll ((o: Optional[Int], f: Int => Unit, n: Int) => { | |
var x: Int = n | |
var y: Int = x | |
o foreach (t => x = x + t) | |
o.toOption foreach (t => y = y + t) | |
x == y | |
}) | |
property("isDefined") = forAll ((o: Optional[Int]) => | |
(o.isDefined) == (o.toOption.isDefined)) | |
property("isEmpty") = forAll ((o: Optional[Int]) => | |
o.isEmpty == o.toOption.isEmpty) | |
property("orElse") = forAll ((o: Optional[Int], p: Optional[Int]) => | |
(o orElse p).toOption == (o.toOption orElse p.toOption)) | |
property("toLeft") = forAll ((o: Optional[Int], n: Int) => | |
(o toLeft n) == (o.toOption toLeft n)) | |
property("toRight") = forAll ((o: Optional[Int], n: Int) => | |
(o toRight n) == (o.toOption toRight n)) | |
property("toList") = forAll ((o: Optional[Int]) => | |
o.toList == o.toOption.toList) | |
property("iterator") = forAll ((o: Optional[Int]) => | |
o.iterator sameElements o.toOption.iterator) | |
// *** READ THIS COMMENT FIRST *** | |
// Note that scala.Option has no such equivalent to this method | |
// Therefore, reading this test may give away clues to how it might be solved. | |
// If you do not wish to spoil it, look away now and follow the | |
// instruction in the Exercise comment. | |
property("applic") = forAll ((o: Optional[Int => String], p: Optional[Int]) => | |
(p applic o).toOption == | |
(for(f <- o.toOption; | |
n <- p.toOption) | |
yield f(n))) | |
/* | |
$ wget http://scalacheck.googlecode.com/files/scalacheck_2.8.0-1.8-SNAPSHOT.jar | |
$ scalac -classpath scalacheck_2.8.0-1.8-SNAPSHOT.jar OptionalExercises.scala | |
$ scala -classpath .:scalacheck_2.8.0-1.8-SNAPSHOT.jar TestOptional | |
+ Optional.map: OK, passed 100 tests. | |
+ Optional.get: OK, passed 100 tests. | |
+ Optional.flatMap: OK, passed 100 tests. | |
+ Optional.mapAgain: OK, passed 100 tests. | |
+ Optional.getOrElse: OK, passed 100 tests. | |
+ Optional.filter: OK, passed 100 tests. | |
+ Optional.exists: OK, passed 100 tests. | |
+ Optional.forall: OK, passed 100 tests. | |
+ Optional.foreach: OK, passed 100 tests. | |
+ Optional.isDefined: OK, passed 100 tests. | |
+ Optional.isEmpty: OK, passed 100 tests. | |
+ Optional.orElse: OK, passed 100 tests. | |
+ Optional.toLeft: OK, passed 100 tests. | |
+ Optional.toRight: OK, passed 100 tests. | |
+ Optional.toList: OK, passed 100 tests. | |
+ Optional.iterator: OK, passed 100 tests. | |
+ Optional.applic: OK, passed 100 tests. | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment