This commit is contained in:
Tommy Parnell
2018-06-02 14:05:05 -04:00
parent cb23f8e27f
commit 1dff526dea
66 changed files with 5362 additions and 2 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.jar filter=lfs diff=lfs merge=lfs -text

20
.gitignore vendored
View File

@@ -1,2 +1,18 @@
*.class lib_managed
*.log 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
View File

@@ -0,0 +1,6 @@
repo: e433542a2a0d9eb9fbe1b548ca5c76d54d12cb4e
node: 769e22d30ec694417d909efedabe480b8c37d392
branch: default
latesttag: null
latesttagdistance: 138
changessincelatesttag: 143

18
.hgignore Normal file
View 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
View 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
View 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
View 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
View 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
View 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))
}

1
sbt Normal file
View File

@@ -0,0 +1 @@
java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"

3
sbt-launch.jar Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b8af85f4ecc3d07a04fb35a9e056a95de2dd9fe260a1551ba9aca5d50b8d1010
size 937683

2
sbt.bat Normal file
View File

@@ -0,0 +1,2 @@
set SCRIPT_DIR=%~dp0
java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*

View File

@@ -0,0 +1,9 @@
package org.functionalkoans
/**
* Hello world!
*
*/
object App extends scala.App {
println( "Hello World!" )
}

View File

@@ -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();
}
}

View 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

View File

@@ -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)
}
}

View File

@@ -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 {
}

View 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.
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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("\\")
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View 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
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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
*/
}
}

View 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
}
}

View File

@@ -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")
}
}

View 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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View 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
}
}

View File

@@ -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) {
}
}
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View 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)
}
}

View File

@@ -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"))
}
}

View 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)
}
}

View 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")
}
}

View File

@@ -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))
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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

View File

@@ -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
^
*/
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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")
}
}

View File

@@ -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
)
}

View File

@@ -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
}

View File

@@ -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)
}