Skip to content

fix exercise for type contravariance to remove mutation #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions src/main/scala/stdlib/TypeVariance.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,25 +99,26 @@ object TypeVariance extends FlatSpec with Matchers with org.scalaexercises.defin
}

/** 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 are a citrus basket. Contravariance is the opposite of covariance:
* //TODO rework without mutability
*
* def contravarianceVarianceTypeVariance(res0: String, res1: String, res2: String, res3: String) {
* class MyContainer[-A](val a: A)(implicit manifest: scala.reflect.Manifest[A]) {
* def contents = manifest.runtimeClass.getSimpleName
* }
*
* val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
* citrusBasket.contents should be(res0)
* val orangeBasket: MyContainer[Orange] = new MyContainer[Citrus](new Tangelo)
* orangeBasket.contents should be(res1)
* val tangeloBasket: MyContainer[Tangelo] = new MyContainer[Citrus](new Orange)
* tangeloBasket.contents should be(res2)
*
* val orangeBasketReally: MyContainer[Orange] = tangeloBasket.asInstanceOf[MyContainer[Orange]]
* orangeBasketReally.contents should be(res3)
* }
*/

def contravarianceVarianceTypeVariance(res0: String, res1: String, res2: String, res3: String) {
class MyContainer[-A](a: A)(implicit manifest: scala.reflect.Manifest[A]) { //Can't receive a val because it would be in a covariant position
def contents = manifest.runtimeClass.getSimpleName
}

val citrusBasket: MyContainer[Citrus] = new MyContainer[Citrus](new Orange)
citrusBasket.contents should be(res0)
val orangeBasket: MyContainer[Orange] = new MyContainer[Citrus](new Tangelo)
orangeBasket.contents should be(res1)
val tangeloBasket: MyContainer[Tangelo] = new MyContainer[Citrus](new Orange)
tangeloBasket.contents should be(res2)
val bananaBasket: MyContainer[Banana] = new MyContainer[Fruit](new Apple)
bananaBasket.contents should be(res3)

//val fruitBasket: MyContainer[Fruit] = new MyContainer[Citrus](new Orange) //Bad!
//val wrongCitrusBasket: MyContainer[Citrus] = new MyContainer[Orange](new Orange) //Bad!
}

/** 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 citrus basket with a citrus variable only.
*
* Invariance means you need to specify the type exactly:
Expand Down
9 changes: 9 additions & 0 deletions src/test/scala/stdlib/TypeVarianceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ class TypeVarianceSpec extends Spec with Checkers {
)
}

def `contravariance` = {
check(
Test.testSuccess(
TypeVariance.contravarianceVarianceTypeVariance _,
"Citrus" :: "Citrus" :: "Citrus" :: "Fruit" :: HNil
)
)
}

def `invariance` = {
check(
Test.testSuccess(
Expand Down