init
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -1,2 +1,18 @@
|
||||
*.class
|
||||
*.log
|
||||
lib_managed
|
||||
project/boot
|
||||
project/build/target
|
||||
scala-koans.iws
|
||||
scala-koans.ipr
|
||||
scala-koans.iml
|
||||
target
|
||||
.*\.DS_Store
|
||||
.*\.zip
|
||||
.idea*
|
||||
.ctags
|
||||
.vimrc
|
||||
tags
|
||||
.*\.swp
|
||||
project/plugins/target
|
||||
project/plugins/lib_managed
|
||||
project/plugins/src_managed
|
||||
.*\.orig
|
||||
|
||||
6
.hg_archival.txt
Normal file
6
.hg_archival.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
repo: e433542a2a0d9eb9fbe1b548ca5c76d54d12cb4e
|
||||
node: 769e22d30ec694417d909efedabe480b8c37d392
|
||||
branch: default
|
||||
latesttag: null
|
||||
latesttagdistance: 138
|
||||
changessincelatesttag: 143
|
||||
18
.hgignore
Normal file
18
.hgignore
Normal file
@@ -0,0 +1,18 @@
|
||||
lib_managed
|
||||
project/boot
|
||||
project/build/target
|
||||
scala-koans.iws
|
||||
scala-koans.ipr
|
||||
scala-koans.iml
|
||||
target
|
||||
.*\.DS_Store
|
||||
.*\.zip
|
||||
.idea*
|
||||
.ctags
|
||||
.vimrc
|
||||
tags
|
||||
.*\.swp
|
||||
project/plugins/target
|
||||
project/plugins/lib_managed
|
||||
project/plugins/src_managed
|
||||
.*\.orig
|
||||
30
ListOfKoans.txt
Normal file
30
ListOfKoans.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
AboutValAndVar.scala
|
||||
AboutConstructors.scala
|
||||
|
||||
AboutForExpressions.scala
|
||||
AboutNamedAndDefaultArguments.scala
|
||||
|
||||
AboutTuples.scala
|
||||
AboutLists.scala
|
||||
AboutMaps.scala
|
||||
AboutSets.scala
|
||||
AboutMutableMaps.scala
|
||||
AboutMutableSets.scala
|
||||
AboutSequencesAndArrays.scala
|
||||
|
||||
AboutPatternMatching.scala
|
||||
AboutCaseClasses.scala
|
||||
AboutOptions.scala
|
||||
AboutEmptyValues.scala
|
||||
AboutParentClasses.scala
|
||||
|
||||
AboutAccessModifiers.scala
|
||||
AboutImportsAndPackages.scala
|
||||
|
||||
AboutLazySequences.scala
|
||||
AboutHigherOrderFunctions.scala
|
||||
|
||||
AboutPreconditions.scala
|
||||
AboutTraits.scala
|
||||
AboutTypeSignatures.scala
|
||||
AboutUniformAccessPrinciple.scala
|
||||
1
README-scala.txt
Normal file
1
README-scala.txt
Normal file
@@ -0,0 +1 @@
|
||||
Please see http://bitbucket.org/dickwall/scala-koans/wiki/Home for more details on scala-koans
|
||||
7
build.sbt
Normal file
7
build.sbt
Normal file
@@ -0,0 +1,7 @@
|
||||
name := "Scala Koans"
|
||||
|
||||
version := "1.0"
|
||||
|
||||
scalaVersion := "2.9.0-1"
|
||||
|
||||
libraryDependencies ++= Seq("junit" % "junit" % "4.8" % "test", "org.scalatest" % "scalatest_2.9.0" % "1.6.1" % "test")
|
||||
51
ideaboard.txt
Normal file
51
ideaboard.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
Putting these in order because some koan packages are prerequisites of others.
|
||||
Feel free to put it in logical order so that
|
||||
|
||||
|
||||
Test asserts - Message, bool, etc. (DMarsh: done)
|
||||
Nothing values (DMarsh: done)
|
||||
uniform access principle (DMarsh: Done)
|
||||
named/default args (DMarsh: done)
|
||||
Tuples - syntax (DMarsh: done)
|
||||
Pattern Matching (DMarsh: done ?? Should we add more)
|
||||
Preconditions (DMarsh: Done)
|
||||
Functions returning functions
|
||||
Functions taking functions
|
||||
if expressions
|
||||
for comprehensions (DMarsh: done)
|
||||
imports, packages, and visibility (dhinojosa:In Process)
|
||||
classes (dhinojosa:In Process)
|
||||
mutation
|
||||
lists / cons
|
||||
collections array, list (DMarsh: Done, also maps and mutable maps, sets and mutable sets)
|
||||
map, reduce, filter (DMarsh: Done ?? covered in various collection classes)
|
||||
immutability/side effects (DMarsh: done ?? Do we have enough here??)
|
||||
lazy sequences (Nilanjan)
|
||||
recursion
|
||||
currying / pfa
|
||||
case classes (DMarsh: Done)
|
||||
Pattern Matching
|
||||
traits / mixins (dhinojosa: In Process)
|
||||
type signatures (dhinojosa: In Process)
|
||||
state identity lifetime
|
||||
memoization
|
||||
recursive list processing
|
||||
lambda
|
||||
null option types (Nilanjan)
|
||||
reflection
|
||||
scala properties - set date / use earlier date
|
||||
|
||||
|
||||
Make sure this is covered:
|
||||
scala> def pointit() = 123
|
||||
pointit: ()Int
|
||||
|
||||
scala> () => pointit()
|
||||
res2: () => Int = <function0>
|
||||
|
||||
scala> res2.apply
|
||||
res4: Int = 123
|
||||
|
||||
|
||||
|
||||
|
||||
13
project/plugins/build.sbt
Normal file
13
project/plugins/build.sbt
Normal file
@@ -0,0 +1,13 @@
|
||||
resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/"
|
||||
|
||||
libraryDependencies += "com.github.mpeltonen" %% "sbt-idea" % "0.10.0"
|
||||
|
||||
resolvers += {
|
||||
val typesafeRepoUrl = new java.net.URL("http://repo.typesafe.com/typesafe/releases")
|
||||
val pattern = Patterns(false, "[organisation]/[module]/[sbtversion]/[revision]/[type]s/[module](-[classifier])-[revision].[ext]")
|
||||
Resolver.url("Typesafe Repository", typesafeRepoUrl)(pattern)
|
||||
}
|
||||
|
||||
libraryDependencies <<= (libraryDependencies, sbtVersion) { (deps, version) =>
|
||||
deps :+ ("com.typesafe.sbteclipse" %% "sbteclipse" % "1.3-RC1" extra("sbtversion" -> version))
|
||||
}
|
||||
3
sbt-launch.jar
Normal file
3
sbt-launch.jar
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b8af85f4ecc3d07a04fb35a9e056a95de2dd9fe260a1551ba9aca5d50b8d1010
|
||||
size 937683
|
||||
2
sbt.bat
Normal file
2
sbt.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
set SCRIPT_DIR=%~dp0
|
||||
java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
|
||||
9
src/main/scala/org/functionalkoans/App.scala
Normal file
9
src/main/scala/org/functionalkoans/App.scala
Normal file
@@ -0,0 +1,9 @@
|
||||
package org.functionalkoans
|
||||
|
||||
/**
|
||||
* Hello world!
|
||||
*
|
||||
*/
|
||||
object App extends scala.App {
|
||||
println( "Hello World!" )
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.functionalkoans.forscala;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 5/11/11
|
||||
* Time: 10:55 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
public class SomeJavaClass {
|
||||
public int findSizeOfRawType(List list) {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
public int findSizeOfUnknownType(List<?> list) {
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
43
src/test/resources/TODO.txt
Normal file
43
src/test/resources/TODO.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
type members
|
||||
self types
|
||||
apply/unapply extractor methods
|
||||
equality
|
||||
case class inheritance
|
||||
objects?
|
||||
inner classes
|
||||
while if control structures
|
||||
for loop and for loop comprehension
|
||||
DSLs
|
||||
putting a method in to a function
|
||||
CanBuildFrom newBuilder
|
||||
@bridge @tailrec and other notations
|
||||
sealed traits and object
|
||||
associativity? is that covered? I believe it was.
|
||||
uses of with keyword there are some various reasons to use em.
|
||||
new Object with {}
|
||||
structural types
|
||||
existential
|
||||
higher kinded
|
||||
|
||||
def (x : => Int) = x()
|
||||
|
||||
shorthand implicit def
|
||||
|
||||
class A {
|
||||
type Abstract
|
||||
def method: Abstract
|
||||
}
|
||||
class B extends A {
|
||||
type Abstract <: Seq[Int] // still abstract but implementation of method must return a Seq[Int] or subtype
|
||||
}
|
||||
class C extends A {
|
||||
type Abstract = List[Int]
|
||||
def method = Nil
|
||||
}
|
||||
|
||||
object OtherUses {
|
||||
type X = java.io.File
|
||||
println(new X("/"))
|
||||
type Y[T] = Se1q[T] // Y[Int] = Seq[Int], Y[String] = Seq[String], etc.
|
||||
type FromStringTo[T] = String=>T // FromStringTo[Int] = String=>Int; FromStringToT[_] = String => _
|
||||
def methodWithContextBound[A : FromStringTo](x: A) = null // there must a an implicit String=>A in scope
|
||||
@@ -0,0 +1,112 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutAbstractTypes extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("A type can be declared anywhere") {
|
||||
type G = Int
|
||||
val x: G = 3
|
||||
val y: G = 5
|
||||
(x + y) should be(8)
|
||||
}
|
||||
|
||||
koan("""Abstract types are types that are declared in an abstract class, and
|
||||
| since it is in abstract class they don't need to be defined until
|
||||
| they are extended.""") {
|
||||
|
||||
abstract class Generator {
|
||||
type F
|
||||
|
||||
def generate: F
|
||||
}
|
||||
|
||||
class StringGenerator extends Generator {
|
||||
type F = String
|
||||
|
||||
def generate = "Scooters are fun"
|
||||
}
|
||||
|
||||
new StringGenerator().generate should be("Scooters are fun")
|
||||
}
|
||||
|
||||
koan("Abstract types can also be included in a trait") {
|
||||
trait Counter {
|
||||
type C
|
||||
def count(c: C): Int
|
||||
}
|
||||
|
||||
class CharacterCounter extends Counter {
|
||||
type C = String
|
||||
override def count(c: String) = c.size
|
||||
}
|
||||
|
||||
new CharacterCounter().count("Boolean") should be(7)
|
||||
}
|
||||
|
||||
koan("""Abstract types are also by default public, the can be locked down with an
|
||||
| access modifier""") {
|
||||
trait Counter {
|
||||
protected type C
|
||||
def count(c: C): Int
|
||||
}
|
||||
|
||||
class CharacterCounter extends Counter {
|
||||
protected type C = String
|
||||
override def count(c: String) = c.size
|
||||
}
|
||||
|
||||
new CharacterCounter().count("Awesome") should be(7)
|
||||
}
|
||||
|
||||
|
||||
koan("""Perhaps the best reason for abstract types is the
|
||||
| ability to refine types so it can make sense. The example often used
|
||||
| is creating an Animal class, and refining the
|
||||
| type of Food that can be eaten. This koan will also show use of a type bound
|
||||
| as an abstract type""") {
|
||||
|
||||
trait Food
|
||||
class DogFood extends Food {
|
||||
override def toString = "Dog Food"
|
||||
}
|
||||
class Hay extends Food {
|
||||
override def toString = "Hay"
|
||||
}
|
||||
|
||||
trait Animal {
|
||||
type C <: Food //Animal has to eat some sort of food, depends on animal
|
||||
def feedMe(c:C)
|
||||
}
|
||||
|
||||
class Cow {
|
||||
type C = Hay //A good choice
|
||||
def feedMe(c:C) = "Nom Nom, I am eating " + c
|
||||
}
|
||||
|
||||
class Dog {
|
||||
type C = DogFood //Again, a good choice
|
||||
def feedMe(c:C) = "Nom Nom, I am eating " + c
|
||||
}
|
||||
|
||||
//new Cow().feedMe(new DogFood) this wont compile, because it's not right
|
||||
new Cow().feedMe(new Hay()) should be ("Nom Nom, I am eating Hay")
|
||||
}
|
||||
|
||||
koan("""Abstract Types can be any type even a type with type parameter""") {
|
||||
trait Counter {
|
||||
type T <: Traversable[_] //The _ means it is existential, in this case
|
||||
//I don't care what kind of Traversable it is.
|
||||
def count(t:T) = t.size //I know it's a Traversable but I'll define later which one.
|
||||
}
|
||||
|
||||
class IntListCounter extends Counter {
|
||||
type T = List[Int]
|
||||
override def count(t:T) = t.sum //overriding implementation; Sum is
|
||||
//only available with Numeric types
|
||||
}
|
||||
|
||||
new IntListCounter().count(List(1,2,3,4)) should be (10)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
package harkonnen {
|
||||
|
||||
class AlphaBase extends KoanSuite with ShouldMatchers {
|
||||
private val melange = 1
|
||||
private[this] val oregano = 2
|
||||
private[AlphaBase] val tarragon = 3
|
||||
private[harkonnen] val rosemary = 4
|
||||
|
||||
val parsley = 5
|
||||
// public val ginger = 6 //does not exist, in case you were wondering
|
||||
// public[this] val lemongrass = 7 //does not exist, in case you were wondering
|
||||
// public[AlphaBase] val carob = 8 //does not exist, in case you were wondering
|
||||
// public[har] val celerySeed = 9 //does not exist, in case you were wondering
|
||||
|
||||
protected val sage = 10
|
||||
protected[this] val paprika = 11
|
||||
protected[AlphaBase] val saffron = 12
|
||||
protected[harkonnen] val thyme = 13
|
||||
}
|
||||
|
||||
|
||||
class BetaBase extends AlphaBase with KoanSuite with ShouldMatchers {
|
||||
val param: AlphaBase = new AlphaBase
|
||||
|
||||
koan("With private keywords: Only private[packagename] members can be accessed via inheritance") {
|
||||
//melange should be(1) //not accessible
|
||||
//oregano should be (2) //not accessible
|
||||
//tarragon should be (3) //not accessible
|
||||
rosemary should be(4)
|
||||
}
|
||||
|
||||
koan("With private keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
//param.melange should be (1) //not accessible
|
||||
//param.oregano should be (2) //not accessible
|
||||
//param.tarragon should be (3) //not accessible
|
||||
param.rosemary should be(4)
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed through inheritance") {
|
||||
parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed can be accessed via parameter") {
|
||||
param.parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With protected keywords: All members can be accessed via inheritance") {
|
||||
sage should be(10)
|
||||
paprika should be(11) //not accessible
|
||||
saffron should be(12) //not accessible
|
||||
thyme should be(13)
|
||||
}
|
||||
|
||||
koan("With protected keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
//param.sage should be (10) //not accessible
|
||||
//param.paprika should be (11) //not accessible
|
||||
//param.saffron should be (12) //not accessible
|
||||
param.thyme should be(13)
|
||||
}
|
||||
}
|
||||
|
||||
class GammaBase extends KoanSuite with ShouldMatchers {
|
||||
|
||||
val param: AlphaBase = new AlphaBase
|
||||
|
||||
koan("With private keywords: No members can be accessed via inheritance") {
|
||||
//melange should be(1) //not accessible
|
||||
//oregano should be (2) //not accessible
|
||||
//tarragon should be (3) //not accessible
|
||||
//rosemary should be(4) //not accessible
|
||||
}
|
||||
|
||||
koan("With private keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
//param.melange should be (1) //not accessible
|
||||
//param.oregano should be (2) //not accessible
|
||||
//param.tarragon should be (3) //not accessible
|
||||
param.rosemary should be(4)
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed through inheritance") {
|
||||
//parsley should be(5) //not accessible
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed can be accessed via parameter") {
|
||||
param.parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With protected keywords: All members can be accessed via inheritance") {
|
||||
//sage should be (10)
|
||||
//paprika should be (11) //not accessible
|
||||
//saffron should be (12) //not accessible
|
||||
//thyme should be (13)
|
||||
}
|
||||
|
||||
koan("With protected keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
// param.sage should be (10) //not accessible
|
||||
// param.paprika should be (11) //not accessible
|
||||
// param.saffron should be (12) //not accessible
|
||||
param.thyme should be(13)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package atreides {
|
||||
|
||||
import org.functionalkoans.forscala.harkonnen.AlphaBase
|
||||
|
||||
class DeltaBase extends AlphaBase with KoanSuite with ShouldMatchers {
|
||||
val param: AlphaBase = new AlphaBase
|
||||
|
||||
koan("With private keywords: Only private and private[packagename] members can be accessed via inheritance") {
|
||||
//melange should be (1)
|
||||
//oregano should be (2) //not accessible
|
||||
//tarragon should be (3) //not accessible
|
||||
//rosemary should be (4)
|
||||
}
|
||||
|
||||
koan("With private keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
// param.melange should be (1) //not accessible
|
||||
// param.oregano should be (2) //not accessible
|
||||
// param.tarragon should be (3) //not accessible
|
||||
// param.rosemary should be(4) //not accessible
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed through inheritance") {
|
||||
parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed can be accessed via parameter") {
|
||||
param.parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With protected keywords: All members can be accessed via inheritance") {
|
||||
sage should be(10)
|
||||
paprika should be(11) //not accessible
|
||||
saffron should be(12) //not accessible
|
||||
thyme should be(13)
|
||||
}
|
||||
|
||||
koan("With protected keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
//param.sage should be (10) //not accessible
|
||||
//param.paprika should be (11) //not accessible
|
||||
//param.saffron should be (12) //not accessible
|
||||
//param.thyme should be (13)
|
||||
}
|
||||
}
|
||||
|
||||
class EpsilonBase extends KoanSuite with ShouldMatchers {
|
||||
val param: AlphaBase = new AlphaBase
|
||||
|
||||
koan("With private keywords: Only private and private[packagename] members can be accessed via inheritance") {
|
||||
// melange should be (1) //not accessible
|
||||
// oregano should be (2) //not accessible
|
||||
// tarragon should be (3) //not accessible
|
||||
// rosemary should be (4) //not accessible
|
||||
}
|
||||
|
||||
koan("With private keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
// param.melange should be (1) //not accessible
|
||||
// param.oregano should be (2) //not accessible
|
||||
// param.tarragon should be (3) //not accessible
|
||||
// param.rosemary should be(4) //not accessible
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed through inheritance") {
|
||||
// parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With public keywords: All members can be accessed can be accessed via parameter") {
|
||||
param.parsley should be(5)
|
||||
}
|
||||
|
||||
koan("With protected keywords: All members can be accessed via inheritance") {
|
||||
// sage should be (10)
|
||||
// paprika should be (11) //not accessible
|
||||
// saffron should be (12) //not accessible
|
||||
// thyme should be (13)
|
||||
}
|
||||
|
||||
koan("With protected keywords: Only private[packagename] members can be accessed via parameter") {
|
||||
// param.sage should be (10) //not accessible
|
||||
// param.paprika should be (11) //not accessible
|
||||
// param.saffron should be (12) //not accessible
|
||||
// param.thyme should be (13)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AboutAccessModifiers extends KoanSuite with ShouldMatchers {
|
||||
|
||||
}
|
||||
131
src/test/scala/org/functionalkoans/forscala/AboutActors.scala
Normal file
131
src/test/scala/org/functionalkoans/forscala/AboutActors.scala
Normal file
@@ -0,0 +1,131 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import actors.Actor
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 4/27/11
|
||||
* Time: 12:45 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
class AboutActors extends KoanSuite with ShouldMatchers {
|
||||
koan("Basic Actor that extends Actor, this will ben invoked in separate thread") {
|
||||
import actors.Actor
|
||||
class AbrahamLincoln extends Actor {
|
||||
def act() {
|
||||
println("Four score and seven years ago.")
|
||||
}
|
||||
}
|
||||
|
||||
val abe = new AbrahamLincoln
|
||||
abe.start()
|
||||
}
|
||||
|
||||
koan("Basic anonymous actor") {
|
||||
import actors.Actor._
|
||||
val jfk = actor {
|
||||
println("Ask not what your country can do for you")
|
||||
}
|
||||
}
|
||||
|
||||
koan("""Messages can be sent to actors. The ! calls are inspired by Erlang
|
||||
| React method returns no result""") {
|
||||
import actors.Actor._
|
||||
val guessNumber = actor {
|
||||
loop {
|
||||
react {
|
||||
case i: Int if (i > 64) => println("Too high")
|
||||
case i: Int if (i < 64) => println("Too low")
|
||||
case i: Int if (i == 64) => {
|
||||
println("Ding!")
|
||||
exit('successful)
|
||||
}
|
||||
case _ => println("You gave me something wrong")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guessNumber ! 20
|
||||
guessNumber ! 23
|
||||
guessNumber ! 90
|
||||
guessNumber ! 75
|
||||
guessNumber ! 70
|
||||
guessNumber ! 61
|
||||
guessNumber ! "Boing"
|
||||
guessNumber ! 65.34
|
||||
guessNumber ! 64
|
||||
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
koan("""case _ => is used as a catch all, if you do not adequately cover all possible scenarios, messages
|
||||
will be held in an actors mail box.""") {
|
||||
println("---------")
|
||||
import actors.Actor._
|
||||
val guessNumber = actor {
|
||||
loop {
|
||||
react {
|
||||
case i: Int if (i > 64) => println("Too high")
|
||||
case i: Int if (i < 64) => println("Too low")
|
||||
case i: Int if (i == 64) => {
|
||||
println("Ding!")
|
||||
println(mailboxSize + " messages have not been matched")
|
||||
exit('successful)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guessNumber ! 20
|
||||
guessNumber ! 23
|
||||
guessNumber ! "Boing"
|
||||
guessNumber ! 90
|
||||
guessNumber ! 75
|
||||
guessNumber ! 70
|
||||
guessNumber ! 61
|
||||
guessNumber ! 64
|
||||
guessNumber ! "Boom"
|
||||
guessNumber ! 65.34
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
koan("""Up until now we have been Actors the way it wasn't intended. Actors are intended
|
||||
to communicate back and forth by message passing using the ! operator.
|
||||
self is to reference the current Actor.""") {
|
||||
|
||||
println("---------")
|
||||
import actors.Actor._
|
||||
val guessNumber = actor {
|
||||
loop {
|
||||
react {
|
||||
case (i: Int, caller: Actor) if (i > 64) => caller ! "Too High"
|
||||
case (i: Int, caller: Actor) if (i < 64) => caller ! "Too Low"
|
||||
case (i: Int, caller: Actor) if (i == 64) => {
|
||||
caller ! "Ding"
|
||||
exit('successful)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val items = List(20, 23, "Boing", 90, 75, 70, 61, 64, "Boom", 65.34)
|
||||
items.foreach {
|
||||
x =>
|
||||
guessNumber ! (x, self)
|
||||
self.receiveWithin(100) {
|
||||
case "Too High" => println("Too High")
|
||||
case "Too Low" => println("Too Low")
|
||||
case "Ding" => println("Just Right")
|
||||
case _ => println("Timed Out")
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(1000) //Wait until all of it is completely done.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.BlankValues.__
|
||||
import support.Blankout.blank
|
||||
import support.KoanSuite
|
||||
|
||||
// meditate on AboutAsserts to see how the Scala Koans work
|
||||
class AboutAsserts extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("asserts can take a boolean argument") {
|
||||
assert(true) // should be true
|
||||
}
|
||||
|
||||
koan("asserts can include a message") {
|
||||
assert(true, "This should be true")
|
||||
}
|
||||
|
||||
koan("true and false values can be compared with should matchers") {
|
||||
true should be(true) // should be true
|
||||
}
|
||||
|
||||
koan("booleans in asserts can test equality") {
|
||||
val v1 = 4
|
||||
val v2 = 4
|
||||
assert(v1 === v2)
|
||||
}
|
||||
|
||||
koan("sometimes we expect you to fill in the values") {
|
||||
assert(2 == 1 + 1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutCaseClasses extends KoanSuite with ShouldMatchers {
|
||||
|
||||
// case classes are very convenient, they give you a lot for free. The following Koans will
|
||||
// help you understand some of the conveniences. Case classes are also an integral part of
|
||||
// pattern matching which are the subject of separate koan
|
||||
|
||||
koan("Case classes have an automatic equals method that works") {
|
||||
case class Person(first: String, last: String)
|
||||
|
||||
val p1 = new Person("Fred", "Jones")
|
||||
val p2 = new Person("Shaggy", "Rogers")
|
||||
val p3 = new Person("Fred", "Jones")
|
||||
|
||||
(p1 == p2) should be(false)
|
||||
(p1 == p3) should be(true)
|
||||
|
||||
(p1 eq p2) should be(false)
|
||||
(p1 eq p3) should be(false) // not identical, merely equal
|
||||
}
|
||||
|
||||
koan("Case classes have an automatic hashcode method that works") {
|
||||
case class Person(first: String, last: String)
|
||||
|
||||
val p1 = new Person("Fred", "Jones")
|
||||
val p2 = new Person("Shaggy", "Rogers")
|
||||
val p3 = new Person("Fred", "Jones")
|
||||
|
||||
(p1.hashCode == p2.hashCode) should be(false)
|
||||
(p1.hashCode == p3.hashCode) should be(true)
|
||||
}
|
||||
|
||||
koan("Case classes have a convenient way they can be created") {
|
||||
case class Dog(name: String, breed: String)
|
||||
|
||||
val d1 = Dog("Scooby", "Doberman")
|
||||
val d2 = Dog("Rex", "Custom")
|
||||
val d3 = new Dog("Scooby", "Doberman") // the old way of creating using new
|
||||
|
||||
(d1 == d3) should be(true)
|
||||
(d1 == d2) should be(false)
|
||||
(d2 == d3) should be(false)
|
||||
}
|
||||
|
||||
koan("Case classes have a convenient toString method defined") {
|
||||
case class Dog(name: String, breed: String)
|
||||
val d1 = Dog("Scooby", "Doberman")
|
||||
d1.toString should be("Dog(Scooby,Doberman)")
|
||||
}
|
||||
|
||||
koan("Case classes have automatic properties") {
|
||||
case class Dog(name: String, breed: String)
|
||||
|
||||
val d1 = Dog("Scooby", "Doberman")
|
||||
d1.name should be("Scooby")
|
||||
d1.breed should be("Doberman")
|
||||
|
||||
// what happens if you uncomment the line below? Why?
|
||||
//d1.name = "Scooby Doo"
|
||||
}
|
||||
|
||||
koan("Case classes can have mutable properties") {
|
||||
case class Dog(var name: String, breed: String) // you can rename a dog, but change its breed? nah!
|
||||
val d1 = Dog("Scooby", "Doberman")
|
||||
|
||||
d1.name should be("Scooby")
|
||||
d1.breed should be("Doberman")
|
||||
|
||||
d1.name = "Scooby Doo" // but is it a good idea?
|
||||
|
||||
d1.name should be("Scooby Doo")
|
||||
d1.breed should be("Doberman")
|
||||
}
|
||||
|
||||
koan("Safer alternatives exist for altering case classes") {
|
||||
case class Dog(name: String, breed: String) // Doberman
|
||||
|
||||
val d1 = Dog("Scooby", "Doberman")
|
||||
|
||||
val d2 = d1.copy(name = "Scooby Doo") // copy the case class but change the name in the copy
|
||||
|
||||
d1.name should be("Scooby") // original left alone
|
||||
d1.breed should be("Doberman")
|
||||
|
||||
d2.name should be("Scooby Doo")
|
||||
d2.breed should be("Doberman") // copied from the original
|
||||
}
|
||||
|
||||
// case class has to be defined outside of the test for this one
|
||||
case class Person(first: String, last: String, age: Int = 0, ssn: String = "")
|
||||
|
||||
koan("Case classes have default and named parameters") {
|
||||
|
||||
val p1 = Person("Fred", "Jones", 23, "111-22-3333")
|
||||
val p2 = Person("Samantha", "Jones") // note missing age and ssn
|
||||
val p3 = Person(last = "Jones", first = "Fred", ssn = "111-22-3333") // note the order can change, and missing age
|
||||
val p4 = p3.copy(age = 23)
|
||||
|
||||
p1.first should be("Fred")
|
||||
p1.last should be("Jones")
|
||||
p1.age should be(23)
|
||||
p1.ssn should be("111-22-3333")
|
||||
|
||||
p2.first should be("Samantha")
|
||||
p2.last should be("Jones")
|
||||
p2.age should be(0)
|
||||
p2.ssn should be("")
|
||||
|
||||
p3.first should be("Fred")
|
||||
p3.last should be("Jones")
|
||||
p3.age should be(0)
|
||||
p3.ssn should be("111-22-3333")
|
||||
|
||||
(p1 == p4) should be(true)
|
||||
}
|
||||
|
||||
koan("Case classes can be disassembled to their constituent parts as a tuple") {
|
||||
val p1 = Person("Fred", "Jones", 23, "111-22-3333")
|
||||
|
||||
val parts = Person.unapply(p1).get // this seems weird, but it's critical to other features of Scala
|
||||
|
||||
parts._1 should be("Fred")
|
||||
parts._2 should be("Jones")
|
||||
parts._3 should be(23)
|
||||
parts._4 should be("111-22-3333")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.BlankValues.__
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutClasses extends KoanSuite with ShouldMatchers {
|
||||
|
||||
|
||||
// you can define class with var or val parameters
|
||||
class ClassWithVarParameter(var description: String) {
|
||||
|
||||
}
|
||||
|
||||
koan("val parameters in class definition define getter") {
|
||||
val aClass = new ClassWithValParameter("name goes here")
|
||||
aClass.name should be("name goes here");
|
||||
}
|
||||
|
||||
class ClassWithValParameter(val name: String) {
|
||||
|
||||
}
|
||||
|
||||
koan("var parameters in class definition define getter and setter") {
|
||||
val aClass = new ClassWithVarParameter("description goes here")
|
||||
aClass.description should be("description goes here");
|
||||
|
||||
aClass.description = "new description"
|
||||
aClass.description should be("new description")
|
||||
}
|
||||
|
||||
// you can define class with private fields
|
||||
class ClassWithPrivateFields(name: String) {
|
||||
|
||||
}
|
||||
|
||||
koan("fields defined internally are private to class") {
|
||||
val aClass = new ClassWithPrivateFields("name")
|
||||
|
||||
// NOTE: aClass.name is not accessible
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutConstructors extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class AboutConstructorWithValParameter(val name: String) {
|
||||
// invoke auxilary constructor
|
||||
def this() {
|
||||
// what happens if you comment out the following line?
|
||||
this ("defaultname")
|
||||
}
|
||||
}
|
||||
|
||||
class AboutClassWithNoClassParameter {
|
||||
}
|
||||
|
||||
class AboutConstructorWithVarParameter(var name: String) {
|
||||
}
|
||||
|
||||
class AboutConstructorWithPrivateClassParameter(name: String) {
|
||||
}
|
||||
|
||||
koan("val in class definition defines read only property") {
|
||||
val aboutMe = new AboutConstructorWithValParameter("MyName")
|
||||
aboutMe.name should be("MyName")
|
||||
}
|
||||
|
||||
koan("var in class definition defines read/write parameters") {
|
||||
val aboutMe = new AboutConstructorWithVarParameter("MyName")
|
||||
aboutMe.name = "YourName"
|
||||
aboutMe.name should be("YourName")
|
||||
}
|
||||
|
||||
koan("private member data is not accessible") {
|
||||
val aboutMe = new AboutConstructorWithPrivateClassParameter("MyName")
|
||||
|
||||
// what happens if you uncomment this line? why?
|
||||
// aboutMe.name = "Me"
|
||||
}
|
||||
|
||||
koan("Primary constructor specified with a parameter requires that parameter to be passed in") {
|
||||
// val aboutMe = new AboutConstructorWithValParameter()
|
||||
}
|
||||
|
||||
koan("Class with no class parameters is called with no arguments") {
|
||||
// add parameter to make this fail
|
||||
val aboutMe = new AboutClassWithNoClassParameter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
|
||||
class AboutEmptyValues extends KoanSuite with ShouldMatchers {
|
||||
|
||||
test("None equals None") {
|
||||
assert(None === None)
|
||||
}
|
||||
|
||||
test("None should be identical to None") {
|
||||
val a = None
|
||||
assert(a eq None) // note that eq denotes identity, and == denotes equality in Scala
|
||||
}
|
||||
|
||||
test("None can be converted to a String") {
|
||||
assert(None.toString === "None")
|
||||
}
|
||||
|
||||
test("An empty list can be represented by another nothing value: Nil") {
|
||||
assert(List() === Nil)
|
||||
}
|
||||
|
||||
test("None can be converted to an empty list") {
|
||||
val a = None
|
||||
assert(a.toList === Nil)
|
||||
}
|
||||
|
||||
test("None is considered empty") {
|
||||
assert(None.isEmpty === true)
|
||||
}
|
||||
|
||||
/*test ("None can be cast Any, AnyRef or AnyVal") {
|
||||
assert(None.asInstanceOf[Any] === __)
|
||||
assert(None.asInstanceOf[AnyRef] === __)
|
||||
assert(None.asInstanceOf[AnyVal] === __)
|
||||
} */
|
||||
|
||||
test("None cannot be cast to all types of objects") {
|
||||
intercept[ClassCastException] {
|
||||
// put the exception you expect to see in place of the blank
|
||||
assert(None.asInstanceOf[String] === classOf[ClassCastException])
|
||||
}
|
||||
}
|
||||
|
||||
test("None can be used with Option instead of null references") {
|
||||
val optional: Option[String] = None
|
||||
assert(optional.isEmpty === true)
|
||||
assert(optional === None)
|
||||
}
|
||||
|
||||
test("Some is the opposite of None for Option types") {
|
||||
val optional: Option[String] = Some("Some Value")
|
||||
assert((optional == None) === false, "Some(value) should not equal None")
|
||||
assert(optional.isEmpty === false, "Some(value) should not be empty")
|
||||
}
|
||||
|
||||
test("Option.getOrElse can be used to provide a default in the case of None") {
|
||||
val optional: Option[String] = Some("Some Value")
|
||||
val optional2: Option[String] = None
|
||||
assert(optional.getOrElse("No Value") === "Some Value", "Should return the value in the option")
|
||||
assert(optional2.getOrElse("No Value") === "No Value", "Should return the specified default value")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
|
||||
class AboutEnumerations extends KoanSuite with ShouldMatchers {
|
||||
|
||||
// To create an enumeration, create an object that extends the abstract class Enumeration,
|
||||
// and set a val variable to the method Value. This is a trick to give values to each val."
|
||||
koan("Value assigns a numerical value to fields") {
|
||||
|
||||
object Planets extends Enumeration {
|
||||
val Mercury = Value
|
||||
val Venus = Value
|
||||
val Earth = Value
|
||||
val Mars = Value
|
||||
val Jupiter = Value
|
||||
val Saturn = Value
|
||||
val Uranus = Value
|
||||
val Neptune = Value
|
||||
val Pluto = Value
|
||||
}
|
||||
|
||||
Planets.Mercury.id should be(0)
|
||||
Planets.Venus.id should be(1)
|
||||
|
||||
Planets.Mercury.toString should be("Mercury") //How does it get the name? by Reflection.
|
||||
Planets.Venus.toString should be("Venus")
|
||||
|
||||
(Planets.Earth == Planets.Earth) should be(true)
|
||||
(Planets.Neptune == Planets.Jupiter) should be(false)
|
||||
}
|
||||
|
||||
// You can create an enumeration with your own index and your own Strings, in this koan,
|
||||
// we will start with an index of one and use Greek names instead of Roman
|
||||
koan("Enumerations can set their own index and name") {
|
||||
object GreekPlanets extends Enumeration {
|
||||
|
||||
val Mercury = Value(1, "Hermes")
|
||||
val Venus = Value(2, "Aphrodite")
|
||||
//FYI: Tellus is Roman for (Mother) Earth
|
||||
val Earth = Value(3, "Gaia")
|
||||
val Mars = Value(4, "Ares")
|
||||
val Jupiter = Value(5, "Zeus")
|
||||
val Saturn = Value(6, "Cronus")
|
||||
val Uranus = Value(7, "Ouranus")
|
||||
val Neptune = Value(8, "Poseidon")
|
||||
val Pluto = Value(9, "Hades")
|
||||
}
|
||||
|
||||
GreekPlanets.Mercury.id should be(1)
|
||||
GreekPlanets.Venus.id should be(2)
|
||||
|
||||
GreekPlanets.Mercury.toString should be("Hermes")
|
||||
GreekPlanets.Venus.toString should be("Aphrodite")
|
||||
|
||||
(GreekPlanets.Earth == GreekPlanets.Earth) should be(true)
|
||||
(GreekPlanets.Neptune == GreekPlanets.Jupiter) should be(false)
|
||||
}
|
||||
|
||||
// Enumerations can be declared in one line if you are merely setting variables to Value
|
||||
koan("Enumeration declarations can be done on one line") {
|
||||
object Planets extends Enumeration {
|
||||
val Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto = Value
|
||||
}
|
||||
|
||||
Planets.Mercury.id should be(0)
|
||||
Planets.Venus.id should be(1)
|
||||
|
||||
Planets.Mercury.toString should be("Mercury") //How does it get the name? by Reflection.
|
||||
Planets.Venus.toString should be("Venus")
|
||||
|
||||
(Planets.Earth == Planets.Earth) should be(true)
|
||||
(Planets.Neptune == Planets.Jupiter) should be(false)
|
||||
}
|
||||
|
||||
|
||||
koan("Enumerations can be declared with a string value only") {
|
||||
object GreekPlanets extends Enumeration {
|
||||
|
||||
val Mercury = Value("Hermes")
|
||||
val Venus = Value("Aphrodite")
|
||||
val Earth = Value("Gaia")
|
||||
val Mars = Value("Ares")
|
||||
val Jupiter = Value("Zeus")
|
||||
val Saturn = Value("Cronus")
|
||||
val Uranus = Value("Ouranus")
|
||||
val Neptune = Value("Poseidon")
|
||||
val Pluto = Value("Hades")
|
||||
}
|
||||
|
||||
GreekPlanets.Mercury.id should be(0)
|
||||
GreekPlanets.Venus.id should be(1)
|
||||
|
||||
GreekPlanets.Mercury.toString should be("Hermes")
|
||||
GreekPlanets.Venus.toString should be("Aphrodite")
|
||||
|
||||
(GreekPlanets.Earth == GreekPlanets.Earth) should be(true)
|
||||
(GreekPlanets.Neptune == GreekPlanets.Jupiter) should be(false)
|
||||
}
|
||||
|
||||
koan("You can extend the Enumeration by extending the Val class.") {
|
||||
|
||||
object Planets extends Enumeration {
|
||||
|
||||
val G = 6.67300E-11;
|
||||
|
||||
class PlanetValue(val i: Int, val name: String, val mass: Double, val radius: Double)
|
||||
extends Val(i: Int, name: String) {
|
||||
|
||||
def surfaceGravity = G * mass / (radius * radius)
|
||||
|
||||
def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
|
||||
}
|
||||
|
||||
val Mercury = new PlanetValue(0, "Mercury", 3.303e+23, 2.4397e6)
|
||||
val Venus = new PlanetValue(1, "Venus", 4.869e+24, 6.0518e6)
|
||||
val Earth = new PlanetValue(2, "Earth", 5.976e+24, 6.37814e6)
|
||||
val Mars = new PlanetValue(3, "Mars", 6.421e+23, 3.3972e6)
|
||||
val Jupiter = new PlanetValue(4, "Jupiter", 1.9e+27, 7.1492e7)
|
||||
val Saturn = new PlanetValue(5, "Saturn", 5.688e+26, 6.0268e7)
|
||||
val Uranus = new PlanetValue(6, "Uranus", 8.686e+25, 2.5559e7)
|
||||
val Neptune = new PlanetValue(7, "Neptune", 1.024e+26, 2.4746e7)
|
||||
val Pluto = new PlanetValue(8, "Pluto", 1.27e+22, 1.137e6)
|
||||
|
||||
}
|
||||
|
||||
Planets.Earth.mass should be(5.976e+24)
|
||||
Planets.Earth.radius should be(6.37814e6)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutForExpressions extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("For loops can be simple") {
|
||||
val someNumbers = Range(0, 10)
|
||||
var sum = 0
|
||||
for (i <- someNumbers)
|
||||
sum += i
|
||||
|
||||
sum should equal(45)
|
||||
}
|
||||
|
||||
koan("For loops can contain additional logic") {
|
||||
val someNumbers = Range(0, 10)
|
||||
var sum = 0
|
||||
// sum only the even numbers
|
||||
for (i <- someNumbers)
|
||||
if (i % 2 == 0) sum += i
|
||||
|
||||
sum should equal(20)
|
||||
}
|
||||
koan("For loops can produce a list which can be summed easily") {
|
||||
val someNumbers = Range(0, 10)
|
||||
|
||||
val theList =
|
||||
for {
|
||||
i <- someNumbers
|
||||
if ((i % 2) == 0)
|
||||
}
|
||||
yield i
|
||||
|
||||
theList.reduceLeft(_ + _) should be(20)
|
||||
}
|
||||
|
||||
koan("For expressions can nest, with later generators varying more rapidly than earlier ones") {
|
||||
val xValues = Range(1, 5)
|
||||
val yValues = Range(1, 3)
|
||||
val coordinates = for {
|
||||
x <- xValues
|
||||
y <- yValues
|
||||
}
|
||||
yield (x, y)
|
||||
coordinates(4) should be(3, 1)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutFormatting extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""Character Literals can either be an a single character,
|
||||
| an escape sequence, a Unicode octal up to 255 or a hexadecimal""") {
|
||||
val a = 'a'
|
||||
val b = 'B'
|
||||
val c = '\u0061' //unicode for a
|
||||
val d = '\141' //octal for a
|
||||
val e = '\"'
|
||||
val f = '\\'
|
||||
|
||||
//format(a) is a string format, meaning the "%c".format(x)
|
||||
//will return the string representation of the char.
|
||||
|
||||
"%c".format(a) should be("a")
|
||||
"%c".format(b) should be("B")
|
||||
"%c".format(c) should be("a")
|
||||
"%c".format(d) should be("a")
|
||||
"%c".format(e) should be("\"")
|
||||
"%c".format(f) should be("\\")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import collection.immutable.List
|
||||
|
||||
class AboutHigherOrderFunctions extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("A variable referencing an anonymous function") {
|
||||
val lambda = {
|
||||
x: Int => x + 1
|
||||
}
|
||||
def result = List(1, 2, 3) map lambda //FYI: map runs a function on each element of a collection
|
||||
result should be(List(2, 3, 4))
|
||||
}
|
||||
|
||||
koan("Another way for a variable to reference an anonymous function") {
|
||||
val lambda = (x: Int) => x + 1
|
||||
def result = List(1, 2, 3) map lambda
|
||||
result should be(List(2, 3, 4))
|
||||
}
|
||||
|
||||
koan("Declaring a variable to reference an explicitly created function") {
|
||||
val lambda = new Function1[Int, Int] {
|
||||
def apply(v1: Int) = v1 + 1
|
||||
}
|
||||
def result = List(1, 2, 3) map lambda
|
||||
result should be(List(2, 3, 4))
|
||||
}
|
||||
|
||||
koan("""Another way for a variable to reference an explicitly created function. In this case (Int) => Int
|
||||
| is the same type as Function1[Int, Int]""") {
|
||||
val lambda = new ((Int) => Int) {
|
||||
def apply(v1: Int) = v1 + 1
|
||||
}
|
||||
def result = List(1, 2, 3) map lambda
|
||||
result should be(List(2, 3, 4))
|
||||
}
|
||||
|
||||
koan("A closure is any function that works with it's surroundings") {
|
||||
var incrementor = 1
|
||||
|
||||
def closure = {
|
||||
x: Int => x + incrementor
|
||||
}
|
||||
|
||||
val result = List(1, 2, 3) map closure
|
||||
result should be(List(2, 3, 4))
|
||||
|
||||
incrementor = 2
|
||||
|
||||
val result1 = List(1, 2, 3) map closure
|
||||
result1 should be(List(3, 4, 5))
|
||||
}
|
||||
|
||||
koan("A function returning another function") {
|
||||
def addWithoutSyntaxSugar(x: Int) = {
|
||||
new Function1[Int, Int]() {
|
||||
def apply(y: Int): Int = x + y
|
||||
}
|
||||
}
|
||||
|
||||
addWithoutSyntaxSugar(1)(2) should be(3)
|
||||
|
||||
def add(x: Int) = (y: Int) => x + y
|
||||
add(2)(3) should be(5)
|
||||
|
||||
def fiveAdder = add(5)
|
||||
fiveAdder(5) should be(10)
|
||||
}
|
||||
|
||||
|
||||
koan("function taking another function as parameter. Helps in compositioning functions") {
|
||||
def makeUpper(xs: List[String]) = xs map {
|
||||
_.toUpperCase
|
||||
}
|
||||
def makeWhatEverYouLike(xs: List[String], sideEffect: String => String) = {
|
||||
xs map sideEffect
|
||||
}
|
||||
makeUpper(List("abc", "xyz", "123")) should be(List("ABC", "XYZ", "123"))
|
||||
|
||||
makeWhatEverYouLike(List("ABC", "XYZ", "123"), {
|
||||
x => x.toLowerCase
|
||||
}) should be(List("abc", "xyz", "123"))
|
||||
//using it inline
|
||||
List("Scala", "Erlang", "Clojure") map {
|
||||
_.length
|
||||
} should be(List(5, 6, 7))
|
||||
}
|
||||
|
||||
koan("Currying is a technique to transform function with multiple parameters to function with one parameter") {
|
||||
def multiply(x: Int, y: Int) = x * y
|
||||
val multiplyCurried = (multiply _).curried
|
||||
multiply(4, 5) should be(20)
|
||||
multiplyCurried(3)(2) should be(6)
|
||||
}
|
||||
|
||||
koan("Currying allows you to create specialized version of generalized function") {
|
||||
def customFilter(f: Int => Boolean)(xs: List[Int]) = {
|
||||
xs filter f
|
||||
}
|
||||
def onlyEven(x: Int) = x % 2 == 0
|
||||
val xs = List(12, 11, 5, 20, 3, 13, 2)
|
||||
customFilter(onlyEven)(xs) should be(List(12, 20, 2))
|
||||
|
||||
val onlyEvenFilter = customFilter(onlyEven) _
|
||||
onlyEvenFilter(xs) should be(List(12, 20, 2))
|
||||
}
|
||||
|
||||
koan("Using a method inside a class to create a function") {
|
||||
class PokerTable(name: String, capacity: Int, players: List[String] = Nil) {
|
||||
def isAvailable(amount: Int) = (capacity - amount < 0)
|
||||
|
||||
def addPlayer(player: String) = {
|
||||
require(players.size < capacity, "Table Full")
|
||||
new PokerTable(name, capacity, players :+ player)
|
||||
}
|
||||
}
|
||||
|
||||
val theTexas = new PokerTable("The Texas", 9)
|
||||
val oldWest = new PokerTable("Old West", 3, Nil)
|
||||
val whirlwind = new PokerTable("Whirlwind", 3, Nil)
|
||||
val enchantress = new PokerTable("Enchantress", 15, Nil)
|
||||
|
||||
val x = theTexas.isAvailable _
|
||||
List(12, 2, 3, 4, 5, 6, 11, 44).filter(x)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 3/10/11
|
||||
* Time: 5:38 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
class AboutImplicits extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""Implicits wrap around existing classes to provide extra functionality
|
||||
| This is similar to \'monkey patching\' in Ruby, and Meta-Programming in Groovy.
|
||||
| Creating a method isOdd for Int, which doesn't exist""") {
|
||||
|
||||
class KoanIntWrapper(val original: Int) {
|
||||
def isOdd() = original % 2 != 0
|
||||
}
|
||||
|
||||
implicit def thisMethodNameIsIrrelevant(value: Int) = new KoanIntWrapper(value)
|
||||
|
||||
19.isOdd() should be(true)
|
||||
20.isOdd() should be(false)
|
||||
}
|
||||
|
||||
koan("""Implicits rules can be imported into your scope with an import""") {
|
||||
object MyPredef {
|
||||
|
||||
class KoanIntWrapper(val original: Int) {
|
||||
def isOdd() = original % 2 != 0
|
||||
|
||||
def isEven() = !isOdd()
|
||||
}
|
||||
|
||||
implicit def thisMethodNameIsIrrelevant(value: Int) = new KoanIntWrapper(value)
|
||||
}
|
||||
|
||||
import MyPredef._
|
||||
//imported implicits come into effect within this scope
|
||||
19.isOdd() should be(true)
|
||||
20.isOdd() should be(false)
|
||||
}
|
||||
|
||||
koan("""Implicits can be used to automatically convert one type to another""") {
|
||||
|
||||
import java.math.BigInteger
|
||||
implicit def Int2BigIntegerConvert(value: Int): BigInteger = new BigInteger(value.toString)
|
||||
|
||||
def add(a: BigInteger, b: BigInteger) = a.add(b)
|
||||
|
||||
(add(3, 6)) should be(new BigInteger("9"))
|
||||
}
|
||||
|
||||
koan("""Implicits can be used declare a value to be provided as a default as
|
||||
| long as an implicit value is set with in the scope. These are
|
||||
| called implicit function parameters""") {
|
||||
|
||||
def howMuchCanIMake_?(hours: Int)(implicit dollarsPerHour: BigDecimal) = dollarsPerHour * hours
|
||||
|
||||
implicit var hourlyRate = BigDecimal(34.00)
|
||||
howMuchCanIMake_?(30) should be(1020.00)
|
||||
|
||||
hourlyRate = BigDecimal(95.00)
|
||||
howMuchCanIMake_?(95) should be(9025.00)
|
||||
}
|
||||
|
||||
koan("""Implicit Function Parameters can contain a list of implicits""") {
|
||||
|
||||
def howMuchCanIMake_?(hours: Int)(implicit amount: BigDecimal, currencyName: String) =
|
||||
(amount * hours).toString() + " " + currencyName
|
||||
|
||||
implicit var hourlyRate = BigDecimal(34.00)
|
||||
implicit val currencyName = "Dollars"
|
||||
|
||||
howMuchCanIMake_?(30) should be("1020.0 Dollars")
|
||||
|
||||
hourlyRate = BigDecimal(95.00)
|
||||
howMuchCanIMake_?(95) should be("9025.0 Dollars")
|
||||
}
|
||||
|
||||
koan("""Default arguments though are preferred to Implicit Function Parameters""") {
|
||||
|
||||
def howMuchCanIMake_?(hours: Int, amount: BigDecimal = 34, currencyName: String = "Dollars") =
|
||||
(amount * hours).toString() + " " + currencyName
|
||||
|
||||
howMuchCanIMake_?(30) should be("1020 Dollars")
|
||||
|
||||
howMuchCanIMake_?(95, 95) should be("9025 Dollars")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.functionalkoans.forscala.support.KoanSuite
|
||||
|
||||
class AboutImportsAndPackages extends KoanSuite with ShouldMatchers {
|
||||
koan("An import can be placed in a method, hint this koan is a method") {
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
val arrayBuffer = ArrayBuffer.range(2, 10)
|
||||
arrayBuffer(0) should be(2)
|
||||
arrayBuffer(1) should be(3)
|
||||
}
|
||||
}
|
||||
|
||||
class Artist(val firstName: String, val lastName: String)
|
||||
|
||||
package subpackage {
|
||||
|
||||
class AboutImportsAndPackagesInSubpackages extends KoanSuite with ShouldMatchers {
|
||||
koan("A package can be included in a file with an established established package, " +
|
||||
"and can encapsulate it's contents with a {} block") {
|
||||
val luther = new Artist("Luther", "Vandross")
|
||||
luther.lastName should be("Vandross")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package album {
|
||||
|
||||
class Album(val name: String, val year: Short, val artist: Artist)
|
||||
|
||||
}
|
||||
|
||||
package media {
|
||||
|
||||
|
||||
class AboutReferencingAbsolutePackages extends KoanSuite with ShouldMatchers {
|
||||
|
||||
import org.functionalkoans.forscala.album.Album
|
||||
|
||||
// <<< Note the import style
|
||||
koan("A import can be done based from absolute package heirarchy") {
|
||||
val stLouisBlues = new Album("St. Louis Blues", 1940, new Artist("Louie", "Armstrong"))
|
||||
stLouisBlues.getClass.getCanonicalName should be("org.functionalkoans.forscala.album.Album")
|
||||
}
|
||||
}
|
||||
|
||||
class AboutReferencingAbsoluteRootPackages extends KoanSuite with ShouldMatchers {
|
||||
|
||||
import _root_.org.functionalkoans.forscala.album.Album
|
||||
|
||||
// <<< Note the import style
|
||||
koan("A import can be done based from absolute root package heirarchy using _root_") {
|
||||
val stLouisBlues = new Album("St. Louis Blues", 1940, new Artist("Louie", "Armstrong"))
|
||||
stLouisBlues.getClass.getCanonicalName should be("org.functionalkoans.forscala.album.Album")
|
||||
}
|
||||
}
|
||||
|
||||
class AboutReferencingRelativePackages extends KoanSuite with ShouldMatchers {
|
||||
|
||||
import album.Album
|
||||
|
||||
// <<< Note the import style
|
||||
koan("A import can be done based from relative packaging") {
|
||||
val stLouisBlues = new Album("St. Louis Blues", 1940, new Artist("Louie", "Armstrong"))
|
||||
stLouisBlues.getClass.getCanonicalName should be("org.functionalkoans.forscala.album.Album")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package music_additions {
|
||||
|
||||
class Genre(val name: String)
|
||||
|
||||
class Producer(val firstName: String, lastName: String)
|
||||
|
||||
class Distributor(val name: String)
|
||||
|
||||
}
|
||||
|
||||
class AboutImportingTechniques extends KoanSuite with ShouldMatchers {
|
||||
koan("To import all classes of a package, use _ as a wildcard") {
|
||||
import music_additions._
|
||||
val genre = new Genre("Jazz")
|
||||
val producer = new Producer("Joe", "Oliver")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
genre.name should be("Jazz")
|
||||
producer.firstName should be("Joe")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("To import all classes of a package, use can also use {_} as a wildcard") {
|
||||
import music_additions.{_}
|
||||
val genre = new Genre("Jazz")
|
||||
val producer = new Producer("Joe", "Oliver")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
genre.name should be("Jazz")
|
||||
producer.firstName should be("Joe")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("To import a select group of classes of a package, use {className1, className}") {
|
||||
import music_additions.{Genre, Distributor}
|
||||
val genre = new Genre("Jazz")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
genre.name should be("Jazz")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("You can rename a class by using => and create an alias") {
|
||||
import music_additions.{Genre => MusicType, Distributor}
|
||||
val musicType = new MusicType("Jazz")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
musicType.name should be("Jazz")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("You can rename a class by using =>, and also import all other classes in a package keeping their name") {
|
||||
import music_additions.{Genre => MusicType, _}
|
||||
val musicType = new MusicType("Jazz")
|
||||
val producer = new Producer("Joe", "Oliver")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
musicType.name should be("Jazz")
|
||||
producer.firstName should be("Joe")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("You can also refuse classes from being imported using => _") {
|
||||
import music_additions.{Producer => _, _}
|
||||
val musicType = new Genre("Jazz")
|
||||
val distributor = new Distributor("RYKO Classic Music")
|
||||
|
||||
musicType.name should be("Jazz")
|
||||
distributor.name should be("RYKO Classic Music")
|
||||
}
|
||||
|
||||
koan("You can just import the package themselves,so you can give it a verbose identity") {
|
||||
import scala.collection.mutable
|
||||
val arrayBuffer = mutable.ArrayBuffer.range(2, 10) //sounds better: A Mutable ArrayBuffer
|
||||
arrayBuffer(0) should be(2)
|
||||
arrayBuffer(1) should be(3)
|
||||
}
|
||||
|
||||
koan("You can just import the package themselves, and give it an alias!") {
|
||||
import scala.collection.{mutable => changeable}
|
||||
val arrayBuffer = changeable.ArrayBuffer.range(2, 10)
|
||||
arrayBuffer(0) should be(2)
|
||||
arrayBuffer(1) should be(3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 4/25/11
|
||||
* Time: 9:32 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
class AboutInfixPrefixAndPostfixOperators extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""Simple: Infix Operators are available if an object
|
||||
| has a method that takes one parameter.""") {
|
||||
|
||||
val g: Int = 3
|
||||
(g + 4) should be(7) // + is an infix operator
|
||||
(g.+(4)) should be(7) // same result but not using the infix operator
|
||||
}
|
||||
|
||||
koan("""Infix Operators do NOT work if an object
|
||||
| has a method that takes two parameters.""") {
|
||||
val g: String = "Check out the big brains on Brad!"
|
||||
g indexOf 'o' should be(6) //indexOf(Char) can be used as an infix operator
|
||||
//g indexOf 'o', 4 should be (6) //indexOf(Char, Int) cannot be used an infix operator
|
||||
g.indexOf('o', 7) should be(25) //indexOf(Char, Int) must use standard java/scala calls
|
||||
}
|
||||
|
||||
koan("""Postfix operators work if an object
|
||||
| has a method that takes no parameters.""") {
|
||||
val g: Int = 31
|
||||
(g toHexString) should be("1f") //toHexString takes no params therefore can be called
|
||||
//as a postfix operator. Hint: The answer is 1f
|
||||
}
|
||||
|
||||
|
||||
koan("""Prefix operators work if an object
|
||||
| has a method name that starts with unary_ .""") {
|
||||
val g: Int = 31
|
||||
(-31) should be(-31)
|
||||
}
|
||||
|
||||
koan("""Here we create our own prefix operator for our own class.
|
||||
| The only identifiers that can be used as prefix operators
|
||||
| are +, -, !, and ~""") {
|
||||
|
||||
class Stereo {
|
||||
def unary_+ = "on"
|
||||
|
||||
def unary_- = "off"
|
||||
}
|
||||
|
||||
val stereo = new Stereo
|
||||
(+stereo) should be("on")
|
||||
(-stereo) should be("off")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 4/25/11
|
||||
* Time: 10:17 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
class AboutInfixTypes extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""We can make a type infix, meaning that the type can be displayed in complement
|
||||
between two types in order to make a readable delaration""") {
|
||||
case class Person(name: String)
|
||||
class Loves[A, B](val a: A, val b: B)
|
||||
|
||||
def announceCouple(couple: Person Loves Person) = {
|
||||
//Notice our type: Person loves Person!
|
||||
couple.a.name + " is in love with " + couple.b.name
|
||||
}
|
||||
|
||||
val romeo = new Person("Romeo")
|
||||
val juliet = new Person("Juliet")
|
||||
|
||||
announceCouple(new Loves(romeo, juliet)) should be("Romeo is in love with Juliet")
|
||||
}
|
||||
|
||||
koan("""Of course we can make this a bit more elegant by creating an infix operator
|
||||
| method to use with our infix type""") {
|
||||
|
||||
case class Person(name: String) {
|
||||
def loves(person: Person) = new Loves(this, person)
|
||||
}
|
||||
|
||||
class Loves[A, B](val a: A, val b: B)
|
||||
|
||||
def announceCouple(couple: Person Loves Person) = {
|
||||
//Notice our type: Person loves Person!
|
||||
couple.a.name + " is in love with " + couple.b.name
|
||||
}
|
||||
|
||||
val romeo = new Person("Romeo")
|
||||
val juliet = new Person("Juliet")
|
||||
|
||||
announceCouple(romeo loves juliet) should be("Romeo is in love with Juliet")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
|
||||
class AboutInteroperability extends KoanSuite with ShouldMatchers {
|
||||
koan("""You can interop with a java class and it's use of collections by importing
|
||||
| scala.collection.JavaConversions and letting scala implicitly convert from a Scala collection type
|
||||
| into a Java collection type. See AboutImplicits Koan Suite for more details and see src/test/java for the
|
||||
| SomeJavaClass file. This koan
|
||||
| converts a scala List of String to java List of raw type.""") {
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
val d = new SomeJavaClass
|
||||
val e = List("one", "two", "three")
|
||||
d.findSizeOfRawType(e) should be(3)
|
||||
}
|
||||
|
||||
class Boat(size: Int, manufacturer: String)
|
||||
|
||||
koan("""This koan converts a scala List of Boat (our own class) to java List of unknown <?> type.""") {
|
||||
import scala.collection.JavaConversions._
|
||||
val d = new SomeJavaClass
|
||||
val e = List(new Boat(33, "Skyway"), new Boat(35, "New Boat"))
|
||||
d.findSizeOfUnknownType(e) should be(2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutIterables extends KoanSuite with ShouldMatchers {
|
||||
koan("""Iterable is a trait that has the ability to return an iterator of itself.
|
||||
| Some known iterators are Sets, Lists, Vectors, Stacks, and Streams. Iterator has two
|
||||
| important methods: `hasNext`, which answers whether the iterator has another element
|
||||
| available. `next` which will return the next element in the iterator.""") {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21)
|
||||
val it = list.iterator
|
||||
if (it.hasNext) {
|
||||
it.next should be(3)
|
||||
}
|
||||
}
|
||||
|
||||
koan("""`grouped` will return an fixed sized Iterable chucks of an Iterable""") {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21, 24, 32)
|
||||
val it = list grouped 3
|
||||
it.next() should be(List(3, 5, 9))
|
||||
it.next() should be(List(11, 15, 19))
|
||||
it.next() should be(List(21, 24, 32))
|
||||
}
|
||||
|
||||
koan("""`sliding` will return an Iterable that shows a sliding window of an Iterable.""") {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21, 24, 32)
|
||||
val it = list sliding 3
|
||||
it.next() should be(List(3, 5, 9))
|
||||
it.next() should be(List(5, 9, 11))
|
||||
it.next() should be(List(9, 11, 15))
|
||||
}
|
||||
|
||||
koan("""`sliding` can take the size of the window as well the size of the step during each
|
||||
| iteration""") {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21, 24, 32)
|
||||
val it = list sliding (3, 3)
|
||||
it.next() should be(List(3, 5, 9))
|
||||
it.next() should be(List(11, 15, 19))
|
||||
it.next() should be(List(21, 24, 32))
|
||||
}
|
||||
|
||||
koan("""`takeRight` is the opposite of 'take' in Traversable. It retrieves the last elements
|
||||
| of an Iterable. """) {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21, 24, 32)
|
||||
(list takeRight 3) should be(List(21, 24, 32))
|
||||
}
|
||||
|
||||
koan("""`dropRight` will drop the number of elements from the right. """) {
|
||||
val list = List(3, 5, 9, 11, 15, 19, 21, 24, 32)
|
||||
(list dropRight 3) should be(List(3, 5, 9, 11, 15, 19))
|
||||
}
|
||||
|
||||
koan("""`zip` will stitch two iterables into an iterable of pairs of corresponding elements
|
||||
| from both iterables. e.g. Iterable(x1, x2, x3) zip Iterable(y1, y2, y3) will
|
||||
| return ((x1,y1), (x2, y2), (x3, y3))""") {
|
||||
val xs = List(3, 5, 9)
|
||||
val ys = List("Bob", "Ann", "Stella")
|
||||
(xs zip ys) should be(List((3, "Bob"), (5, "Ann"), (9, "Stella")))
|
||||
}
|
||||
|
||||
koan("""if two Iterables aren't the same size, then `zip` will only zip what can only be paired.
|
||||
| e.g. Iterable(x1, x2, x3) zip Iterable(y1, y2) will
|
||||
| return ((x1,y1), (x2, y2))""") {
|
||||
val xs = List(3, 5, 9)
|
||||
val ys = List("Bob", "Ann")
|
||||
(xs zip ys) should be(List((3, "Bob"), (5, "Ann")))
|
||||
}
|
||||
|
||||
koan("""if two Iterables aren't the same size, then `zipAll` can provide fillers for what it couldn't
|
||||
| find a complement for. e.g. Iterable(x1, x2, x3) zipAll (Iterable(y1, y2), x, y) will
|
||||
| return ((x1,y1), (x2, y2, y))""") {
|
||||
val xs = List(3, 5, 9)
|
||||
val ys = List("Bob", "Ann")
|
||||
(xs zipAll (ys, -1, "?")) should be(List((3, "Bob"), (5, "Ann"), (9, "?")))
|
||||
}
|
||||
|
||||
koan("""`zipWithIndex` will zip an Iterable with it's integer index""") {
|
||||
val xs = List("Manny", "Moe", "Jack")
|
||||
xs.zipWithIndex should be(List(("Manny", 0), ("Moe", 1), ("Jack", 2)))
|
||||
}
|
||||
|
||||
koan("""`sameElements` will return true if the two iterables have the same number of elements""") {
|
||||
val xs = List("Manny", "Moe", "Jack")
|
||||
val ys = List("Manny", "Moe", "Jack")
|
||||
(xs sameElements ys) should be (true)
|
||||
|
||||
val xs1 = Set(3,2,1,4,5,6,7)
|
||||
val ys1 = Set(7,2,1,4,5,6,3)
|
||||
(xs1 sameElements ys1) should be (true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutLazySequences extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Creating a lazy collection form a strict collection") {
|
||||
val strictList = List(10, 20, 30)
|
||||
val lazyList = strictList.view
|
||||
lazyList.head should be(strictList.head)
|
||||
}
|
||||
|
||||
koan("Strict collection always processes it elements but lazy collection does on demand") {
|
||||
var x = 0
|
||||
def inc = {
|
||||
x += 1;
|
||||
x
|
||||
}
|
||||
var strictList = List(inc _, inc _, inc _)
|
||||
strictList.map(f => f()).head should be(1)
|
||||
x should be(3)
|
||||
strictList.map(f => f()).head
|
||||
x should be(6)
|
||||
|
||||
x = 0
|
||||
val lazyList = strictList.view
|
||||
lazyList.map(f => f()).head should be(1)
|
||||
x should be(1)
|
||||
lazyList.map(f => f()).head should be(2)
|
||||
x should be(2)
|
||||
}
|
||||
|
||||
koan("Lazy collection sometimes avoid processing errors") {
|
||||
val lazyList = List(2, -2, 0, 4).view map {
|
||||
2 / _
|
||||
}
|
||||
lazyList.head should be(1)
|
||||
lazyList(1) should be(-1)
|
||||
intercept[ArithmeticException] {
|
||||
lazyList(2)
|
||||
}
|
||||
}
|
||||
|
||||
koan("Lazy collections could also be infinite") {
|
||||
val infinite = Stream.from(1)
|
||||
infinite.take(4).sum should be(10)
|
||||
Stream.continually(1).take(4).sum should be(4)
|
||||
}
|
||||
|
||||
koan("Always remember tail of a lazy collection is never computed unless required") {
|
||||
def makeLazy(value: Int): Stream[Int] = {
|
||||
Stream.cons(value, makeLazy(value + 1))
|
||||
}
|
||||
val stream = makeLazy(1)
|
||||
stream.head should be(1)
|
||||
stream.tail.head should be(2)
|
||||
}
|
||||
|
||||
}
|
||||
130
src/test/scala/org/functionalkoans/forscala/AboutLists.scala
Normal file
130
src/test/scala/org/functionalkoans/forscala/AboutLists.scala
Normal file
@@ -0,0 +1,130 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutLists extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Nil lists are identical, even of different types") {
|
||||
val a: List[String] = Nil
|
||||
val b: List[Int] = Nil
|
||||
|
||||
(a == Nil) should be(true)
|
||||
(b == Nil) should be(true)
|
||||
(a == b) should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("Lists are easily created") {
|
||||
val a = List(1, 2, 3)
|
||||
a should equal(List(1, 2, 3))
|
||||
}
|
||||
|
||||
koan("Eq tests identity (same object)") {
|
||||
val a = List(1, 2, 3)
|
||||
val b = List(1, 2, 3)
|
||||
|
||||
|
||||
(a eq b) should be(false)
|
||||
}
|
||||
|
||||
koan("Lists can be accessed via head and tail") {
|
||||
val a = List(1, 2, 3)
|
||||
a.head should equal(1)
|
||||
a.tail should equal(List(2, 3))
|
||||
}
|
||||
|
||||
koan("Lists can be accessed at random") {
|
||||
val a = List(1, 3, 5, 7, 9)
|
||||
a(0) should equal(1)
|
||||
a(1) should equal(3)
|
||||
a(4) should equal(9)
|
||||
intercept[IndexOutOfBoundsException] {
|
||||
println(a(5))
|
||||
}
|
||||
}
|
||||
|
||||
koan("Lists are immutable") {
|
||||
val a = List(1, 3, 5, 7, 9)
|
||||
val b = a.filterNot(v => v == 5) // remove where value is 5
|
||||
|
||||
a should equal(List(1, 3, 5, 7, 9))
|
||||
b should equal(List(1, 3, 7, 9))
|
||||
}
|
||||
|
||||
koan("Lists have many useful methods") {
|
||||
val a = List(1, 3, 5, 7, 9)
|
||||
|
||||
// get the length of the list
|
||||
a.length should equal(5)
|
||||
|
||||
// reverse the list
|
||||
a.reverse should equal(List(9, 7, 5, 3, 1))
|
||||
|
||||
// convert the list to a string representation
|
||||
a.toString should equal("List(1, 3, 5, 7, 9)")
|
||||
|
||||
// map a function to double the numbers over the list
|
||||
a.map {
|
||||
v => v * 2
|
||||
} should equal(List(2, 6, 10, 14, 18))
|
||||
|
||||
// filter out any values divisible by 3 in the list
|
||||
a.filter {
|
||||
v => v % 3 == 0
|
||||
} should equal(List(3, 9))
|
||||
}
|
||||
|
||||
koan("Functions over lists can use _ as shorthand") {
|
||||
val a = List(1, 2, 3)
|
||||
a.map {
|
||||
_ * 2
|
||||
} should equal(List(2, 4, 6))
|
||||
a.filter {
|
||||
_ % 2 == 0
|
||||
} should equal(List(2))
|
||||
}
|
||||
|
||||
koan("Functions over lists can use () instead of {}") {
|
||||
val a = List(1, 2, 3)
|
||||
a.map(_ * 2) should equal(List(2, 4, 6))
|
||||
a.filter(_ % 2 != 0) should equal(List(1, 3))
|
||||
}
|
||||
|
||||
koan("Lists can be 'reduced' with a mathematical operation") {
|
||||
val a = List(1, 3, 5, 7)
|
||||
// note the two _s below indicate the first and second args respectively
|
||||
a.reduceLeft(_ + _) should equal(16)
|
||||
a.reduceLeft(_ * _) should equal(105)
|
||||
}
|
||||
|
||||
koan("Foldleft is like reduce, but with an explicit starting value") {
|
||||
val a = List(1, 3, 5, 7)
|
||||
// foldLeft uses a form called currying that we will explore later
|
||||
a.foldLeft(0)(_ + _) should equal(16)
|
||||
a.foldLeft(10)(_ + _) should equal(26)
|
||||
a.foldLeft(1)(_ * _) should equal(105)
|
||||
a.foldLeft(0)(_ * _) should equal(0)
|
||||
}
|
||||
|
||||
koan("You can create a list from a range") {
|
||||
val a = (1 to 5).toList
|
||||
a should be(List(1, 2, 3, 4, 5))
|
||||
val b = (1 until 5).toList
|
||||
b should be(List(1, 2, 3, 4))
|
||||
}
|
||||
|
||||
koan("Lists reuse their tails") {
|
||||
val d = Nil
|
||||
val c = 3 :: d
|
||||
val b = 2 :: c
|
||||
val a = 1 :: b
|
||||
|
||||
a should be(List(1, 2, 3))
|
||||
a.tail should be(List(2, 3))
|
||||
b.tail should be(List(3))
|
||||
c.tail should be(List())
|
||||
}
|
||||
|
||||
//For more about what lists can do, visit AboutTraversables.scala koans
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutLiteralBooleans extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""Boolean literals are either true or false, using the true or false keyword""") {
|
||||
val a = true
|
||||
val b = false
|
||||
val c = 1 > 2
|
||||
val d = 1 < 2
|
||||
val e = a == c
|
||||
val f = b == d
|
||||
a should be(true)
|
||||
b should be(false)
|
||||
c should be(false)
|
||||
d should be(true)
|
||||
e should be(false)
|
||||
f should be(false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutLiteralNumbers extends KoanSuite with ShouldMatchers {
|
||||
koan("Integer Literals are 32-bit and can be created from decimal, octal, or hexadecimal") {
|
||||
val a = 2
|
||||
val b = 31
|
||||
val c = 0x30F
|
||||
val d = 077
|
||||
val e = 0
|
||||
val f = -2
|
||||
val g = -31
|
||||
val h = -0x30F
|
||||
val i = -077
|
||||
a should be(2)
|
||||
b should be(31)
|
||||
c should be(783) //Hint: 30F = 783
|
||||
d should be(63) //Hint: 077 = 63
|
||||
e should be(0)
|
||||
f should be(-2)
|
||||
g should be(-31)
|
||||
h should be(-783) //Hint: 30F = 783
|
||||
i should be(-63) //Hint: 077 = 63
|
||||
}
|
||||
|
||||
koan("""Long Literals are 64 bit, are specified by appending an L or l at the end;
|
||||
| l is rarely used since it looks like a 1""") {
|
||||
val a = 2L
|
||||
val b = 31L
|
||||
val c = 0x30FL
|
||||
val d = 077L
|
||||
val e = 0L
|
||||
val f = -2l
|
||||
val g = -31L
|
||||
val h = -0x30FL
|
||||
val i = -077L
|
||||
|
||||
a should be(2)
|
||||
b should be(31)
|
||||
c should be(783) //Hint: 30F = 783
|
||||
d should be(63) //Hint: 077 = 63
|
||||
e should be(0)
|
||||
f should be(-2)
|
||||
g should be(-31)
|
||||
h should be(-783) //Hint: 30F = 783
|
||||
i should be(-63) //Hint: 077 = 63
|
||||
}
|
||||
|
||||
koan("""Float and Double Literals are IEEE 754 for specific,
|
||||
| Float are 32-bit length, Doubles are 64-bit.
|
||||
| Floats can be coerced using a f or F suffix, and
|
||||
| Doubles can be coerced using a d or D suffix.
|
||||
| Exponent are specified using e or E.""") {
|
||||
|
||||
val a = 3.0
|
||||
val b = 3.00
|
||||
val c = 2.73
|
||||
val d = 3f
|
||||
val e = 3.22d
|
||||
val f = 93e-9
|
||||
val g = 93E-9
|
||||
val h = 0.0
|
||||
val i = 9.23E-9D
|
||||
|
||||
a should be(3.0)
|
||||
b should be(3.0)
|
||||
c should be(2.73)
|
||||
d should be(3.0)
|
||||
e should be(3.22)
|
||||
f should be(93e-9)
|
||||
g should be(93E-9)
|
||||
h should be(0.0)
|
||||
i should be(9.23E-9D)
|
||||
}
|
||||
|
||||
|
||||
koan("""Trick: To distinguish the dot for a method invocation from the
|
||||
| decimal point in a float or double literal,
|
||||
| add a space after the literal""") {
|
||||
3.0.toString should be("3.0")
|
||||
3.toString should be("3")
|
||||
(3. toString) should be("3.0")
|
||||
(3.0 toString) should be("3.0")
|
||||
3d.toString should be("3.0")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutLiteralStrings extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Character Literals are quoted with single quotes") {
|
||||
val a = 'a'
|
||||
val b = 'B'
|
||||
|
||||
a.toString should be("a")
|
||||
b.toString should be("B")
|
||||
}
|
||||
|
||||
koan("Character Literals can use hexadecimal Unicode") {
|
||||
val c = '\u0061' //unicode for a
|
||||
|
||||
c.toString should be("a")
|
||||
}
|
||||
|
||||
koan("Character Literals can use octal as well") {
|
||||
val d = '\141' //octal for a
|
||||
|
||||
d.toString should be("a")
|
||||
}
|
||||
|
||||
koan("Character Literals can use escape sequences") {
|
||||
val e = '\"'
|
||||
val f = '\\'
|
||||
|
||||
e.toString should be("\"")
|
||||
f.toString should be("\\")
|
||||
}
|
||||
|
||||
koan("One-Line String Literals are surrounded by quotation marks.") {
|
||||
val a = "To be or not to be"
|
||||
a should be("To be or not to be")
|
||||
}
|
||||
|
||||
koan("String Literals can contain escape sequences.") {
|
||||
val a = "An \141pple \141 d\141y keeps the doctor \141w\141y"
|
||||
a should be("An apple a day keeps the doctor away")
|
||||
}
|
||||
|
||||
koan("""Multiline String literals
|
||||
are surrounded
|
||||
by three quotation marks""") {
|
||||
val a = """An apple a day
|
||||
keeps the doctor away"""
|
||||
a.split('\n').size should be(2) //a.split('\n').size determines the number of lines
|
||||
}
|
||||
|
||||
koan("Use stripMargin to prettify multi-line strings") {
|
||||
|
||||
/*
|
||||
* Multiline String literals can use | to specify the starting position
|
||||
* of subsequent lines, then use stripMargin to remove the surplus indentation.
|
||||
*/
|
||||
|
||||
val a = """An apple a day
|
||||
|keeps the doctor away"""
|
||||
a.stripMargin.split('\n')(1).charAt(0) should be('k')
|
||||
|
||||
/*
|
||||
* a.stripMargin.split('\n')(1).charAt(0)
|
||||
* gets the first character of the second line
|
||||
*/
|
||||
}
|
||||
}
|
||||
138
src/test/scala/org/functionalkoans/forscala/AboutLiterals.scala
Normal file
138
src/test/scala/org/functionalkoans/forscala/AboutLiterals.scala
Normal file
@@ -0,0 +1,138 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutLiterals extends KoanSuite with ShouldMatchers {
|
||||
koan("Integer Literals are 32-bit and can be created from decimal, octal, or hexadecimal") {
|
||||
val a = 2
|
||||
val b = 31
|
||||
val c = 0x30F
|
||||
val d = 077
|
||||
val e = 0
|
||||
val f = -2
|
||||
val g = -31
|
||||
val h = -0x30F
|
||||
val i = -077
|
||||
a should be(2)
|
||||
b should be(31)
|
||||
c should be(783) //Hint: 30F = 783
|
||||
d should be(63) //Hint: 077 = 63
|
||||
e should be(0)
|
||||
f should be(-2)
|
||||
g should be(-31)
|
||||
h should be(-783) //Hint: 30F = 783
|
||||
i should be(-63) //Hint: 077 = 63
|
||||
}
|
||||
|
||||
koan("""Long Literals are 64 bit, are specified by appending an L or l at the end;
|
||||
| l is rarely used since it looks like a 1""") {
|
||||
val a = 2L
|
||||
val b = 31L
|
||||
val c = 0x30FL
|
||||
val d = 077L
|
||||
val e = 0L
|
||||
val f = -2l
|
||||
val g = -31L
|
||||
val h = -0x30FL
|
||||
val i = -077L
|
||||
|
||||
a should be(2)
|
||||
b should be(31)
|
||||
c should be(783) //Hint: 30F = 783
|
||||
d should be(63) //Hint: 077 = 73
|
||||
e should be(0)
|
||||
f should be(-2)
|
||||
g should be(-31)
|
||||
h should be(-783) //Hint: 30F = 783
|
||||
i should be(-63) //Hint: 077 = 63
|
||||
}
|
||||
|
||||
koan("""Float and Double Literals are IEEE 754 for specific,
|
||||
| Float are 32-bit length, Doubles are 64-bit.
|
||||
| Floats can be coerced using a f or F suffix, and
|
||||
| Doubles can be coerced using a d or D suffix.
|
||||
| Exponent are specified using e or E.""") {
|
||||
|
||||
val a = 3.0
|
||||
val b = 3.00
|
||||
val c = 2.73
|
||||
val d = 3f
|
||||
val e = 3.22d
|
||||
val f = 93e-9
|
||||
val g = 93E-9
|
||||
val h = 0.0
|
||||
val i = 9.23E-9D
|
||||
|
||||
a should be(3.0)
|
||||
b should be(3.00)
|
||||
c should be(2.73)
|
||||
d should be(3.0)
|
||||
e should be(3.22)
|
||||
f should be(93e-9)
|
||||
g should be(93E-9)
|
||||
h should be(0)
|
||||
i should be(9.23E-9D)
|
||||
}
|
||||
|
||||
|
||||
koan("""Trick: To get a string representation of the
|
||||
| float or double literal add a space after the literal""") {
|
||||
3.0.toString should be("3.0")
|
||||
3.toString should be("3")
|
||||
(3. toString) should be("3.0")
|
||||
(3.0 toString) should be("3.0")
|
||||
3d.toString should be("3.0")
|
||||
}
|
||||
|
||||
koan("""Boolean literals are either true or false, using the true or false keyword""") {
|
||||
val a = true
|
||||
val b = false
|
||||
a should be(true)
|
||||
b should be(false)
|
||||
}
|
||||
|
||||
koan("""Character Literals can either be an a single character,
|
||||
| an escape sequence, a Unicode octal up to 255 or a hexadecimal""") {
|
||||
val a = 'a'
|
||||
val b = 'B'
|
||||
val c = '\u0061' //unicode for a
|
||||
val d = '\141' //octal for a
|
||||
val e = '\"'
|
||||
val f = '\\'
|
||||
|
||||
//format(a) is a string format, meaning the "%c".format(x)
|
||||
//will return the string representation of the char.
|
||||
|
||||
"%c".format(a) should be("a")
|
||||
"%c".format(b) should be("B")
|
||||
"%c".format(c) should be("a")
|
||||
"%c".format(d) should be("a")
|
||||
"%c".format(e) should be("\"")
|
||||
"%c".format(f) should be("\\")
|
||||
}
|
||||
|
||||
koan("""One-Line String Literals are surrounded by quotation marks.""") {
|
||||
val a = "To be or not to be"
|
||||
a should be("To be or not to be")
|
||||
}
|
||||
|
||||
koan("""String Literals can contain escape sequences.""") {
|
||||
val a = "An \141pple \141 d\141y keeps the doctor \141w\141y"
|
||||
a should be("An apple a day keeps the doctor away")
|
||||
}
|
||||
|
||||
koan("""Multiline String literals are surrounded by three quotation marks""") {
|
||||
val a = """An apple a day
|
||||
keeps the doctor away"""
|
||||
a.split('\n').size should be(2) //a.split('\n').size determines the number of lines
|
||||
}
|
||||
|
||||
koan("""Multiline String literals on subsequent lines can have | to specify
|
||||
| the start of the line, then use stripMargin to display it correctly""") {
|
||||
val a = """An apple a day
|
||||
|keeps the doctor away"""
|
||||
a.stripMargin.split('\n')(1).charAt(0) should be('k') //a.stripMargin.split('\n')(1).charAt(0)
|
||||
//gets the first character on the 2nd line
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
/**
|
||||
* Created by Daniel Hinojosa
|
||||
* User: Daniel Hinojosa
|
||||
* Date: 3/6/11
|
||||
* Time: 9:50 PM
|
||||
* url: <a href="http://www.evolutionnext.com">http://www.evolutionnext.com</a>
|
||||
* email: <a href="mailto:dhinojosa@evolutionnext.com">dhinojosa@evolutionnext.com</a>
|
||||
* tel: 505.363.5832
|
||||
*/
|
||||
|
||||
class Monkey
|
||||
|
||||
|
||||
class AboutManifests extends KoanSuite with ShouldMatchers {
|
||||
koan("""Manifests can be used to determine a type used
|
||||
| before it erased by the VM by using an implicit manifest argument.""") {
|
||||
def inspect[T](l: List[T])(implicit manifest: scala.reflect.Manifest[T]) = manifest.toString
|
||||
val list = 1 :: 2 :: 3 :: 4 :: 5 :: Nil
|
||||
inspect(list) should be("Int")
|
||||
}
|
||||
|
||||
koan("""Manifests can be attached to classes. Manifests have other meta-information about
|
||||
| the type erased""") {
|
||||
class Barrel[T](implicit m: scala.reflect.Manifest[T]) {
|
||||
def +(t: T) = "1 %s has been added".format(m.erasure.getSimpleName) //Simple-name of the class erased
|
||||
}
|
||||
val monkeyBarrel = new Barrel[Monkey]
|
||||
(monkeyBarrel + new Monkey) should be("1 Monkey has been added")
|
||||
}
|
||||
}
|
||||
139
src/test/scala/org/functionalkoans/forscala/AboutMaps.scala
Normal file
139
src/test/scala/org/functionalkoans/forscala/AboutMaps.scala
Normal file
@@ -0,0 +1,139 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
|
||||
class AboutMaps extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Maps can be created easily") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap.size should be(4)
|
||||
}
|
||||
|
||||
koan("Maps contain distinct pairings") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "MI" -> "Michigan")
|
||||
myMap.size should be(3)
|
||||
|
||||
|
||||
}
|
||||
|
||||
koan("Maps can be added to easily") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "MI" -> "Michigan")
|
||||
|
||||
val aNewMap = myMap + ("IL" -> "Illinois")
|
||||
|
||||
aNewMap.contains("IL") should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("Map values can be iterated") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "MI" -> "Michigan")
|
||||
|
||||
val mapValues = myMap.values
|
||||
|
||||
mapValues.size should be(3)
|
||||
|
||||
mapValues.head should be("Michigan")
|
||||
|
||||
val lastElement = mapValues.last
|
||||
lastElement should be("Wisconsin")
|
||||
|
||||
// for (mval <- mapValues) println(mval)
|
||||
|
||||
// NOTE that the following will not compile, as iterators do not implement "contains"
|
||||
//mapValues.contains("Illinois") should be (true)
|
||||
}
|
||||
|
||||
koan("Maps insertion with duplicate key updates previous entry with subsequent value") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "MI" -> "Meechigan")
|
||||
|
||||
val mapValues = myMap.values
|
||||
|
||||
mapValues.size should be(3)
|
||||
|
||||
myMap("MI") should be("Meechigan")
|
||||
}
|
||||
|
||||
koan("Map keys may be of mixed type") {
|
||||
val myMap = Map("Ann Arbor" -> "MI", 49931 -> "MI")
|
||||
myMap("Ann Arbor") should be("MI")
|
||||
myMap(49931) should be("MI")
|
||||
}
|
||||
|
||||
koan("Mixed type values can be added to a map ") {
|
||||
val myMap = scala.collection.mutable.Map.empty[String, Any]
|
||||
myMap("Ann Arbor") = (48103, 48104, 48108)
|
||||
myMap("Houghton") = 49931
|
||||
|
||||
myMap("Houghton") should be(49931)
|
||||
myMap("Ann Arbor") should be((48103, 48104, 48108))
|
||||
|
||||
|
||||
|
||||
// what happens if you change the Any to Int
|
||||
|
||||
}
|
||||
|
||||
|
||||
koan("Maps may be accessed") {
|
||||
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap("MI") should be("Michigan")
|
||||
myMap("IA") should be("Iowa")
|
||||
|
||||
}
|
||||
|
||||
koan("Map elements can be removed easily") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
val aNewMap = myMap - "MI"
|
||||
aNewMap.contains("MI") should be(false)
|
||||
}
|
||||
|
||||
koan("Accessing a map by key results in an exception if key is not found") {
|
||||
|
||||
val myMap = Map("OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
|
||||
intercept[NoSuchElementException] {
|
||||
myMap("MI") should be("Michigan")
|
||||
}
|
||||
}
|
||||
|
||||
koan("Map elements can be removed in multiple") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
|
||||
|
||||
val aNewMap = myMap -- List("MI", "OH")
|
||||
|
||||
aNewMap.contains("MI") should be(false)
|
||||
|
||||
aNewMap.contains("WI") should be(true)
|
||||
aNewMap.size should be(2)
|
||||
}
|
||||
|
||||
koan("Map elements can be removed with a tuple") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
val aNewMap = myMap - ("MI", "WI") // Notice: single '-' operator for tuples
|
||||
|
||||
aNewMap.contains("MI") should be(false)
|
||||
aNewMap.contains("OH") should be(true)
|
||||
aNewMap.size should be(2)
|
||||
}
|
||||
|
||||
koan("Attempted removal of nonexistent elements from a map is handled gracefully") {
|
||||
val myMap = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
val aNewMap = myMap - "MN"
|
||||
|
||||
aNewMap.equals(myMap) should be(true)
|
||||
}
|
||||
|
||||
koan("Map equivalency is independent of order") {
|
||||
|
||||
val myMap1 = Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
val myMap2 = Map("WI" -> "Wisconsin", "MI" -> "Michigan", "IA" -> "Iowa", "OH" -> "Ohio")
|
||||
|
||||
|
||||
myMap1.equals(myMap2) should be(true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
import scala.collection.mutable
|
||||
|
||||
class AboutMutableMaps extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Mutable maps can be created easily") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap.size should be(4)
|
||||
myMap += "OR" -> "Oregon"
|
||||
myMap contains "OR" should be(true)
|
||||
}
|
||||
|
||||
koan("Mutable maps can have elements removed") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap -= "OH"
|
||||
myMap contains "OH" should be(false)
|
||||
}
|
||||
|
||||
koan("Mutable maps can have tuples of elements removed") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap -= ("IA", "OH")
|
||||
myMap contains "OH" should be(false)
|
||||
myMap.size should be(2)
|
||||
}
|
||||
|
||||
koan("Mutable maps can have tuples of elements added") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "WI" -> "Wisconsin")
|
||||
myMap += ("IA" -> "Iowa", "OH" -> "Ohio")
|
||||
myMap contains "OH" should be(true)
|
||||
myMap.size should be(4)
|
||||
}
|
||||
|
||||
koan("Mutable maps can have Lists of elements added") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "WI" -> "Wisconsin")
|
||||
myMap ++= List("IA" -> "Iowa", "OH" -> "Ohio")
|
||||
myMap contains "OH" should be(true)
|
||||
myMap.size should be(4)
|
||||
}
|
||||
|
||||
koan("Mutable maps can have Lists of elements removed") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap --= List("IA", "OH")
|
||||
myMap contains "OH" should be(false)
|
||||
myMap.size should be(2)
|
||||
}
|
||||
|
||||
koan("Mutable maps can be cleared") {
|
||||
val myMap = mutable.Map("MI" -> "Michigan", "OH" -> "Ohio", "WI" -> "Wisconsin", "IA" -> "Iowa")
|
||||
myMap.clear() // Convention is to use parens if possible when method called changes state
|
||||
myMap contains "OH" should be(false)
|
||||
myMap.size should be(0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
import scala.collection.mutable
|
||||
|
||||
class AboutMutableSets extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Mutable sets can be created easily") {
|
||||
val mySet = mutable.Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet.size should be(4)
|
||||
mySet += "Oregon"
|
||||
mySet contains "Oregon" should be(true)
|
||||
}
|
||||
|
||||
koan("Mutable sets can have elements removed") {
|
||||
val mySet = mutable.Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet -= "Ohio"
|
||||
mySet contains "Ohio" should be(false)
|
||||
}
|
||||
|
||||
koan("Mutable sets can have tuples of elements removed") {
|
||||
val mySet = mutable.Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet -= ("Iowa", "Ohio")
|
||||
mySet contains "Ohio" should be(false)
|
||||
mySet.size should be(2)
|
||||
}
|
||||
|
||||
koan("Mutable sets can have tuples of elements added") {
|
||||
val mySet = mutable.Set("Michigan", "Wisconsin")
|
||||
mySet += ("Iowa", "Ohio")
|
||||
mySet contains "Ohio" should be(true)
|
||||
mySet.size should be(4)
|
||||
}
|
||||
|
||||
koan("Mutable sets can have Lists of elements added") {
|
||||
val mySet = mutable.Set("Michigan", "Wisconsin")
|
||||
mySet ++= List("Iowa", "Ohio")
|
||||
mySet contains "Ohio" should be(true)
|
||||
mySet.size should be(4)
|
||||
}
|
||||
|
||||
koan("Mutable sets can have Lists of elements removed") {
|
||||
val mySet = mutable.Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet --= List("Iowa", "Ohio")
|
||||
mySet contains "Ohio" should be(false)
|
||||
mySet.size should be(2)
|
||||
}
|
||||
|
||||
koan("Mutable sets can be cleared") {
|
||||
val mySet = mutable.Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet.clear() // Convention is to use parens if possible when method called changes state
|
||||
mySet contains "Ohio" should be(false)
|
||||
mySet.size should be(0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.BlankValues.__
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutNamedAndDefaultArguments() extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class WithoutClassParameters() {
|
||||
def addColors(red: Int, green: Int, blue: Int) = {
|
||||
(red, green, blue)
|
||||
}
|
||||
|
||||
def addColorsWithDefaults(red: Int = 0, green: Int = 0, blue: Int = 0) = {
|
||||
(red, green, blue)
|
||||
}
|
||||
}
|
||||
|
||||
class WithClassParameters(val defaultRed: Int, val defaultGreen: Int, val defaultBlue: Int) {
|
||||
def addColors(red: Int, green: Int, blue: Int) = {
|
||||
(red + defaultRed, green + defaultGreen, blue + defaultBlue)
|
||||
}
|
||||
|
||||
def addColorsWithDefaults(red: Int = 0, green: Int = 0, blue: Int = 0) = {
|
||||
(red + defaultRed, green + defaultGreen, blue + defaultBlue)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class WithClassParametersInClassDefinition(val defaultRed: Int = 0, val defaultGreen: Int = 255, val defaultBlue: Int = 100) {
|
||||
def addColors(red: Int, green: Int, blue: Int) = {
|
||||
(red + defaultRed, green + defaultGreen, blue + defaultBlue)
|
||||
}
|
||||
|
||||
def addColorsWithDefaults(red: Int = 0, green: Int = 0, blue: Int = 0) = {
|
||||
(red + defaultRed, green + defaultGreen, blue + defaultBlue)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
koan("can specify arguments in any order if you use their names") {
|
||||
val me = new WithoutClassParameters()
|
||||
|
||||
// what happens if you change the order of these parameters (nothing)
|
||||
val myColor = me.addColors(green = 0, red = 255, blue = 0)
|
||||
|
||||
// for koan, remove the values in the should equal
|
||||
myColor should equal(255, 0, 0)
|
||||
}
|
||||
|
||||
koan("can default arguments if you leave them off") {
|
||||
val me = new WithoutClassParameters()
|
||||
val myColor = me.addColorsWithDefaults(green = 255)
|
||||
|
||||
myColor should equal(0, 255, 0)
|
||||
}
|
||||
|
||||
koan("can access class parameters and specify arguments in any order if you use their names") {
|
||||
val me = new WithClassParameters(40, 50, 60)
|
||||
val myColor = me.addColors(green = 50, red = 60, blue = 40)
|
||||
|
||||
myColor should equal(100, 100, 100)
|
||||
}
|
||||
|
||||
koan("can access class parameters and default arguments if you leave them off") {
|
||||
val me = new WithClassParameters(10, 20, 30)
|
||||
val myColor = me.addColorsWithDefaults(green = 70)
|
||||
|
||||
myColor should equal(10, 90, 30)
|
||||
}
|
||||
|
||||
koan("can default class parameters and have default arguments too") {
|
||||
val me = new WithClassParametersInClassDefinition()
|
||||
val myColor = me.addColorsWithDefaults(green = 70)
|
||||
|
||||
myColor should equal(0, 325, 100)
|
||||
}
|
||||
|
||||
koan("default parameters can be functional too") {
|
||||
def reduce(a: Int, f: (Int, Int) => Int = (_ + _)): Int = f(a, a)
|
||||
|
||||
reduce(5) should equal(10)
|
||||
reduce(5, _ * _) should equal(25)
|
||||
}
|
||||
}
|
||||
|
||||
130
src/test/scala/org/functionalkoans/forscala/AboutOptions.scala
Normal file
130
src/test/scala/org/functionalkoans/forscala/AboutOptions.scala
Normal file
@@ -0,0 +1,130 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutOptions extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Option can have one of two values - Some or None") {
|
||||
val someValue: Option[String] = Some("I am wrapped in something")
|
||||
someValue.get should be("I am wrapped in something")
|
||||
|
||||
val nullValue: Option[String] = None
|
||||
nullValue should be(None)
|
||||
}
|
||||
|
||||
koan("Represent null with None because null is a bad idea") {
|
||||
val value1 = maybeItWillReturnSomething(true)
|
||||
val value2 = maybeItWillReturnSomething(false)
|
||||
|
||||
value1.get should be("Found value")
|
||||
intercept[java.util.NoSuchElementException] {
|
||||
value2.get
|
||||
}
|
||||
}
|
||||
|
||||
koan("Provide a default value for None") {
|
||||
val value1 = maybeItWillReturnSomething(true)
|
||||
val value2 = maybeItWillReturnSomething(false)
|
||||
|
||||
value1 getOrElse "No value" should be("Found value")
|
||||
value2 getOrElse "No value" should be("No value")
|
||||
value2 getOrElse {
|
||||
"default function"
|
||||
} should be("default function")
|
||||
|
||||
}
|
||||
|
||||
koan("checking whether option has value") {
|
||||
val value1 = maybeItWillReturnSomething(true)
|
||||
val value2 = maybeItWillReturnSomething(false)
|
||||
|
||||
value1.isEmpty should be(false)
|
||||
value2.isEmpty should be(true)
|
||||
}
|
||||
|
||||
koan("Option can also be used with pattern matching") {
|
||||
val someValue: Option[Double] = Some(20.0)
|
||||
val value = someValue match {
|
||||
case Some(v) => v
|
||||
case None => 0.0
|
||||
}
|
||||
value should be(20.0)
|
||||
val noValue: Option[Double] = None
|
||||
val value1 = noValue match {
|
||||
case Some(v) => v
|
||||
case None => 0.0
|
||||
}
|
||||
value1 should be(0.0)
|
||||
|
||||
}
|
||||
|
||||
koan("Option is more than just a replacement of null, its also a collection") {
|
||||
Some(10) map {
|
||||
_ + 10
|
||||
} should be(Some(20))
|
||||
Some(10) filter {
|
||||
_ == 10
|
||||
} should be(Some(10))
|
||||
Some(Some(10)) flatMap {
|
||||
_ map {
|
||||
_ + 10
|
||||
}
|
||||
} should be(Some(20))
|
||||
|
||||
var newValue1 = 0
|
||||
Some(20) foreach {
|
||||
newValue1 = _
|
||||
}
|
||||
newValue1 should be(20)
|
||||
|
||||
var newValue2 = 0
|
||||
None foreach {
|
||||
newValue2 = _
|
||||
}
|
||||
newValue2 should be(0)
|
||||
}
|
||||
|
||||
koan("Using Option to avoid if checks for null") {
|
||||
//the ugly version
|
||||
def makeFullName(firstName: String, lastName: String) = {
|
||||
if (firstName != null) {
|
||||
if (lastName != null) {
|
||||
firstName + " " + lastName
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
makeFullName("Nilanjan", "Raychaudhuri") should be("Nilanjan Raychaudhuri")
|
||||
makeFullName("Nilanjan", null) should be(null)
|
||||
//the pretty version
|
||||
def makeFullNamePrettyVersion(firstName: Option[String], lastName: Option[String]) = {
|
||||
firstName flatMap {
|
||||
fname =>
|
||||
lastName flatMap {
|
||||
lname =>
|
||||
Some(fname + " " + lname)
|
||||
}
|
||||
}
|
||||
}
|
||||
makeFullNamePrettyVersion(Some("Nilanjan"), Some("Raychaudhuri")) should be(Some("Nilanjan Raychaudhuri"))
|
||||
makeFullNamePrettyVersion(Some("Nilanjan"), None) should be(None)
|
||||
}
|
||||
|
||||
koan("Using in for comprehension") {
|
||||
val values = List(Some(10), Some(20), None, Some(15))
|
||||
val newValues = for {
|
||||
someValue <- values
|
||||
value <- someValue
|
||||
} yield value
|
||||
newValues should be(List(10, 20, 15))
|
||||
}
|
||||
|
||||
def maybeItWillReturnSomething(flag: Boolean): Option[String] = {
|
||||
if (flag) Some("Found value") else None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutParentClasses extends KoanSuite with ShouldMatchers {
|
||||
koan("Class heirarchy is linear, a class can only extend from one parent class") {
|
||||
class Worker(firstName: String, lastName: String) {}
|
||||
class Employee(firstName: String, lastName: String, employeeID: Long) extends Worker(firstName, lastName)
|
||||
}
|
||||
|
||||
koan("A class that extends from another is polymorphic") {
|
||||
class Worker(val firstName: String, val lastName: String) {}
|
||||
class Employee(override val firstName: String, override val lastName: String,
|
||||
val employeeID: Long) extends Worker(firstName, lastName)
|
||||
|
||||
val me = new Employee("Name", "Yourself", 1233)
|
||||
val worker: Worker = me
|
||||
|
||||
worker.firstName should be("Name")
|
||||
worker.lastName should be("Yourself")
|
||||
}
|
||||
|
||||
koan("An abstract class, as in Java, cannot be instantiated and only inherited") {
|
||||
abstract class Worker(val firstName: String, val lastName: String) {}
|
||||
|
||||
//val worker = new Worker
|
||||
}
|
||||
|
||||
|
||||
koan("An class can be placed inside an abstract class just like in java") {
|
||||
abstract class Worker(val firstName: String, val lastName: String) {
|
||||
|
||||
class Assignment(val hours: Long) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutPartialFunctions extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""A partial function is a trait that when
|
||||
| implemented can be used as building blocks to determine
|
||||
| a solution. The trait PartialFunction requires that the
|
||||
| the method isDefinedAt and apply be implemented.""") {
|
||||
|
||||
val doubleEvens: PartialFunction[Int, Int] = new PartialFunction[Int, Int] {
|
||||
//States that this partial function will take on the task
|
||||
def isDefinedAt(x: Int) = x % 2 == 0
|
||||
|
||||
//What we do if this does partial function matches
|
||||
def apply(v1: Int) = v1 * 2
|
||||
}
|
||||
|
||||
val tripleOdds: PartialFunction[Int, Int] = new PartialFunction[Int, Int] {
|
||||
def isDefinedAt(x: Int) = x % 2 != 0
|
||||
|
||||
def apply(v1: Int) = v1 * 3
|
||||
}
|
||||
|
||||
val whatToDo = doubleEvens orElse tripleOdds //Here we chain the partial functions together
|
||||
|
||||
whatToDo(3) should be(9)
|
||||
whatToDo(4) should be(8)
|
||||
}
|
||||
|
||||
koan("""Case statements are a quick way to create partial functions. When you create a case
|
||||
| statement, the apply and isDefinedAt is created for you.""") {
|
||||
|
||||
//The case statements are called case statements with guards
|
||||
val doubleEvens: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) == 0) => x * 2
|
||||
}
|
||||
val tripleOdds: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) != 0) => x * 3
|
||||
}
|
||||
|
||||
val whatToDo = doubleEvens orElse tripleOdds //Here we chain the partial functions together
|
||||
whatToDo(3) should be(9)
|
||||
whatToDo(4) should be(8)
|
||||
}
|
||||
|
||||
koan("""The result of partial functions can have an \'andThen\' function added to the end
|
||||
| of the chain""") {
|
||||
|
||||
//These are called case statements with guards
|
||||
val doubleEvens: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) == 0) => x * 2
|
||||
}
|
||||
val tripleOdds: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) != 0) => x * 3
|
||||
}
|
||||
|
||||
val addFive = (x: Int) => x + 5
|
||||
val whatToDo = doubleEvens orElse tripleOdds andThen addFive //Here we chain the partial functions together
|
||||
whatToDo(3) should be(14)
|
||||
whatToDo(4) should be(13)
|
||||
}
|
||||
|
||||
koan("""The result of partial functions can have an \'andThen\' function added to the end
|
||||
| of the chain used to continue onto another chain of logic""") {
|
||||
|
||||
val doubleEvens: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) == 0) => x * 2
|
||||
}
|
||||
val tripleOdds: PartialFunction[Int, Int] = {
|
||||
case x: Int if ((x % 2) != 0) => x * 3
|
||||
}
|
||||
|
||||
val printEven: PartialFunction[Int, String] = {
|
||||
case x: Int if ((x % 2) == 0) => "Even"
|
||||
}
|
||||
val printOdd: PartialFunction[Int, String] = {
|
||||
case x: Int if ((x % 2) != 0) => "Odd"
|
||||
}
|
||||
|
||||
val whatToDo = doubleEvens orElse tripleOdds andThen (printEven orElse printOdd)
|
||||
|
||||
whatToDo(3) should be("Odd")
|
||||
whatToDo(4) should be("Even")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutPartiallyAppliedFunctions extends KoanSuite with ShouldMatchers {
|
||||
koan("""A partially applied function is a function that you do not apply any or all the
|
||||
| arguments, creating another function. This partially applied function
|
||||
| doesn't apply any arguments""") {
|
||||
def sum(a: Int, b: Int, c: Int) = a + b + c
|
||||
val sum3 = sum _
|
||||
sum3(1, 9, 7) should be(17)
|
||||
sum(4, 5, 6) should be(15)
|
||||
}
|
||||
|
||||
koan("""Partially applied functions can replace any number of arguments""") {
|
||||
def sum(a: Int, b: Int, c: Int) = a + b + c
|
||||
val sumC = sum(1, 10, _: Int)
|
||||
sumC(4) should be(15)
|
||||
sum(4, 5, 6) should be(15)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutPatternMatching extends KoanSuite with ShouldMatchers {
|
||||
|
||||
//TODO:Lists
|
||||
//TODO:Guards
|
||||
|
||||
koan("Pattern matching returns something") {
|
||||
|
||||
val stuff = "blue"
|
||||
|
||||
val myStuff = stuff match {
|
||||
case "red" => println("RED"); 1
|
||||
case "blue" => println("BLUE"); 2
|
||||
case "green" => println("GREEN"); 3
|
||||
case _ => println(stuff); 0
|
||||
}
|
||||
|
||||
myStuff should be(2)
|
||||
|
||||
}
|
||||
|
||||
koan("Pattern matching can return complex somethings") {
|
||||
val stuff = "blue"
|
||||
|
||||
val myStuff = stuff match {
|
||||
case "red" => (255, 0, 0)
|
||||
case "green" => (0, 255, 0)
|
||||
case "blue" => (0, 0, 255)
|
||||
case _ => println(stuff); 0
|
||||
}
|
||||
|
||||
myStuff should be(0, 0, 255)
|
||||
|
||||
}
|
||||
|
||||
koan("Pattern matching can match complex expressions") {
|
||||
def goldilocks(expr: Any) = expr match {
|
||||
case ("porridge", "Papa") => "Papa eating porridge"
|
||||
case ("porridge", "Mama") => "Mama eating porridge"
|
||||
case ("porridge", "Baby") => "Baby eating porridge"
|
||||
case _ => "what?"
|
||||
}
|
||||
|
||||
goldilocks(("porridge", "Mama")) should be("Mama eating porridge")
|
||||
}
|
||||
|
||||
koan("Pattern matching can wildcard parts of expressions") {
|
||||
def goldilocks(expr: Any) = expr match {
|
||||
case ("porridge", _) => "eating"
|
||||
case ("chair", "Mama") => "sitting"
|
||||
case ("bed", "Baby") => "sleeping"
|
||||
case _ => "what?"
|
||||
}
|
||||
|
||||
goldilocks(("porridge", "Papa")) should be("eating")
|
||||
goldilocks(("chair", "Mama")) should be("sitting")
|
||||
}
|
||||
|
||||
koan("Pattern matching can substitute parts of expressions") {
|
||||
def goldilocks(expr: Any) = expr match {
|
||||
case ("porridge", bear) => bear + " said someone's been eating my porridge"
|
||||
case ("chair", bear) => bear + " said someone's been sitting in my chair"
|
||||
case ("bed", bear) => bear + " sais someone's been sleeping in my bed"
|
||||
case _ => "what?"
|
||||
}
|
||||
|
||||
goldilocks(("porridge", "Papa")) should be("Papa said someone's been eating my porridge")
|
||||
goldilocks(("chair", "Mama")) should be("Mama said someone's been sitting in my chair")
|
||||
}
|
||||
|
||||
koan("Pattern matching can done on regular expression groups") {
|
||||
val EatingRegularExpression = """Eating Alert: bear=([^,]+),\s+source=(.+)""".r
|
||||
val SittingRegularExpression = """Sitting Alert: bear=([^,]+),\s+source=(.+)""".r
|
||||
val SleepingRegularExpression = """Sleeping Alert: bear=([^,]+),\s+source=(.+)""".r
|
||||
|
||||
def goldilocks(expr: String) = expr match {
|
||||
case (EatingRegularExpression(bear, source)) => "%s said someone's been eating my %s".format(bear, source)
|
||||
case (SittingRegularExpression(bear, source)) => "%s said someone's been sitting on my %s".format(bear, source)
|
||||
case (SleepingRegularExpression(bear, source)) => "%s said someone's been sleeping in my %s".format(bear, source)
|
||||
case _ => "what?"
|
||||
}
|
||||
|
||||
goldilocks("Eating Alert: bear=Papa, source=porridge") should be("Papa said someone's been eating my porridge")
|
||||
goldilocks("Sitting Alert: bear=Mama, source=chair") should be("Mama said someone's been sitting on my chair")
|
||||
}
|
||||
|
||||
koan("""A backquote can be used to refer to a stable variable in scope to create a case statement.
|
||||
| This prevents what is called \'Variable Shadowing\'""") {
|
||||
val foodItem = "porridge"
|
||||
|
||||
def goldilocks(expr: Any) = expr match {
|
||||
case (`foodItem`, _) => "eating"
|
||||
case ("chair", "Mama") => "sitting"
|
||||
case ("bed", "Baby") => "sleeping"
|
||||
case _ => "what?"
|
||||
}
|
||||
|
||||
goldilocks(("porridge", "Papa")) should be("eating")
|
||||
goldilocks(("chair", "Mama")) should be("sitting")
|
||||
goldilocks(("porridge", "Cousin")) should be("eating")
|
||||
goldilocks(("beer", "Cousin")) should be("what?")
|
||||
}
|
||||
|
||||
koan("A backquote can be used to refer to a method parameter as a stable variable to create a case statement.") {
|
||||
|
||||
def patternEquals(i: Int, j: Int) = j match {
|
||||
case `i` => true
|
||||
case _ => false
|
||||
}
|
||||
patternEquals(3, 3) should be(true)
|
||||
patternEquals(7, 9) should be(false)
|
||||
patternEquals(9, 9) should be(true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.BlankValues.__
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutPreconditions extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class WithParameterRequirement(val myValue: Int) {
|
||||
require(myValue != 0)
|
||||
|
||||
|
||||
}
|
||||
|
||||
koan("Violating preconditions throws an exception") {
|
||||
intercept[IllegalArgumentException] {
|
||||
|
||||
|
||||
val myInstance = new WithParameterRequirement(0) // put the value needed to cause IllegalArgumentException in place of the blank
|
||||
|
||||
}
|
||||
}
|
||||
koan("On precondition violation, intercept expects type of exception thrown") {
|
||||
intercept[IllegalArgumentException] {
|
||||
// put the exception that will be thrown in place of the blank
|
||||
|
||||
|
||||
val myInstance = new WithParameterRequirement(0)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
34
src/test/scala/org/functionalkoans/forscala/AboutRange.scala
Normal file
34
src/test/scala/org/functionalkoans/forscala/AboutRange.scala
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutRange extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Range are not inclusive at end of range") {
|
||||
val someNumbers = Range(0, 10)
|
||||
|
||||
someNumbers.size should be(10)
|
||||
}
|
||||
|
||||
koan("Range can specify increment") {
|
||||
val someNumbers = Range(2, 10, 3)
|
||||
|
||||
someNumbers.size should be(3)
|
||||
}
|
||||
|
||||
koan("Range can indicate inclusion") {
|
||||
val someNumbers = Range(0, 34, 2)
|
||||
someNumbers.contains(33) should be(false)
|
||||
someNumbers.contains(32) should be(true)
|
||||
someNumbers.contains(34) should be(false)
|
||||
}
|
||||
|
||||
koan("Range can specify to include value") {
|
||||
val someNumbers = Range(0, 34).inclusive
|
||||
|
||||
someNumbers.contains(34) should be(true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutSequencesAndArrays extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("A list can be converted to an array") {
|
||||
val l = List(1, 2, 3)
|
||||
val a = l.toArray
|
||||
a should equal(Array(1, 2, 3))
|
||||
}
|
||||
|
||||
koan("Any sequence can be converted to a list") {
|
||||
val a = Array(1, 2, 3)
|
||||
val s = a.toSeq
|
||||
val l = s.toList
|
||||
l should equal(List(1, 2, 3))
|
||||
}
|
||||
|
||||
koan("You can create a sequence from a for comprehension") {
|
||||
val s = for (v <- 1 to 4) yield v
|
||||
s.toList should be(List(1, 2, 3, 4))
|
||||
}
|
||||
|
||||
koan("You can create a sequence from a for comprehension with a condition") {
|
||||
val s = for (v <- 1 to 10 if v % 3 == 0) yield v
|
||||
s.toList should be(List(3, 6, 9))
|
||||
}
|
||||
|
||||
koan("You can filter any sequence based on a predicate") {
|
||||
val s = Seq("hello", "to", "you")
|
||||
val filtered = s.filter(_.length > 2)
|
||||
filtered should be(Seq("hello", "you"))
|
||||
}
|
||||
|
||||
koan("You can also filter Arrays in the same way") {
|
||||
val a = Array("hello", "to", "you", "again")
|
||||
val filtered = a.filter(_.length > 3)
|
||||
filtered should be(Array("hello", "again"))
|
||||
}
|
||||
|
||||
koan("You can map values in a sequence through a function") {
|
||||
val s = Seq("hello", "world")
|
||||
val r = s map {
|
||||
_.reverse
|
||||
}
|
||||
|
||||
r should be(Seq("olleh", "dlrow"))
|
||||
}
|
||||
|
||||
}
|
||||
130
src/test/scala/org/functionalkoans/forscala/AboutSets.scala
Normal file
130
src/test/scala/org/functionalkoans/forscala/AboutSets.scala
Normal file
@@ -0,0 +1,130 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
|
||||
class AboutSets extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Sets can be created easily") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
mySet.size should be(4)
|
||||
}
|
||||
|
||||
koan("Sets contain distinct values") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Michigan")
|
||||
mySet.size should be(3)
|
||||
}
|
||||
|
||||
koan("Sets can be added to easily") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val aNewSet = mySet + "Illinois"
|
||||
|
||||
aNewSet.contains("Illinois") should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("Sets may be of mixed type") {
|
||||
val mySet = Set("Michigan", "Ohio", 12)
|
||||
|
||||
mySet.contains(12) should be(true)
|
||||
|
||||
mySet.contains("MI") should be(false)
|
||||
|
||||
}
|
||||
|
||||
koan("Sets may be accessed") {
|
||||
val mySet = Set("Michigan", "Ohio", 12)
|
||||
|
||||
mySet(12) should be(true)
|
||||
mySet("MI") should be(false)
|
||||
|
||||
}
|
||||
|
||||
koan("Set elements can be removed easily") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val aNewSet = mySet - "Michigan"
|
||||
|
||||
aNewSet.contains("Michigan") should be(false)
|
||||
}
|
||||
|
||||
koan("Set elements can be removed in multiple") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val aNewSet = mySet -- List("Michigan", "Ohio")
|
||||
|
||||
aNewSet.contains("Michigan") should be(false)
|
||||
aNewSet.contains("Wisconsin") should be(true)
|
||||
aNewSet.size should be(2)
|
||||
}
|
||||
|
||||
koan("Set elements can be removed with a tuple") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val aNewSet = mySet - ("Michigan", "Ohio") // Notice: single '-' operator for tuples
|
||||
|
||||
aNewSet.contains("Michigan") should be(false)
|
||||
aNewSet.contains("Wisconsin") should be(true)
|
||||
aNewSet.size should be(2)
|
||||
}
|
||||
koan("Attempted removal of nonexistent elements from a set is handled gracefully") {
|
||||
val mySet = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val aNewSet = mySet - "Minnesota"
|
||||
|
||||
aNewSet.equals(mySet) should be(true)
|
||||
}
|
||||
|
||||
koan("Sets can be iterated easily") {
|
||||
val mySet = Set(1, 3, 4, 9)
|
||||
var sum = 0
|
||||
for (i <- mySet)
|
||||
sum = sum + i
|
||||
|
||||
sum should be(17)
|
||||
}
|
||||
|
||||
koan("Two sets can be intersected easily") {
|
||||
val mySet1 = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val mySet2 = Set("Wisconsin", "Michigan", "Minnesota")
|
||||
val aNewSet = mySet1 intersect mySet2
|
||||
// NOTE: Scala 2.7 used **, deprecated for & or intersect in Scala 2.8
|
||||
|
||||
aNewSet.equals(Set("Michigan", "Wisconsin")) should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("Two sets can be joined as their union easily") {
|
||||
val mySet1 = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val mySet2 = Set("Wisconsin", "Michigan", "Minnesota")
|
||||
val aNewSet = mySet1 union mySet2 // NOTE: You can also use the "|" operator
|
||||
|
||||
aNewSet.equals(Set("Michigan", "Wisconsin", "Ohio", "Iowa", "Minnesota")) should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("A set is either a subset of another set or it isn't") {
|
||||
val mySet1 = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val mySet2 = Set("Wisconsin", "Michigan", "Minnesota")
|
||||
val mySet3 = Set("Wisconsin", "Michigan")
|
||||
|
||||
mySet2 subsetOf mySet1 should be(false)
|
||||
mySet3 subsetOf mySet1 should be(true)
|
||||
|
||||
}
|
||||
|
||||
koan("The difference between two sets can be obtained easily") {
|
||||
val mySet1 = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val mySet2 = Set("Wisconsin", "Michigan")
|
||||
val aNewSet = mySet1 diff mySet2 // Note: you can use the "&~" operator if you *really* want to.
|
||||
|
||||
aNewSet.equals(Set("Ohio", "Iowa")) should be(true)
|
||||
}
|
||||
koan("Set equivalency is independent of order") {
|
||||
val mySet1 = Set("Michigan", "Ohio", "Wisconsin", "Iowa")
|
||||
val mySet2 = Set("Wisconsin", "Michigan", "Ohio", "Iowa")
|
||||
|
||||
mySet1.equals(mySet2) should be(true)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
399
src/test/scala/org/functionalkoans/forscala/AboutTraits.scala
Normal file
399
src/test/scala/org/functionalkoans/forscala/AboutTraits.scala
Normal file
@@ -0,0 +1,399 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutTraits extends KoanSuite with ShouldMatchers {
|
||||
koan("A class can use the \'extends\' keyword to mixin a trait if it is the only relationship the class inherits") {
|
||||
case class Event(name: String, source: Any)
|
||||
|
||||
trait EventListener {
|
||||
def listen(event: Event)
|
||||
}
|
||||
|
||||
class MyListener extends EventListener {
|
||||
def listen(event: Event) {
|
||||
event match {
|
||||
case Event("Moose Stampede", _) => println("An unfortunate moose stampede occured")
|
||||
case _ => println("Nothing of importance occured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val evt = Event("Moose Stampede", this)
|
||||
val myListener = new MyListener()
|
||||
myListener.listen(evt)
|
||||
}
|
||||
|
||||
koan("""A class can only \'extends\' from _one_ class or trait, any subsequent extension
|
||||
| should use the keyword \'with\'""") {
|
||||
|
||||
case class Event(name: String, source: Any)
|
||||
|
||||
trait EventListener {
|
||||
def listen(event: Event)
|
||||
}
|
||||
|
||||
class OurListener
|
||||
|
||||
class MyListener extends OurListener with EventListener {
|
||||
def listen(event: Event) {
|
||||
event match {
|
||||
case Event("Woodchuck Stampede", _) => println("An unfortunate woodchuck stampede occured")
|
||||
case _ => println("Nothing of importance occured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val evt = Event("Woodchuck Stampede", this)
|
||||
val myListener = new MyListener()
|
||||
myListener.listen(evt)
|
||||
}
|
||||
|
||||
|
||||
koan("""Protip: For many; using \'extends\' gives the impression of a \'is-a\' relationship and many don't want to
|
||||
| extend from a trait that doesn't follow that relationship. If a class
|
||||
| has no need to extend from a parent class but you want to mixin one or more traits, and would rather use
|
||||
| \'with\' with all traits instead of one trait with extends and the others with \'with\' you can
|
||||
| \'extends\' from an Object so that all traits can be used with \'with\'""") {
|
||||
|
||||
case class Event(name: String, source: Any)
|
||||
|
||||
trait EventListener {
|
||||
def listen(event: Event)
|
||||
}
|
||||
|
||||
trait MouseListener {
|
||||
def listen(mouseEvent: Event)
|
||||
}
|
||||
|
||||
class MyListener extends Object with MouseListener with EventListener {
|
||||
//Nice line here
|
||||
def listen(event: Event) {
|
||||
event match {
|
||||
case Event("Woodchuck Stampede", _) => println("An unfortunate woodchuck stampede occured")
|
||||
case _ => println("Nothing of importance occured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val evt = Event("Woodchuck Stampede", this)
|
||||
val myListener = new MyListener()
|
||||
myListener.listen(evt)
|
||||
}
|
||||
|
||||
koan("Traits are polymorphic. Any type can be referred to by another type if related by extension or trait") {
|
||||
|
||||
case class Event(name: String, source: Any)
|
||||
|
||||
trait EventListener {
|
||||
def listen(event: Event)
|
||||
}
|
||||
|
||||
class MyListener extends EventListener {
|
||||
def listen(event: Event) {
|
||||
event match {
|
||||
case Event("Moose Stampede", _) => println("An unfortunate moose stampede occured")
|
||||
case _ => println("Nothing of importance occured")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val evt = Event("Moose Stampede", this)
|
||||
val myListener = new MyListener()
|
||||
|
||||
myListener.isInstanceOf[MyListener] should be(true)
|
||||
myListener.isInstanceOf[EventListener] should be(true)
|
||||
myListener.isInstanceOf[Any] should be(true)
|
||||
myListener.isInstanceOf[AnyRef] should be(true)
|
||||
}
|
||||
|
||||
koan("""Traits can have concrete method implementations that can be mixed
|
||||
| into concrete classes with it's own state""") {
|
||||
|
||||
trait ListLog {
|
||||
var logCache = List[String]()
|
||||
|
||||
def log(value: String) {
|
||||
logCache = logCache :+ value
|
||||
}
|
||||
}
|
||||
|
||||
class Welder extends ListLog {
|
||||
def weld() {
|
||||
log("welding pipe")
|
||||
}
|
||||
}
|
||||
|
||||
class Baker extends ListLog {
|
||||
def bake() {
|
||||
log("baking cake")
|
||||
}
|
||||
}
|
||||
|
||||
val welder = new Welder
|
||||
welder.weld()
|
||||
|
||||
|
||||
val baker = new Baker
|
||||
baker.bake()
|
||||
|
||||
welder.logCache.size should be(1)
|
||||
baker.logCache.size should be(1)
|
||||
|
||||
welder.logCache(0) should be("welding pipe")
|
||||
baker.logCache(0) should be("baking cake")
|
||||
}
|
||||
|
||||
koan("""Traits can have concrete implementations, but can still be overridden by classes that
|
||||
| mixin the trait""") {
|
||||
|
||||
trait ListLog {
|
||||
var logCache = List[String]()
|
||||
|
||||
def log(value: String) {
|
||||
logCache = logCache :+ value
|
||||
}
|
||||
}
|
||||
|
||||
class Welder extends Object with ListLog {
|
||||
def weld() {
|
||||
log("welding pipe")
|
||||
}
|
||||
|
||||
override def log(value: String) {
|
||||
super.log("Weld Log : " + value)
|
||||
}
|
||||
}
|
||||
|
||||
class Baker extends ListLog {
|
||||
def bake() {
|
||||
log("baking cake")
|
||||
}
|
||||
|
||||
override def log(value: String) {
|
||||
super.log("Bake Log : " + value)
|
||||
}
|
||||
}
|
||||
|
||||
val welder = new Welder
|
||||
welder.weld() //Parenthesis are used to make calls on methods with side-effects
|
||||
|
||||
val baker = new Baker
|
||||
baker.bake() //Parenthesis are used to make calls on methods with side-effects
|
||||
|
||||
welder.logCache.size should be(1)
|
||||
baker.logCache.size should be(1)
|
||||
|
||||
welder.logCache(0) should be("Weld Log : welding pipe")
|
||||
baker.logCache(0) should be("Bake Log : baking cake")
|
||||
}
|
||||
|
||||
koan("""Traits can be stacked with other traits to create customizable decorative abstractions for a class""") {
|
||||
|
||||
trait Log {
|
||||
//A log
|
||||
def log(value: String)
|
||||
}
|
||||
|
||||
trait TimedLog extends Log {
|
||||
abstract override def log(value: String) {
|
||||
super.log("January, 12, 2025 : " + value)
|
||||
}
|
||||
}
|
||||
|
||||
trait UserLog extends Log {
|
||||
abstract override def log(value: String) {
|
||||
super.log("Root said: " + value)
|
||||
}
|
||||
}
|
||||
|
||||
trait ListLog extends Log {
|
||||
var logCache = List[String]()
|
||||
|
||||
override def log(value: String) {
|
||||
logCache = logCache :+ value
|
||||
println(value)
|
||||
}
|
||||
}
|
||||
|
||||
class Baker extends Object with ListLog with TimedLog with UserLog {
|
||||
def bake() {
|
||||
log("baking cake")
|
||||
}
|
||||
}
|
||||
|
||||
val baker = new Baker
|
||||
baker.bake()
|
||||
baker.logCache(0) should be("January, 12, 2025 : Root said: baking cake")
|
||||
|
||||
class Welder extends Object with ListLog with TimedLog {
|
||||
// I don't want UserLog with a Welder
|
||||
def weld() {
|
||||
log("welding pipe")
|
||||
}
|
||||
}
|
||||
|
||||
val welder = new Welder
|
||||
welder.weld()
|
||||
welder.logCache(0) should be("January, 12, 2025 : welding pipe")
|
||||
}
|
||||
|
||||
koan("""Traits can be stacked with other traits to create customizable decorative
|
||||
| abstractions for a class that weren't written in originally!""") {
|
||||
|
||||
trait Log {
|
||||
//A log
|
||||
def log(value: String)
|
||||
}
|
||||
|
||||
trait TimedLog extends Log {
|
||||
abstract override def log(value: String) {
|
||||
super.log("January, 12, 2025 : " + value)
|
||||
}
|
||||
}
|
||||
|
||||
trait UserLog extends Log {
|
||||
abstract override def log(value: String) {
|
||||
super.log("Root said: " + value)
|
||||
}
|
||||
}
|
||||
|
||||
trait ListLog extends Log {
|
||||
var logCache = List[String]()
|
||||
|
||||
override def log(value: String) {
|
||||
logCache = logCache :+ value
|
||||
println(value)
|
||||
}
|
||||
}
|
||||
|
||||
case class Baker() //define a class
|
||||
val baker = new Baker with ListLog with UserLog with TimedLog //Pick and choose what traits to stack!
|
||||
baker.log("baked cake")
|
||||
baker.logCache(0) should be("Root said: January, 12, 2025 : baked cake") //Whoa!
|
||||
}
|
||||
|
||||
|
||||
koan("Traits are instantiated before a classes instantition") {
|
||||
var sb = List[String]()
|
||||
|
||||
trait T1 {
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
val x = 1
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
}
|
||||
|
||||
class C1 extends T1 {
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
val y = 2
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
}
|
||||
|
||||
sb = sb :+ ("Creating C1")
|
||||
new C1
|
||||
sb = sb :+ ("Created C1")
|
||||
|
||||
sb.mkString(";") should be("Creating C1;In T1: x=0;In T1: x=1;In C1: y=0;In C1: y=2;Created C1")
|
||||
}
|
||||
|
||||
|
||||
koan("Traits are instantiated before a classes instantition from left to right") {
|
||||
var sb = List[String]()
|
||||
|
||||
trait T1 {
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
val x = 1
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
}
|
||||
|
||||
trait T2 {
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
val z = 1
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
}
|
||||
|
||||
class C1 extends T1 with T2 {
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
val y = 2
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
}
|
||||
|
||||
sb = sb :+ ("Creating C1")
|
||||
new C1
|
||||
sb = sb :+ ("Created C1")
|
||||
|
||||
sb.mkString(";") should be(
|
||||
"Creating C1;In T1: x=0;In T1: x=1;In T2: z=0;In T2: z=1;In C1: y=0;In C1: y=2;Created C1")
|
||||
}
|
||||
|
||||
koan("""Instantiations are tracked and will not allow a duplicate instantiation.
|
||||
| Note T1 extends T2, and C1 also extends T2, but T2 is only instantiated twice.""") {
|
||||
|
||||
var sb = List[String]()
|
||||
|
||||
trait T1 extends T2 {
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
val x = 1
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
}
|
||||
|
||||
trait T2 {
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
val z = 1
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
}
|
||||
|
||||
class C1 extends T1 with T2 {
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
val y = 2
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
}
|
||||
|
||||
sb = sb :+ ("Creating C1")
|
||||
new C1
|
||||
sb = sb :+ ("Created C1")
|
||||
|
||||
sb.mkString(";") should
|
||||
be("Creating C1;In T2: z=0;In T2: z=1;In T1: x=0;In T1: x=1;In C1: y=0;In C1: y=2;Created C1")
|
||||
}
|
||||
|
||||
|
||||
koan("The diamond of death (http://en.wikipedia.org/wiki/Diamond_problem) is avoided since " +
|
||||
"instantiations are tracked and will not allow multiple instantiations") {
|
||||
|
||||
var sb = List[String]()
|
||||
|
||||
trait T1 {
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
val x = 1
|
||||
sb = sb :+ ("In T1: x=%s".format(x))
|
||||
}
|
||||
|
||||
trait T2 extends T1 {
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
val z = 2
|
||||
sb = sb :+ ("In T2: z=%s".format(z))
|
||||
}
|
||||
|
||||
trait T3 extends T1 {
|
||||
sb = sb :+ ("In T3: w=%s".format(w))
|
||||
val w = 3
|
||||
sb = sb :+ ("In T3: w=%s".format(w))
|
||||
}
|
||||
|
||||
class C1 extends T2 with T3 {
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
val y = 4
|
||||
sb = sb :+ ("In C1: y=%s".format(y))
|
||||
}
|
||||
|
||||
sb = sb :+ ("Creating C1")
|
||||
new C1
|
||||
sb = sb :+ ("Created C1")
|
||||
|
||||
sb.mkString(";") should be
|
||||
("Creating C1;In T1: x=0;In T1: x=1;In T2: z=0;In T2: z=2;" +
|
||||
"In T3: w=0;In T3: w=3;In C1: y=0;In C1: y=4;Created C1")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,550 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
import Stream._
|
||||
import collection.immutable.{TreeMap, TreeSet}
|
||||
|
||||
class AboutTraversables extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("""Traverables are the superclass of Lists, Arrays, Maps, Sets, Streams, and more.
|
||||
| The methods involved can be applied to each other in a different type. ++ appends
|
||||
| two Traversables together.""") {
|
||||
|
||||
val set = Set(1, 9, 10, 22)
|
||||
val list = List(3, 4, 5, 10)
|
||||
val result = set ++ list
|
||||
result.size should be(7)
|
||||
|
||||
val result2 = list ++ set
|
||||
result2.size should be(8)
|
||||
}
|
||||
|
||||
koan("""map will apply the given function on all elements of a
|
||||
| Traversable and return a new collection of the result.""") {
|
||||
val set = Set(1, 3, 4, 6)
|
||||
val result = set.map(_ * 4)
|
||||
result.last should be(24)
|
||||
}
|
||||
|
||||
koan("""flatten will smash all child Traversables within a Traversable""") {
|
||||
val list = List(List(1), List(2, 3, 4), List(5, 6, 7), List(8, 9, 10))
|
||||
list.flatten should be(List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
|
||||
}
|
||||
|
||||
koan("""flatMap will not only apply the given function on all elements of a Traversable,
|
||||
| but all elements within the elements and flatten the results""") {
|
||||
val list = List(List(1), List(2, 3, 4), List(5, 6, 7), List(8, 9, 10))
|
||||
val result = list.flatMap(_.map(_ * 4))
|
||||
result should be(List(4, 8, 12, 16, 20, 24, 28, 32, 36, 40))
|
||||
}
|
||||
|
||||
koan("""flatMap of Options will filter out all Nones and Keep the Somes""") {
|
||||
val list = List(1, 2, 3, 4, 5)
|
||||
val result = list.flatMap(it => if (it % 2 == 0) Some(it) else None)
|
||||
result should be(List(2, 4))
|
||||
}
|
||||
|
||||
koan("""collect will apply a partial function to all elements of a Traversable
|
||||
and will return a different collection. In this koan, a case fragment is a partial function.""") {
|
||||
val list = List(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = list.collect {
|
||||
case x: Int if (x % 2 == 0) => x * 3
|
||||
}
|
||||
result should be(List(12, 18, 24, 42))
|
||||
}
|
||||
|
||||
koan("""collect will apply a partial function to all elements of a Traversable
|
||||
| and will return a different collection. In this koan, two case fragments are chained to create
|
||||
| a more robust result.""") {
|
||||
val list = List(4, 6, 7, 8, 9, 13, 14)
|
||||
val partialFunction1: PartialFunction[Int, Int] = {
|
||||
case x: Int if (x % 2 == 0) => x * 3
|
||||
}
|
||||
val partialFunction2: PartialFunction[Int, Int] = {
|
||||
case y: Int if (y % 2 != 0) => y * 4
|
||||
}
|
||||
val result = list.collect(partialFunction1 orElse partialFunction2)
|
||||
result should be(List(12, 18, 28, 24, 36, 52, 42))
|
||||
}
|
||||
|
||||
koan("""foreach will apply a function to all elements of a Traversable, but unlike
|
||||
| the map function, it will not return anything since the return type is Unit, which
|
||||
| is like a void return type in Java, C++""") {
|
||||
val list = List(4, 6, 7, 8, 9, 13, 14)
|
||||
list.foreach(num => println(num * 4))
|
||||
list should be(List(4, 6, 7, 8, 9, 13, 14))
|
||||
}
|
||||
|
||||
koan("""toArray will convert any Traversable to an Array, which is a special wrapper around a
|
||||
| primitive Java array.""") {
|
||||
val set = Set(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = set.toArray
|
||||
result.isInstanceOf[Array[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toList will convert any Traversable to a List.""") {
|
||||
val set = Set(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = set.toList
|
||||
result.isInstanceOf[List[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toList, as well as other conversion methods like toSet, toArray,
|
||||
| will not convert if the collection type is the same.""") {
|
||||
val list = List(5, 6, 7, 8, 9)
|
||||
val result = list.toList
|
||||
(result eq list) should be(true) //Reminder: eq tests for reference equality
|
||||
}
|
||||
|
||||
koan("""toIterable will convert any Traversable to an Iterable. This is a base
|
||||
| trait for all Scala collections that define an iterator method to step
|
||||
| through one-by-one the collection's elements.
|
||||
| (see AboutIterable koan).""") {
|
||||
|
||||
val set = Set(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = set.toIterable
|
||||
result.isInstanceOf[Iterable[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toSeq will convert any Traversable to a Seq which is an ordered Iterable
|
||||
| and is the superclass to List, Queues, and Vectors. Sequences provide
|
||||
| a method apply for indexing. Indices range from 0 up the the
|
||||
| length of a sequence.""") {
|
||||
val set = Set(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = set.toSeq
|
||||
result.isInstanceOf[Seq[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toIndexedSeq will convert any Traversable to an IndexedSeq which is
|
||||
| an indexed sequence used in
|
||||
| Vectors and Strings""") {
|
||||
val set = Set(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = set.toIndexedSeq
|
||||
result.isInstanceOf[IndexedSeq[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toStream will convert any Traversable to an Stream which is
|
||||
| which is a lazy lists where elements are evaluated as they
|
||||
| are needed.""") {
|
||||
val list = List(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = list.toStream
|
||||
result.isInstanceOf[Stream[Int]] should be(true)
|
||||
(result take 3) should be(List(4, 6, 7))
|
||||
}
|
||||
|
||||
koan("""toSet will convert any Traversable to an Set which is
|
||||
| which is a collection of unordered, unique values""") {
|
||||
val list = List(4, 6, 7, 8, 9, 13, 14)
|
||||
val result = list.toSet
|
||||
result.isInstanceOf[Set[Int]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toMap will convert any Traversable to a Map. How it's
|
||||
| used, depends on the original collection, if it's a List or Seq,
|
||||
| it should be of parameterized type Tuple2.""") {
|
||||
val list = List(("Phoenix" -> "Arizona"), ("Austin" -> "Texas"))
|
||||
val result = list.toMap
|
||||
result.isInstanceOf[Map[String, String]] should be(true)
|
||||
}
|
||||
|
||||
koan("""toMap will convert a Set to a Map,
|
||||
| it should be of parameterized type Tuple2.""") {
|
||||
val set = Set(("Phoenix" -> "Arizona"), ("Austin" -> "Texas"))
|
||||
val result = set.toMap
|
||||
result.isInstanceOf[Map[String, String]] should be(true)
|
||||
}
|
||||
|
||||
koan("""isEmpty is pretty self evident""") {
|
||||
val map = Map("Phoenix" -> "Arizona", "Austin" -> "Texas")
|
||||
map.isEmpty should be(false)
|
||||
|
||||
val set = Set()
|
||||
set.isEmpty should be(true)
|
||||
}
|
||||
|
||||
koan("""nonEmpty is pretty self evident too""") {
|
||||
val map = Map("Phoenix" -> "Arizona", "Austin" -> "Texas")
|
||||
map.nonEmpty should be(true)
|
||||
|
||||
val set = Set()
|
||||
set.nonEmpty should be(false)
|
||||
}
|
||||
|
||||
koan("""size provides the size of the traversable""") {
|
||||
val map = Map("Phoenix" -> "Arizona", "Austin" -> "Texas")
|
||||
map.size should be(2)
|
||||
}
|
||||
|
||||
koan("""hasDefiniteSize will return true if there is traversable that has a
|
||||
finite end, otherwise false""") {
|
||||
val map = Map("Phoenix" -> "Arizona", "Austin" -> "Texas")
|
||||
map.hasDefiniteSize should be(true)
|
||||
|
||||
import Stream.cons
|
||||
val stream = cons(0, cons(1, Stream.empty))
|
||||
stream.hasDefiniteSize should be(false)
|
||||
}
|
||||
|
||||
koan("""head will return the first element of an ordered collection, or some random
|
||||
| element if order is not defined like in a Set or Map""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.head should be(10)
|
||||
}
|
||||
|
||||
koan("""headOption will return the first element as an Option of an order collection,
|
||||
| or some random element if order is not defined. If a first element
|
||||
| is not available, then None is returned""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.headOption should be(Some(10))
|
||||
|
||||
val list2 = List()
|
||||
list2.headOption should be(None)
|
||||
}
|
||||
|
||||
koan("""last will return the last element of an ordered collection, or some random
|
||||
| element if order is not defined like in a Set or Map""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.last should be(22)
|
||||
}
|
||||
|
||||
koan("""lastOption will return the first element as an Option of an order collection,
|
||||
| or some random element if order is not defined. If a first element
|
||||
| is not available, then None is returned""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.lastOption should be(Some(22))
|
||||
|
||||
val list2 = List()
|
||||
list2.lastOption should be(None)
|
||||
}
|
||||
|
||||
koan("""find will locate an the first item that matches a predicate p as Some or None if
|
||||
| an element is not found""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.find(_ % 2 != 0) should be(Some(19))
|
||||
|
||||
val list2 = List(4, 8, 16)
|
||||
list2.find(_ % 2 != 0) should be(None)
|
||||
}
|
||||
|
||||
koan("""tail will return the rest of the collection without the head""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.tail should be(List(19, 45, 1, 22))
|
||||
}
|
||||
|
||||
koan("""init will return the rest of the collection without the last""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.init should be(List(10, 19, 45, 1))
|
||||
}
|
||||
|
||||
koan("""Given a `from` index, and a `to` index, slice will return the part of the
|
||||
| collection including `from`, and excluding `to`""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.slice(1, 3) should be(List(19, 45))
|
||||
}
|
||||
|
||||
koan("""Take will return the the first number of elements given.""") {
|
||||
val list = List(10, 19, 45, 1, 22)
|
||||
list.take(3) should be(List(10, 19, 45))
|
||||
}
|
||||
|
||||
koan("""Take is used often with Streams, and Streams after all are Traversable""") {
|
||||
def streamer(v: Int): Stream[Int] = cons(v, streamer(v + 1))
|
||||
val a = streamer(2)
|
||||
(a take 3 toList) should be(List(2, 3, 4))
|
||||
}
|
||||
|
||||
koan("""Drop will take the rest of the Traversable except
|
||||
| the number of elements given""") {
|
||||
def streamer(v: Int): Stream[Int] = cons(v, streamer(v + 1))
|
||||
val a = streamer(2)
|
||||
((a drop 6) take 3).toList should be(List(8, 9, 10))
|
||||
}
|
||||
|
||||
koan("""takeWhile will continually accumulate elements until a predicate
|
||||
| is no longer satisfied. In this koan, TreeSet is Traversable.
|
||||
| TreeSet also is also sorted.""") {
|
||||
val treeSet = TreeSet(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
treeSet.takeWhile(_ < 100) should be(TreeSet(4, 5, 10, 39, 44, 87))
|
||||
}
|
||||
|
||||
koan("""dropWhile will continually drop elements until a predicate
|
||||
| is no longer satisfied. Again, TreeSet is Traversable.
|
||||
| TreeSet also is also sorted.""") {
|
||||
val treeSet = TreeSet(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
treeSet.dropWhile(_ < 100) should be(TreeSet(100, 200))
|
||||
}
|
||||
|
||||
koan("""filter will take out all elements that don't satisfy a predicate. An
|
||||
| Array is also Traversable.""") {
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
array.filter(_ < 100) should be(Array(87, 44, 5, 4, 10, 39))
|
||||
}
|
||||
|
||||
koan("""filterNot will take out all elements that satisfy a predicate. An
|
||||
| Array is also Traversable.""") {
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
array.filterNot(_ < 100) should be(Array(200, 100))
|
||||
}
|
||||
|
||||
koan("""splitAt will split a Traversable at a position, returning a 2 product
|
||||
| Tuple. Array is Traversable. splitAt is also defined as
|
||||
| (xs take n, xs drop n)""") {
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = array splitAt 3
|
||||
result._1 should be(Array(87, 44, 5))
|
||||
result._2 should be(Array(4, 200, 10, 39, 100))
|
||||
}
|
||||
|
||||
koan("""span will split a Traversable according to predicate, returning
|
||||
| a 2 product Tuple. Array is Traversable, span
|
||||
| is also defined as (xs takeWhile p, xs dropWhile p)""") {
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = array span (_ < 100)
|
||||
result._1 should be(Array(87, 44, 5, 4))
|
||||
result._2 should be(Array(200, 10, 39, 100))
|
||||
}
|
||||
|
||||
koan("""partition will split a Traversable according to predicate, return
|
||||
| a 2 product Tuple. The left side are the elements satisfied by
|
||||
| the predicate, the right side is not. Array is Traversable,
|
||||
| partition is also defined as (xs filter p, xs filterNot p)""") {
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = array partition (_ < 100)
|
||||
result._1 should be(Array(87, 44, 5, 4, 10, 39))
|
||||
result._2 should be(Array(200, 100))
|
||||
}
|
||||
|
||||
koan("""groupBy will categorize a Traversable according to function, and return
|
||||
a map with the results. This koan uses Partial Function chaining. If you are
|
||||
still unfamiliar with PartialFunctions, see AboutPartialFunctions koans.""") {
|
||||
|
||||
val array = Array(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
|
||||
val oddAndSmallPartial: PartialFunction[Int, String] = {
|
||||
case x: Int if (x % 2 != 0 && x < 100) => "Odd and less than 100"
|
||||
}
|
||||
|
||||
val evenAndSmallPartial: PartialFunction[Int, String] = {
|
||||
case x: Int if (x != 0 && x % 2 == 0 && x < 100) => "Even and less than 100"
|
||||
}
|
||||
|
||||
val negativePartial: PartialFunction[Int, String] = {
|
||||
case x: Int if (x < 0) => "Negative Number"
|
||||
}
|
||||
|
||||
val largePartial: PartialFunction[Int, String] = {
|
||||
case x: Int if (x > 99) => "Large Number"
|
||||
}
|
||||
|
||||
val zeroPartial: PartialFunction[Int, String] = {
|
||||
case x: Int if (x == 0) => "Zero"
|
||||
}
|
||||
|
||||
val result = array groupBy {
|
||||
oddAndSmallPartial orElse
|
||||
evenAndSmallPartial orElse
|
||||
negativePartial orElse
|
||||
largePartial orElse
|
||||
zeroPartial
|
||||
}
|
||||
|
||||
(result("Even and less than 100") size) should be(3)
|
||||
(result("Large Number") size) should be(2)
|
||||
}
|
||||
|
||||
koan("""forall will determine if a predicate is valid for all members of a
|
||||
| Traversable.""") {
|
||||
val list = List(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = list forall (_ < 100)
|
||||
result should be(false)
|
||||
}
|
||||
|
||||
koan("""`exists` should've been called `forsome`. `exists` will determine if a predicate
|
||||
| is valid for some members of a Traversable.""") {
|
||||
val list = List(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = list exists (_ < 100)
|
||||
result should be(true)
|
||||
}
|
||||
|
||||
koan("""`count` will count the number of elements that satisfy a predicate
|
||||
| in a Traversable.""") {
|
||||
val list = List(87, 44, 5, 4, 200, 10, 39, 100)
|
||||
val result = list count (_ < 100)
|
||||
result should be(6)
|
||||
}
|
||||
|
||||
koan(""" `/:` or `foldLeft` will combine an operation starting with a seed and combining from the left. Fold Left
|
||||
| is defined as (seed /: list), where seed is the initial value. Once the fold is established, you
|
||||
| provide a function that takes two arguments. The first argument is the running total of the operation,
|
||||
| and the second element is the next element of the list.
|
||||
|
|
||||
| Given a Traversable (x1, x2, x3, x4), an initial value of init, an operation op,
|
||||
| foldLeft is defined as: (((init op x1) op x2) op x3) op x4)""") {
|
||||
val list = List(5, 4, 3, 2, 1)
|
||||
val result = (0 /: list) {
|
||||
(`running total`, `next element`) => `running total` - `next element`
|
||||
}
|
||||
result should be(-15)
|
||||
|
||||
val result2 = list.foldLeft(0) {
|
||||
(`running total`, `next element`) => `running total` - `next element`
|
||||
}
|
||||
result2 should be(-15)
|
||||
|
||||
val result3 = (0 /: list)(_ - _) //Short hand
|
||||
result3 should be(-15)
|
||||
|
||||
val result4 = list.foldLeft(0)(_ - _)
|
||||
result4 should be(-15)
|
||||
|
||||
(((((0 - 5) - 4) - 3) - 2) - 1) should be(-15)
|
||||
}
|
||||
|
||||
koan(""" `:\` or foldRight` will combine an operation starting with a seed and combining from the right. Fold right
|
||||
| is defined as (list :\ seed), where seed is the initial value. Once the fold is established, you
|
||||
| provide a function that takes two elements. The first is the next element of the list, and the
|
||||
| second element is the running total of the operation.
|
||||
|
|
||||
| Given a Traversable (x1, x2, x3, x4), an initial value of init, an operation op,
|
||||
| foldRight is defined as: x1 op (x2 op (x3 op (x4 op init)))""") {
|
||||
|
||||
val list = List(5, 4, 3, 2, 1)
|
||||
val result = (list :\ 0) {
|
||||
(`next element`, `running total`) => `next element` - `running total`
|
||||
}
|
||||
result should be(3)
|
||||
|
||||
val result2 = (list :\ 0) {
|
||||
(`next element`, `running total`) => `next element` - `running total`
|
||||
}
|
||||
result2 should be(3)
|
||||
|
||||
val result3 = (list :\ 0)(_ - _) //Short hand
|
||||
result3 should be(3)
|
||||
|
||||
val result4 = list.foldRight(0)(_ - _)
|
||||
result4 should be(3)
|
||||
|
||||
(5 - (4 - (3 - (2 - (1 - 0))))) should be(3)
|
||||
}
|
||||
|
||||
koan("""`reduceLeft` is the similar to foldLeft, except that the seed is the head value""") {
|
||||
val intList = List(5, 4, 3, 2, 1)
|
||||
intList.reduceLeft {
|
||||
_ + _
|
||||
} should be(15)
|
||||
|
||||
val stringList = List("Do", "Re", "Me", "Fa", "So", "La", "Te", "Do")
|
||||
stringList.reduceLeft {
|
||||
_ + _
|
||||
} should be("DoReMeFaSoLaTeDo")
|
||||
}
|
||||
|
||||
koan("""`reduceRight` is the similar to foldRight, except that the seed is the last value""") {
|
||||
val intList = List(5, 4, 3, 2, 1)
|
||||
intList.reduceRight {
|
||||
_ + _
|
||||
} should be(15)
|
||||
|
||||
val stringList = List("Do", "Re", "Me", "Fa", "So", "La", "Te", "Do")
|
||||
stringList.reduceRight {
|
||||
_ + _
|
||||
} should be("DoReMeFaSoLaTeDo")
|
||||
}
|
||||
|
||||
koan("""There are some methods that take much of the folding work out by providing basic functionality.
|
||||
| `sum` will add all the elements, product will multiply, min would determine the smallest element, and
|
||||
| `max` the largest.""") {
|
||||
val intList = List(5, 4, 3, 2, 1)
|
||||
intList.sum should be(15)
|
||||
intList.product should be(120)
|
||||
intList.max should be(5)
|
||||
intList.min should be(1)
|
||||
}
|
||||
|
||||
koan("""You would choose foldLeft/reduceLeft or foldRight/reduceRight based on your mathematical goal.
|
||||
| One other reason for deciding is performance. foldLeft is more perfomant since it uses
|
||||
| tail recursion and is optimized. This koan will either work or you will receieve a
|
||||
| StackOverflowError. If you do receive a StackOverflowError, try reducing the MAX_SIZE value.""") {
|
||||
|
||||
val MAX_SIZE = 1000000
|
||||
val reduceLeftStartTime = new java.util.Date
|
||||
((1 to MAX_SIZE) reduceLeft (_ + _))
|
||||
val reduceLeftEndTime = new java.util.Date
|
||||
|
||||
|
||||
val reduceRightStartTime = new java.util.Date
|
||||
((1 to MAX_SIZE) reduceRight (_ + _))
|
||||
val reduceRightEndTime = new java.util.Date
|
||||
|
||||
val totalReduceLeftTime = (reduceLeftEndTime.getTime - reduceLeftStartTime.getTime)
|
||||
val totalReduceRightTime = (reduceRightEndTime.getTime - reduceRightStartTime.getTime)
|
||||
|
||||
(totalReduceRightTime > totalReduceLeftTime) should be(true)
|
||||
}
|
||||
|
||||
koan("""`transpose` will take a traversable of traversables and group them by their position in
|
||||
| it's own traversable. E.g. ((x1, x2),(y1, y2)).transpose = (x1, y1), (x2, y2).
|
||||
| or ((x1, x2),(y1, y2),(z1, z2)).transpose = ((x1, y1, z1), (x2, y2, z2), (x3, y3, z3))""") {
|
||||
val list = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
|
||||
list.transpose should be(List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9)))
|
||||
|
||||
val list2 = List(List(1), List(4))
|
||||
list2.transpose should be(List(List(1, 4)))
|
||||
}
|
||||
|
||||
koan("""`mkString` will format a Traversable using a given string as the delimiter.""") {
|
||||
val list = List(1, 2, 3, 4, 5)
|
||||
list.mkString(",") should be("1,2,3,4,5")
|
||||
}
|
||||
|
||||
koan("""`mkString` will also take a beginning and ending string to surround the list.""") {
|
||||
val list = List(1, 2, 3, 4, 5)
|
||||
list.mkString(">", ",", "<") should be(">1,2,3,4,5<")
|
||||
}
|
||||
|
||||
koan("""`addString` will take a StringBuilder to add the contents of list into the builder.""") {
|
||||
val stringBuilder = new StringBuilder()
|
||||
val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
|
||||
stringBuilder.append("I want all numbers 6-12: ")
|
||||
list.filter(it => it > 5 && it < 13).addString(stringBuilder, ",")
|
||||
stringBuilder.mkString should be("I want all numbers 6-12: 6,7,8,9,10,11,12")
|
||||
}
|
||||
|
||||
koan("Traversables can have views which allow you to efficiently do compound work.") {
|
||||
val lst = List(1, 2, 3)
|
||||
var history = List[String]()
|
||||
|
||||
def addHistory(s: String) {
|
||||
history = history :+ s
|
||||
}
|
||||
|
||||
lst.map {x => addHistory("Doubling %s".format(x)); x * 2}
|
||||
.map {x => addHistory("Adding 1 to %s".format(x)); x + 1}
|
||||
|
||||
history(0) should be("Doubling 1")
|
||||
history(1) should be("Doubling 2")
|
||||
history(2) should be("Doubling 3")
|
||||
history(3) should be("Adding 1 to 2")
|
||||
history(4) should be("Adding 1 to 4")
|
||||
history(5) should be("Adding 1 to 6")
|
||||
|
||||
history = List[String]()
|
||||
|
||||
lst.view.map {x => addHistory("Doubling %s".format(x)); x * 2}
|
||||
.map {x => addHistory("Adding 1 to %s".format(x)); x + 1}.force
|
||||
|
||||
history(0) should be("Doubling 1")
|
||||
history(1) should be("Adding 1 to 2")
|
||||
history(2) should be("Doubling 2")
|
||||
history(3) should be("Adding 1 to 4")
|
||||
history(4) should be("Doubling 3")
|
||||
history(5) should be("Adding 1 to 6")
|
||||
}
|
||||
|
||||
koan("""Views can also accept a `to` and `from` value which takes the substring and performs your view
|
||||
| functions on the subset.""") {
|
||||
val list = List(1,2,3,4,5,6,7,8)
|
||||
list.view(3,6).map(_+2).map(_*10).force should be (List(60,70,80))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
|
||||
class AboutTuples extends KoanSuite with ShouldMatchers {
|
||||
|
||||
koan("Tuples can be created easily") {
|
||||
val tuple = ("apple", "dog")
|
||||
tuple should be("apple", "dog")
|
||||
}
|
||||
|
||||
koan("Tuple items may be accessed individually") {
|
||||
val tuple = ("apple", "dog")
|
||||
val fruit = tuple._1
|
||||
val animal = tuple._2
|
||||
|
||||
fruit should be("apple")
|
||||
animal should be("dog")
|
||||
}
|
||||
|
||||
koan("Tuples may be of mixed type") {
|
||||
val tuple5 = ("a", 1, 2.2, new Date(), BigDecimal(5))
|
||||
|
||||
tuple5._1.isInstanceOf[String]
|
||||
tuple5._2.isInstanceOf[Int]
|
||||
tuple5._3 should be(2.2)
|
||||
tuple5._4.isInstanceOf[Date]
|
||||
tuple5._5 should be(5)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutTypeAliases extends KoanSuite with ShouldMatchers {
|
||||
koan("A type alias merely allows you to call a class by a different name") {
|
||||
case class Student(firstName: String, lastName: String)
|
||||
|
||||
type Pupil = Student
|
||||
val harryPotter = new Pupil("Harry", "Potter")
|
||||
harryPotter.firstName should be("Harry")
|
||||
}
|
||||
|
||||
koan("""Although you can't make an type alias of an singleton object,
|
||||
| you can just assign a singleton object to a var or val to
|
||||
| create the alias.
|
||||
|
|
||||
| When you assign a val or var to a singleton object,
|
||||
| the type is <object-name>.type.""") {
|
||||
|
||||
object StarGaze {
|
||||
def lookOntoTheSky = "I see Scorpio to the south"
|
||||
}
|
||||
|
||||
val Observatory = StarGaze
|
||||
val LoversLookout: Observatory.type = Observatory
|
||||
|
||||
Observatory.lookOntoTheSky should be("I see Scorpio to the south")
|
||||
LoversLookout.lookOntoTheSky should be("I see Scorpio to the south")
|
||||
}
|
||||
|
||||
koan("""You can use <object-name>.type as a method parameter to accept singleton objects.""") {
|
||||
object StarGaze {
|
||||
def lookOntoTheSky = "I see Scorpio to the south"
|
||||
}
|
||||
|
||||
def lookout(starGazer:StarGaze.type) = {
|
||||
starGazer.lookOntoTheSky
|
||||
}
|
||||
lookout(StarGaze) should be ("I see Scorpio to the south")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutTypeBounds extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class Fruit
|
||||
|
||||
abstract class Citrus extends Fruit
|
||||
|
||||
class Orange extends Citrus
|
||||
|
||||
class Tangelo extends Citrus
|
||||
|
||||
class Apple extends Fruit
|
||||
|
||||
class Banana extends Fruit
|
||||
|
||||
koan("""You can declare an upper bound of type using the <: operator. The <: operator designates that the
|
||||
| operator on the left must be a subtype of the type on the right.""") {
|
||||
|
||||
class MyContainer[A <: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
|
||||
citrusBasket.set(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
citrusBasket.set(new Tangelo)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Variance notations are for assigment while bounds are rules on containment.
|
||||
| Although variance and bounds are different, they can be used
|
||||
| with one another. In this koan we can put in all anything that is a Citrus or any of
|
||||
| subclasses and we can assign it to variables
|
||||
| with a type of Citrus or any of its superclasses.""") {
|
||||
|
||||
class MyContainer[+A <: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange) //+A allows the assignment
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer[Citrus](new Tangelo) //+A allows the assignment
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3: MyContainer[Citrus] = new MyContainer[Tangelo](new Tangelo) //+A allows the assignment
|
||||
citrusBasket3.contents should be("Tangelo")
|
||||
|
||||
val citrusBasket4: MyContainer[Tangelo] = new MyContainer[Tangelo](new Tangelo) //+A allows the assignment
|
||||
citrusBasket4.contents should be("Tangelo")
|
||||
|
||||
val citrusBasket5: MyContainer[Citrus] = citrusBasket4
|
||||
citrusBasket5.contents should be("Tangelo")
|
||||
}
|
||||
|
||||
koan("""Variance notations are for assigment while bounds are rules on containment. Although variance and bounds
|
||||
| are different, they can be used
|
||||
| with one another. This koan will allow all citruses to be placed in the container, and will allow
|
||||
| the container to be assigned to
|
||||
| variables of a type less than Citrus.""") {
|
||||
|
||||
class MyContainer[+A <: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket = new MyContainer[Citrus](new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
val citrusBasket2 = new MyContainer[Orange](new Orange)
|
||||
citrusBasket2.contents should be("Orange")
|
||||
}
|
||||
|
||||
koan("""This koan uses the contravariant type upper bound by Citrus, therefore any object can be created that is a Citrus or any
|
||||
| of its subtypes.""") {
|
||||
|
||||
class MyContainer[-A <: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Tangelo")
|
||||
|
||||
val citrusBasket4: MyContainer[Tangelo] = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Tangelo")
|
||||
|
||||
val citrusBasket5 = new MyContainer(new Tangelo)
|
||||
citrusBasket5.contents should be("Tangelo")
|
||||
}
|
||||
|
||||
|
||||
koan("""Lower bounds; invariant""") {
|
||||
|
||||
class MyContainer[A >: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus")
|
||||
|
||||
val citrusBasket4 = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Lower bounds contravariant""") {
|
||||
class MyContainer[-A >: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus")
|
||||
|
||||
val citrusBasket4 = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Lower bounds covariant""") {
|
||||
class MyContainer[+A >: Citrus](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus") //Why? very important!
|
||||
|
||||
val citrusBasket5 = new MyContainer(new Tangelo)
|
||||
citrusBasket5.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Both upper and lower bounds; invariant""") {
|
||||
class MyContainer[A >: Citrus <: AnyRef](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus")
|
||||
|
||||
val citrusBasket4 = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Both upper and lower bounds; contravariant""") {
|
||||
class MyContainer[-A >: Citrus <: AnyRef](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus")
|
||||
|
||||
val citrusBasket4 = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Citrus")
|
||||
}
|
||||
|
||||
koan("""Both upper and lower bounds; covariant""") {
|
||||
class MyContainer[+A >: Citrus <: AnyRef](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
val citrusBasket2: MyContainer[Citrus] = new MyContainer(new Tangelo)
|
||||
citrusBasket2.contents should be("Citrus")
|
||||
|
||||
val citrusBasket3 = new MyContainer(new Tangelo)
|
||||
citrusBasket3.contents should be("Citrus")
|
||||
|
||||
val citrusBasket4 = new MyContainer(new Tangelo)
|
||||
citrusBasket4.contents should be("Citrus")
|
||||
|
||||
val citrusBasket5: MyContainer[Fruit] = new MyContainer(new Tangelo)
|
||||
citrusBasket5.contents should be("Citrus")
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Do I need koans for overriding and subclassing?
|
||||
//TODO: Check if subclasses of parents who implement traits still get traits
|
||||
//TODO: koan("() => Unit is a type, and so is => Unit, and so is Int, Int => Int")
|
||||
//TODO: Do we have anything for :_* to fit it into an Array, there was some trick I am forgetting.
|
||||
|
||||
|
||||
//(02:26:52 PM) retronym: M[A] <: M[B] if A <: B Covariance
|
||||
//(02:27:12 PM) retronym: M[A] <: M[B] if B <: A Contravariance
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutTypeProjections extends KoanSuite with ShouldMatchers {
|
||||
|
||||
|
||||
class Fruit
|
||||
|
||||
class Citrus extends Fruit
|
||||
|
||||
class Orange extends Citrus
|
||||
|
||||
class Tangelo extends Citrus
|
||||
|
||||
class Apple extends Fruit
|
||||
|
||||
class Banana extends Fruit
|
||||
|
||||
koan("In generic form3") {
|
||||
trait X {
|
||||
type Y <: Fruit
|
||||
}
|
||||
|
||||
class TangeloBasket extends X {
|
||||
type Y = Tangelo
|
||||
}
|
||||
|
||||
class AppleBasket extends X {
|
||||
type Y = Apple
|
||||
}
|
||||
classOf[TangeloBasket#Y].getSimpleName should be("Tangelo")
|
||||
classOf[AppleBasket#Y].getSimpleName should be("Apple")
|
||||
}
|
||||
|
||||
koan("In generic form4") {
|
||||
trait X {
|
||||
type Y <: Fruit
|
||||
|
||||
def whatsFeedingMe: String
|
||||
}
|
||||
|
||||
val x = new X {
|
||||
type Y = Tangelo
|
||||
|
||||
override def whatsFeedingMe = classOf[Y].getSimpleName
|
||||
}
|
||||
|
||||
val z = new X {
|
||||
type Y = Orange
|
||||
|
||||
override def whatsFeedingMe = classOf[Y].getSimpleName
|
||||
}
|
||||
|
||||
println(x.whatsFeedingMe)
|
||||
println(z.whatsFeedingMe)
|
||||
}
|
||||
|
||||
|
||||
koan("In generic form65") {
|
||||
trait X {
|
||||
type Y
|
||||
}
|
||||
|
||||
val x = new X {
|
||||
type Y = Tangelo
|
||||
}
|
||||
|
||||
val z = new X {
|
||||
type Y = Orange
|
||||
}
|
||||
|
||||
class AwesomeX extends X {
|
||||
type Y = String
|
||||
}
|
||||
|
||||
println(classOf[AwesomeX#Y].getSimpleName)
|
||||
|
||||
val r = new X {
|
||||
type Y = Apple
|
||||
}
|
||||
|
||||
println(classOf[r.Y].getSimpleName)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
scala> val r = new X {
|
||||
| type Y = String
|
||||
| }
|
||||
<console>:9: error: overriding type Y in trait X, which equals String;
|
||||
type Y needs `override' modifier
|
||||
type Y = String
|
||||
^
|
||||
|
||||
scala> val r = new X {
|
||||
| override type Y = Int
|
||||
| }
|
||||
<console>:9: error: overriding type Y in trait X, which equals String;
|
||||
type Y has incompatible type
|
||||
override type Y = Int
|
||||
^
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutTypeSignatures extends KoanSuite with ShouldMatchers {
|
||||
koan("In Java you declare a generic type within a <>, in Scala it is []") {
|
||||
val z: List[String] = "Do" :: "Re" :: "Mi" :: "Fa" :: "So" :: "La" :: "Te" :: "Do" :: Nil
|
||||
z(3) should be("Fa")
|
||||
}
|
||||
|
||||
koan("Most of the time, Scala will infer the type and [] are optional") {
|
||||
val z = "Do" :: "Re" :: "Mi" :: "Fa" :: "So" :: "La" :: "Te" :: "Do" :: Nil //Infers that the list assigned to variable is of type List[String]
|
||||
}
|
||||
|
||||
koan("A trait can be declared containing a type, where a concrete implmenter will satisfy the type") {
|
||||
trait Randomizer[A] {
|
||||
def draw: A
|
||||
}
|
||||
|
||||
class IntRandomizer extends Randomizer[Int] {
|
||||
def draw = {
|
||||
import util.Random
|
||||
Random.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
val intRand = new IntRandomizer
|
||||
intRand.draw should be < Int.MaxValue
|
||||
}
|
||||
|
||||
koan("Class meta-information can be retrieved by class name by using classOf[className]") {
|
||||
classOf[String].getCanonicalName should be("java.lang.String")
|
||||
classOf[String].getSimpleName should be("String")
|
||||
}
|
||||
|
||||
koan("Class meta-information can be derived from an object reference using getClass()") {
|
||||
val zoom = "zoom"
|
||||
zoom.getClass should be(classOf[String])
|
||||
zoom.getClass.getCanonicalName should be("java.lang.String")
|
||||
zoom.getClass.getSimpleName should be("String")
|
||||
}
|
||||
|
||||
koan("isInstanceOf[className] is used to determine the if an object reference is an instance of given class") {
|
||||
trait Randomizer[A] {
|
||||
def draw: A
|
||||
}
|
||||
|
||||
class IntRandomizer extends Randomizer[Int] {
|
||||
override def draw = {
|
||||
import util.Random
|
||||
Random.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
val intRand = new IntRandomizer
|
||||
intRand.draw.isInstanceOf[Int] should be(true)
|
||||
}
|
||||
|
||||
koan("asInstanceOf[className] is used to cast one reference to another") {
|
||||
trait Randomizer[A] {
|
||||
def draw: A
|
||||
}
|
||||
|
||||
class IntRandomizer extends Randomizer[Int] {
|
||||
override def draw = {
|
||||
import util.Random
|
||||
Random.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
val intRand = new IntRandomizer
|
||||
val rand = intRand
|
||||
val intRand2 = rand.asInstanceOf[IntRandomizer]
|
||||
}
|
||||
|
||||
koan("""asInstanceOf[className] will throw a ClassCastException if a class derived from
|
||||
| and the class target aren't from the same inheritance branch""") {
|
||||
trait Randomizer[A] {
|
||||
def draw: A
|
||||
}
|
||||
|
||||
class IntRandomizer extends Randomizer[Int] {
|
||||
def draw = {
|
||||
import util.Random
|
||||
Random.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
val intRand = new IntRandomizer
|
||||
|
||||
intercept[ClassCastException] {
|
||||
intRand.asInstanceOf[String] //intRand cannot be cast to String
|
||||
}
|
||||
}
|
||||
|
||||
koan("null.asInstanceOf[className] can be used to generate basic default values") {
|
||||
null.asInstanceOf[String] should be(null)
|
||||
null.asInstanceOf[Int] should be(0)
|
||||
null.asInstanceOf[Short] should be(0)
|
||||
}
|
||||
|
||||
|
||||
/* TODO: This probably needs to move to another category,
|
||||
TODO: since this class is supposed to be about type signatures */
|
||||
koan("""Classes can be abstract. Abstract classes can define some methods
|
||||
| concretely or may rely on it\'s subclasses to implement.
|
||||
| If a method has no body and is in
|
||||
| an abstract class, the method is considered abstract.""") {
|
||||
abstract class Parent {
|
||||
def add(x: Int): Int
|
||||
|
||||
//this is considered abstract
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
def add(x: Int): Int = x + 3
|
||||
}
|
||||
|
||||
new Child().add(3) should be(6)
|
||||
}
|
||||
|
||||
/* TODO: This probably needs to move to another category,
|
||||
TODO: since this class is supposed to be about type signatures */
|
||||
koan("""Same koan as above. Except that concrete methods
|
||||
| can have the modifier override to designate that it overrides a parent class.""") {
|
||||
abstract class Parent {
|
||||
def add(x: Int): Int
|
||||
|
||||
//this is considered abstract
|
||||
}
|
||||
|
||||
class Child extends Parent {
|
||||
override def add(x: Int): Int = x + 3
|
||||
|
||||
//explicitly
|
||||
}
|
||||
|
||||
new Child().add(3) should be(6)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
class AboutTypeVariance extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class Fruit
|
||||
|
||||
abstract class Citrus extends Fruit
|
||||
|
||||
class Orange extends Citrus
|
||||
|
||||
class Tangelo extends Citrus
|
||||
|
||||
class Apple extends Fruit
|
||||
|
||||
class Banana extends Fruit
|
||||
|
||||
koan("""Using type inference the type that you instantiate it will be the val or var reference type""") {
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val fruitBasket = new MyContainer(new Orange())
|
||||
fruitBasket.contents should be("Orange")
|
||||
}
|
||||
|
||||
|
||||
koan("""You can explicitly declare the type variable of the object during instantiation""") {
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val fruitBasket = new MyContainer[Fruit](new Orange())
|
||||
fruitBasket.contents should be("Fruit")
|
||||
}
|
||||
|
||||
koan("You can coerece your object to a type.") {
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val fruitBasket: MyContainer[Fruit] = new MyContainer(new Orange())
|
||||
fruitBasket.contents should be("Fruit")
|
||||
}
|
||||
|
||||
// That one probably blew your mind. Now if you assign a type to the instantiation,
|
||||
// that's different to the variable type, you'll have problems. You may want to take time after this
|
||||
// o compare this koan with the previous koan to compare and contrast. """) {
|
||||
|
||||
|
||||
koan("variable type must match assigned type") {
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
// Uncomment the following line
|
||||
// val fruitBasket:MyContainer[Fruit] = new MyContainer[Orange](new Orange())
|
||||
}
|
||||
|
||||
// So, if you want to set a Fruit basket to an orange basket so how do we fix that? You make it covariant using +.
|
||||
// This will allow you to set the your container to a either a variable with the same type or parent type.
|
||||
// In other words, you can assign MyContainer[Fruit] or MyContainer[Citrus]."""
|
||||
koan("covariance lets you specify the container of that type or parent type") {
|
||||
|
||||
class MyContainer[+A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val fruitBasket: MyContainer[Fruit] = new MyContainer[Orange](new Orange())
|
||||
fruitBasket.contents should be("Orange")
|
||||
}
|
||||
|
||||
// The problem with covariance is that you can't mutate, set, or change the object since
|
||||
// it has to guarantee that what you put in has to be that type. In other words the reference is a fruit basket,
|
||||
// but we still have to make sure that no other fruit can be placed in our orange basket"""
|
||||
|
||||
koan("mutating an object is not allowed with covariance") {
|
||||
|
||||
class MyContainer[+A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] val item = a
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val fruitBasket: MyContainer[Fruit] = new MyContainer[Orange](new Orange())
|
||||
fruitBasket.contents should be("Orange")
|
||||
|
||||
class NavelOrange extends Orange //Creating a subtype to prove a point
|
||||
// val navelOrangeBasket: MyContainer[NavelOrange] = new MyContainer[Orange](new Orange()) //Bad!
|
||||
// val tangeloBasket: MyContainer[Tangelo] = new MyContainer[Orange](new Orange()) //Bad!
|
||||
}
|
||||
|
||||
// Declaring - indicates contravariance variance.
|
||||
// Using - you can apply any container with a certain type to a container with a superclass of that type.
|
||||
// This is reverse to covariant. In our example, we can set a citrus basket to
|
||||
// an orange or tangelo basket. Since an orange or tangelo basket is a citrus basket
|
||||
|
||||
koan("contravariance is the opposite of covariance") {
|
||||
|
||||
class MyContainer[-A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
val orangeBasket: MyContainer[Orange] = new MyContainer[Citrus](new Tangelo)
|
||||
orangeBasket.contents should be("Citrus")
|
||||
val tangeloBasket: MyContainer[Tangelo] = new MyContainer[Citrus](new Orange)
|
||||
tangeloBasket.contents should be("Citrus")
|
||||
|
||||
val orangeBasketReally: MyContainer[Orange] = tangeloBasket.asInstanceOf[MyContainer[Orange]]
|
||||
orangeBasketReally.contents should be("Citrus")
|
||||
orangeBasketReally.set(new Orange())
|
||||
}
|
||||
|
||||
// Declaring contravariance variance with - also means that the container cannot be accessed with a getter or
|
||||
// or some other accessor, since that would cause type inconsistency. In our example, you can put an orange
|
||||
// or a tangelo into a citrus basket. Problem is, if you have a reference to an orange basket,
|
||||
// and if you believe that you have an orange basket then you shouldn't expect to get a
|
||||
// tangelo out of it.
|
||||
koan("A reference to a parent type means you cannot anticipate getting a more specific type") {
|
||||
|
||||
class MyContainer[-A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
val orangeBasket: MyContainer[Orange] = new MyContainer[Citrus](new Tangelo)
|
||||
orangeBasket.contents should be("Citrus")
|
||||
val tangeloBasket: MyContainer[Tangelo] = new MyContainer[Citrus](new Orange)
|
||||
tangeloBasket.contents should be("Citrus")
|
||||
}
|
||||
|
||||
// Declaring neither -/+, indicates invariance variance. You cannot use a superclass
|
||||
// variable reference (\"contravariant\" position) or a subclass variable reference (\"covariant\" position)
|
||||
// of that type. In our example, this means that if you create a citrus basket you can only reference that
|
||||
// that citrus basket with a citrus variable only.
|
||||
|
||||
koan("invariance means you need to specify the type exactly") {
|
||||
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
}
|
||||
|
||||
|
||||
koan("""Declaring a type as invariant also means that you can both mutate and access elements from an object of generic type""") {
|
||||
|
||||
class MyContainer[A](a: A)(implicit manifest: scala.reflect.Manifest[A]) {
|
||||
private[this] var item = a
|
||||
|
||||
def set(a: A) {
|
||||
item = a
|
||||
}
|
||||
|
||||
def get = item
|
||||
|
||||
def contents = manifest.erasure.getSimpleName
|
||||
}
|
||||
|
||||
val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
|
||||
|
||||
citrusBasket.set(new Orange)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
|
||||
citrusBasket.set(new Tangelo)
|
||||
citrusBasket.contents should be("Citrus")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import support.KoanSuite
|
||||
import support.BlankValues._
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import java.util.Date
|
||||
|
||||
class AboutUniformAccessPrinciple extends KoanSuite with ShouldMatchers {
|
||||
|
||||
class CalculatesAgeUsingMethod(var currentYear: Int, birthYear: Int) {
|
||||
|
||||
def age = currentYear - birthYear
|
||||
|
||||
// calculated when method is called
|
||||
}
|
||||
|
||||
class CalculatesAgeUsingProperty(var currentYear: Int, birthYear: Int) {
|
||||
// does age stay up to date if defined as a var instead of a val?
|
||||
val age = currentYear - birthYear
|
||||
// calculated at instantiation, returns property when called
|
||||
}
|
||||
|
||||
koan("Can access age as parameterless method") {
|
||||
val me = new CalculatesAgeUsingMethod(2010, 2003)
|
||||
me.age should be(7)
|
||||
}
|
||||
|
||||
koan("Can access age as property") {
|
||||
val me = new CalculatesAgeUsingProperty(2010, 2003)
|
||||
me.age should be(7)
|
||||
}
|
||||
|
||||
koan("Cannot add parameter to Method invocation") {
|
||||
val me = new CalculatesAgeUsingMethod(2010, 2003)
|
||||
// uncomment following line to see what happens if you try to access parameterless method with parens
|
||||
//me.age() should be (7)
|
||||
}
|
||||
koan("What happens when I update current year using property") {
|
||||
val me = new CalculatesAgeUsingProperty(2010, 2003)
|
||||
|
||||
|
||||
me.currentYear = 2011
|
||||
me.age should be(7)
|
||||
}
|
||||
|
||||
koan("What happens when I update current year using method") {
|
||||
val me = new CalculatesAgeUsingMethod(2010, 2003)
|
||||
|
||||
|
||||
me.currentYear = 2011
|
||||
me.age should be(8)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import support.KoanSuite
|
||||
|
||||
class AboutValAndVar extends KoanSuite with ShouldMatchers {
|
||||
koan("vars may be reassigned") {
|
||||
var a = 5
|
||||
a should be(5)
|
||||
|
||||
a = 7
|
||||
a should be(7)
|
||||
}
|
||||
|
||||
koan("vals may not be reassigned") {
|
||||
val a = 5
|
||||
a should be(5)
|
||||
|
||||
// What happens if you uncomment these lines?
|
||||
// a = 7
|
||||
// a should be (5)
|
||||
}
|
||||
|
||||
koan("vals or vars can have the same name as a keyword as long as it's surrounded by `") {
|
||||
val `class` = "MyClassName"
|
||||
`class` should be("MyClassName")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.functionalkoans.forscala
|
||||
|
||||
import org.scalatest.Suite
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class PathToEnlightenment extends Suite {
|
||||
override def nestedSuites = List(new AboutAsserts,
|
||||
new AboutValAndVar,
|
||||
new AboutLiteralBooleans,
|
||||
new AboutLiteralNumbers,
|
||||
new AboutLiteralStrings,
|
||||
new AboutConstructors,
|
||||
new AboutTuples,
|
||||
new AboutLists,
|
||||
new AboutMaps,
|
||||
new AboutSets,
|
||||
new AboutSequencesAndArrays,
|
||||
new AboutMutableMaps,
|
||||
new AboutMutableSets,
|
||||
new AboutOptions,
|
||||
new AboutPatternMatching,
|
||||
new AboutCaseClasses,
|
||||
new AboutHigherOrderFunctions,
|
||||
new AboutPartiallyAppliedFunctions,
|
||||
new AboutPartialFunctions,
|
||||
new AboutForExpressions,
|
||||
new AboutEnumerations,
|
||||
new AboutEmptyValues,
|
||||
new AboutParentClasses,
|
||||
new AboutNamedAndDefaultArguments,
|
||||
new AboutInfixPrefixAndPostfixOperators,
|
||||
new AboutInfixTypes,
|
||||
new AboutAccessModifiers,
|
||||
new AboutTypeSignatures,
|
||||
new AboutTraits,
|
||||
new AboutImportsAndPackages,
|
||||
new AboutPreconditions,
|
||||
new AboutUniformAccessPrinciple,
|
||||
new AboutImplicits,
|
||||
new AboutInteroperability,
|
||||
new AboutManifests
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.functionalkoans.forscala.support
|
||||
|
||||
object BlankValues {
|
||||
|
||||
class ReplaceWithCorrectException extends Exception
|
||||
|
||||
val __ = "Should be filled in"
|
||||
|
||||
class ___ extends ReplaceWithCorrectException {
|
||||
override def toString() = "___"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
object Blankout {
|
||||
def blank[T](t: T): T = t
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.functionalkoans.forscala.support
|
||||
|
||||
import org.scalatest.Reporter
|
||||
import org.scalatest.Stopper
|
||||
import org.scalatest.Distributor
|
||||
import org.scalatest.Filter
|
||||
import org.scalatest.Tracker
|
||||
import org.scalatest.FunSuite
|
||||
import org.scalatest.events.Event
|
||||
import org.scalatest.events.TestSucceeded
|
||||
|
||||
trait KoanSuite extends FunSuite {
|
||||
override def runTests(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter,
|
||||
configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) {
|
||||
|
||||
if (testName == null)
|
||||
throw new NullPointerException("testName was null")
|
||||
if (reporter == null)
|
||||
throw new NullPointerException("reporter was null")
|
||||
if (stopper == null)
|
||||
throw new NullPointerException("stopper was null")
|
||||
if (filter == null)
|
||||
throw new NullPointerException("filter was null")
|
||||
if (configMap == null)
|
||||
throw new NullPointerException("configMap was null")
|
||||
if (distributor == null)
|
||||
throw new NullPointerException("distributor was null")
|
||||
if (tracker == null)
|
||||
throw new NullPointerException("tracker was null")
|
||||
|
||||
class KoanReporter(wrappedReporter: Reporter) extends Reporter {
|
||||
var succeeded = false
|
||||
|
||||
override def apply(event: Event) = {
|
||||
event match {
|
||||
case e: TestSucceeded => succeeded = true
|
||||
case _ =>
|
||||
}
|
||||
wrappedReporter(event)
|
||||
}
|
||||
}
|
||||
|
||||
val stopRequested = stopper
|
||||
|
||||
// If a testName is passed to run, just run that, else run the tests returned
|
||||
// by testNames.
|
||||
testName match {
|
||||
case Some(tn) => runTest(tn, reporter, stopRequested, configMap, tracker)
|
||||
case None =>
|
||||
val tests = testNames.iterator
|
||||
var failed = false
|
||||
for (test <- tests) {
|
||||
if (failed == false) {
|
||||
val koanReporter = new KoanReporter(reporter)
|
||||
runTest(test, koanReporter, stopper, configMap, tracker)
|
||||
failed = !koanReporter.succeeded
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def koan(name: String)(fun: => Unit) = test(name.stripMargin)(fun)
|
||||
}
|
||||
Reference in New Issue
Block a user