Showing posts with label Advanced. Show all posts
Showing posts with label Advanced. Show all posts
Monday, May 17, 2010
Instance Type (Abstract type gotcha 1)
In a previous post about abstract types I showed one of the benefits of using abstract types over parameterized types. Abstract Types vs Parameter. The next several posts will feature potential problems you may encounter when using Abstract Types.
I should point out that abstract types are not inherently difficult to understand but they are rather different from anything you will see when you come from the Java world so if you are new to them I would use them with caution at first.
In the abstract types example you will notice that the abstract type 'I' in Foreach is not within the trait Source rather it is outside in the Foreach trait. At first one might consider putting the type in Source rather than Foreach. The naive change can get you in trouble (but there is a couple easy fixes)
Compiling the class results in a compilation error:
jeichar: tmp$ scalac XX.scala
XX.scala:12: error: type mismatch;
found : s.type (with underlying type Foreach.this.Source#I)
required: _2.I where val _2: Foreach.this.Source
def processNext : Unit = source.next(s) match {
^
XX.scala:16: error: type mismatch;
found : value.type (with underlying type Any)
required: A
f(value)
^
two errors found
So what is the problem? The problem is simple but subtle. Notice that source is defined as a def. So calling source 2 times may return 2 different instances of Source. A simple change can fix this. Either change def source : Source to val source : Source. Or change the method foreach to assign the result from source to a val.
I should point out that abstract types are not inherently difficult to understand but they are rather different from anything you will see when you come from the Java world so if you are new to them I would use them with caution at first.
In the abstract types example you will notice that the abstract type 'I' in Foreach is not within the trait Source rather it is outside in the Foreach trait. At first one might consider putting the type in Source rather than Foreach. The naive change can get you in trouble (but there is a couple easy fixes)
- trait Foreach[A] {
- trait Source {
- type I <: java.io.Closeable // moved this line into Source
- def in : I
- def next(in : I) : Option[A]
- }
- def source : Source
-
- def foreach[U](f : A => U) : Unit = {
- val s = source.in
- try {
- def processNext : Unit = source.next(s) match {
- case None =>
- ()
- case Some(value) =>
- f(value)
- processNext
- }
-
- processNext
- } finally {
- // correctly handle exceptions
- s.close
- }
- }
- }
Compiling the class results in a compilation error:
jeichar: tmp$ scalac XX.scala
XX.scala:12: error: type mismatch;
found : s.type (with underlying type Foreach.this.Source#I)
required: _2.I where val _2: Foreach.this.Source
def processNext : Unit = source.next(s) match {
^
XX.scala:16: error: type mismatch;
found : value.type (with underlying type Any)
required: A
f(value)
^
two errors found
So what is the problem? The problem is simple but subtle. Notice that source is defined as a def. So calling source 2 times may return 2 different instances of Source. A simple change can fix this. Either change def source : Source to val source : Source. Or change the method foreach to assign the result from source to a val.
- trait Foreach {
- trait Source {
- type I <: java.io.Closeable // moved this line into Source
- def in : I
- def next(in : I) : Option[Int]
- }
- def source : Source
-
- def foreach[U](f : Int => U) : Unit = {
- // this assignment allows this example to compile
- val sameSource = source
- val s = sameSource.in
- try {
- def processNext : Unit = sameSource.next(s) match {
- case None =>
- ()
- case Some(value) =>
- f(value)
- processNext
- }
-
- processNext
- } finally {
- // correctly handle exceptions
- s.close
- }
- }
- }
Labels:
abstract type,
Advanced,
daily-scala
Wednesday, April 21, 2010
Companion Object Implicits
When a method requires an implicit there are several ways that the implicit is resolved. One way is to search for an implicit definition in the companion object of the required type. For example:
To demonstrate the method put the following code block into a file and run the script:
Running: scala impl.scala should produce:
6
3
32
def x(implicit m:MyClass)
parameter m will search local scope, class hierarchy and the MyClass companion object for an implicit val or def. (More on implicit resolution later).To demonstrate the method put the following code block into a file and run the script:
- class X(val i:Int) {
- def add(implicit x:X)=println(x.i+i)
- }
- object X {
- implicit def xx = new X(3)
- }
- // implicit is obtained from companion object of X
- new X(3).add
- val other = new {
- def print(implicit x:X)=println(x.i)
- }
- // implicit is obtained from companion object of X
- other.print
- implicit def x = new X(32)
- // implicit is obtained local scope
- other.print
Running: scala impl.scala should produce:
6
3
32
Monday, February 15, 2010
Self Annotation vs inheritance
At first glance the "sself annotation" declaration seems similar to extending another class. (For a look at self annotations read the topic: Self Type.) They are completely different but the comparison is understandable since both of them provide access to the functionality of referenced class.
For example both of the following compile:
But the two are completely different. Extender can be mixed in with any class and adds both the "magic" and "myMethod" to the class it is mixed with. SelfType can only be mixed in with a class that extends Base and SelfTyper only adds the method "myMethod" NOT "magic".
Why is the "self annotations" useful? Because it allows several provides a way of declaring dependencies. One can think of the self annotation declaration as the phrase "I am useable with" or "I require a".
The following example demonstrates one possible reason to use self annotations instead of extend.
Note: These examples can be pasted into the REPL but I have shown that here because it would make the examples too long.
The justification for using self annotations here is flexibility. A couple other solutions would be
The next question that comes to mind is why use extends then if self annotation is so flexible? My answer (and I welcome discussion on this point) has three points.
For example both of the following compile:
- class Base {
- def magic = "bibbity bobbity boo!!"
- }
- trait Extender extends Base {
- def myMethod = "I can "+magic
- }
- trait SelfTyper {
- self : Base =>
-
- def myMethod = "I can "+magic
- }
But the two are completely different. Extender can be mixed in with any class and adds both the "magic" and "myMethod" to the class it is mixed with. SelfType can only be mixed in with a class that extends Base and SelfTyper only adds the method "myMethod" NOT "magic".
Why is the "self annotations" useful? Because it allows several provides a way of declaring dependencies. One can think of the self annotation declaration as the phrase "I am useable with" or "I require a".
The following example demonstrates one possible reason to use self annotations instead of extend.
Note: These examples can be pasted into the REPL but I have shown that here because it would make the examples too long.
- import java.io._
- import java.util.{Properties => JProps}
- trait Properties {
- def apply(key:String) : String
- }
- trait XmlProperties extends Properties {
- import scala.xml._
-
- def xml(key:String) = Elem(null,key,Null,TopScope, Text(apply(key)))
- }
- trait JSonProperties extends Properties {
- def json(key:String) : String = "%s : %s".format(key, apply(key))
- }
- trait StreamProperties {
- self : Properties =>
-
- protected def source : InputStream
-
- private val props = new JProps()
- props.load(source)
-
- def apply(key:String) = props.get(key).asInstanceOf[String]
- }
- trait MapProperties {
- self : Properties =>
-
- protected def source : Map[String,String]
- def apply(key:String) = source.apply(key)
- }
- val sampleMap = Map("1" -> "one", "2" -> "two", "3" -> "three")
- val sampleData = """1=one
- 2=two
- 3=three"""
- val sXml = new XmlProperties() with StreamProperties{
- def source = new ByteArrayInputStream(sampleData.getBytes)
- }
- val mXml = new XmlProperties() with MapProperties{
- def source = sampleMap
- }
- val sJSon = new JSonProperties() with StreamProperties{
- def source = new ByteArrayInputStream(sampleData.getBytes)
- }
- val mJSon = new JSonProperties() with MapProperties{
- def source = sampleMap
- }
- sXml.xml("1")
- mXml.xml("2")
- sJSon.json("1")
- mJSon.json("2")
The justification for using self annotations here is flexibility. A couple other solutions would be
- Use subclassing - this is poor solution because there would be an explosion of classes. Instead of having 5 traits you would need 7 traits. Properties, XmlProperties, JSonProperties, XmlStreamProperties, XmlMapProperties, JsonStreamProperties and JsonMapProperties. And if you later wanted to add a new type of properties or a new source like reading from a database then you need 2 new subclasses.
- Composition - Another strategy is to use construct the XmlProperties with a strategy that reads from the source. This is essentially the same mechanism except that you need to build and maintain the the dependencies. It also makes layering more difficult. For example:
- trait IterableXmlProperties {
- self : MapProperties with XmlProperties =>
- def xmlIterable = source.keySet map {xml _}
- }
- new XmlProperties with MapProperties with IterableXmlProperties {def source = sampleMap}
The next question that comes to mind is why use extends then if self annotation is so flexible? My answer (and I welcome discussion on this point) has three points.
- The first is of semantics and modeling. When designing a model it is often more logical to use inheritance because of the semantics that comes with inheriting from another object.
- Another argument is pragmatism.
Imagine the collections library where there is no inheritance. If you wanted a map with Iterable functionality you would have to always declare Traversable with Iterable with Map (and this is greatly simplified). That declaration would have to be used for virtually all methods that require both the Iterable and Map functionality. To say that is impractical is an understatement. Also the semantics of Map is changed from what it is now. The trait Map currently includes the concept of Iterable. - The last point is sealed traits/classes. When a trait is "sealed" all of its subclasses are declared within the same file and that makes the set of subclasses finite which allows certain compiler checks. This (as far as I know) cannot be done with self annotations.
Labels:
Advanced,
inheritance,
Scala,
self-annotation,
self-type
Thursday, February 11, 2010
Selection with Implicit Methods
This tip is a cool tip based on Daniel's Use Implicits to Select Types post. That post goes into more detail.
The idea is that depending on a given parameter of type T a particular type of object is required. There are several ways to do this. One would be to use matching to match the type T and create the correct object. Most likely the biggest draw back to matching is caused by type erasure. The following solution gets around that issue.
Two very interesting points.
The idea is that depending on a given parameter of type T a particular type of object is required. There are several ways to do this. One would be to use matching to match the type T and create the correct object. Most likely the biggest draw back to matching is caused by type erasure. The following solution gets around that issue.
Two very interesting points.
- Implicit methods do not require parameters. They can be selected based only on type parameters
- Implicit methods are not subject to type erasure
- // the base class we need
- scala> abstract class X[T] { def id :Unit }
- defined class X
- /*
- One of the types we need created. It is the catch all case
- */
- scala> implicit def a[T] =new X[T] { def id =println("generic") }
- a: [T]X[T]
- /*
- A specific subclass for integers
- */
- scala> implicit def b =new X[Int] { def id =println("Int") }
- b: X[Int]
- /*
- One simple usage. The most specific implicit will be used to
- create the object to be passed to g.
- */
- scala> def f[T](a :T)(implicit g :X[T]) = g.id
- f: [T](a: T)(implicit g: X[T])Unit
- scala> f(5)
- Int
- scala> f('c')
- generic
- /*
- This example demonstrates how erasure is not an issue because
- the selection of the implicit is done at compile time so
- the correct type is selected. If a match was used instead
- then a more complicated solution would be required
- */
- scala> def g[T](l:List[T])(implicit i:X[T]) = i.id
- g: [T](l: List[T])(implicit i: X[T])Unit
- scala> g(List(1,2,3))
- Int
- scala> g(List("a",2,3))
- generic
Friday, January 29, 2010
Overcoming Type Erasure in Matching 2 (Variance)
A commenter (Brian Howard) on the post Overcoming Type Erasure in Matching 1 made a very good point:
There is a way to do this, however the information is not captured in the Manifest. A manifest is not designed to do full reflection it is by design very light and has little impact on performance. So to provide the functionality requested by Brian one needs to add that information to the Extractor Definition. Have have a possible solution below.
Is there a way to deal with some type arguments being contravariant? Try the following:
class A
class B extends A
val AAFunction = new Def[Function1[A,A]]
((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK
((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None} // this is OK
((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None} // gives a ClassCastException, since new A is not a B
There is a way to do this, however the information is not captured in the Manifest. A manifest is not designed to do full reflection it is by design very light and has little impact on performance. So to provide the functionality requested by Brian one needs to add that information to the Extractor Definition. Have have a possible solution below.
- scala> class A
- defined class A
- scala> class B extends A
- defined class B
- scala> object Variance extends Enumeration {
- | val Co, Contra, No = Value
- | }
- defined module Variance
- scala> import Variance._
- import Variance._
- scala> class Def[C](variance:Variance.Value*)(implicit desired : Manifest[C]) {
- | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
- | val typeArgsTriplet = desired.typeArguments.zip(m.typeArguments).
- | zipWithIndex
- | def sameArgs = typeArgsTriplet forall {
- | case ((desired,actual),index) if(getVariance(index) == Contra) =>
- | desired <:< actual
- | case ((desired,actual),index) if(getVariance(index) == No) =>
- | desired == actual
- | case ((desired,actual),index) =>
- | desired >:> actual
- | }
- |
- | val isAssignable = desired.erasure.isAssignableFrom(m.erasure) || (desired >:> m)
- | if (isAssignable && sameArgs) Some(c.asInstanceOf[C])
- | else None
- | }
- | def getVariance(i:Int) = if(variance.length > i) variance(i) else No
- | }
- defined class Def
- scala> val AAFunction = new Def[Function1[A,A]]
- AAFunction: Def[(A) => A] = Def@64a65760
- scala> ((a:A) => a) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res0: Option[A] = Some(A@1bd4f279)
- scala> ((a:A) => new B) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res1: Option[A] = None
- scala> ((b:B) => b) match {case AAFunction(f) => Some(f(new A)); case _ => None}
- res2: Option[A] = None
- scala> val BAFunction = new Def[Function1[B,A]](Contra,Co)
- BAFunction: Def[(B) => A] = Def@26114629
- scala> ((b:A) => b) match {case BAFunction(f) => Some(f(new B)); case _ => None}
- res3: Option[A] = Some(B@15dcc3ca)
Labels:
Advanced,
contravariance,
covariance,
Enumeration,
manifest,
match,
matching,
Scala,
variance
Thursday, January 28, 2010
Overcoming Type Erasure in matching 1
Since Scala runs on the JVM (it also runs on .NET but we this tip is aimed at Scala on the JVM) much of the type information that is available at compile-time is lost during runtime. This means certain types of matching are not possible. For example it would be nice to be able to do the following:
If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.
This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:
The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.
The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.
The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.
The secret is to use manifests:
It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:
List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
- scala> val x : List[Any] = List(1.0,2.0,3.0)
- x: List[Any] = List(1, 2, 3)
- scala> x match { case l : List[Boolean] => l(0) }
If you run this code the list matches the list of ints which is incorrect (I have ran the following with -unchecked so it will print the warning about erasure):
- scala> val x : List[Any] = List(1.0,2.0,3.0)
- x: List[Any] = List(1, 2, 3)
- scala> x match { case l : List[Boolean] => l(0) }
- < console>:6: warning: non variable type-argument Boolean in type pattern List[Boolean] is unchecked since it is eliminated by erasure
- x match { case l : List[Boolean] => l(0) }
- ^
- java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Boolean
- at scala.runtime.BoxesRunTime.unboxToBoolean(Unknown Source)
- at .< init>(< console>:6)
Another example is trying to match on structural types. Wouldn't it be nice to be able to do the following (We will solve this in a future post):
- scala> "a string" match {
- | case l : {def length : Int} => l.length
- | }
- < console>:6: warning: refinement AnyRef{def length: Int} in type pattern AnyRef{def length: Int} is unchecked since it is eliminated by erasure
- case l : {def length : Int} => l.length
- ^
- res5: Int = 8
So lets see what we can do about this. My proposed solution is to create an class that can be used as an extractor when instantiated to do the check.
This is a fairly advanced tip so make sure you have read up on Matchers and Manifests:
The key parts of the next examples are the Def class and the function 'func'. 'func' is defined in the comments in the code block.
The Def class is the definition of what we want to match. Once we have the definition we can use it as an Extractor to match List[Int] instances.
The Def solution is quite generic so it will satisfy may cases where you might want to do matching but erasure is getting in the way.
The secret is to use manifests:
- When the class is created the manifest for the class we want is captured.
- Each time a match is attempted the manifest of the class being matched is captured
- In the unapply method, the two manifests are compared and when they match we can return the matched element but cast to the desired type for the compiler
It is critical to notice the use of the typeArguments of the manifest. This returns a list of the manifests of each typeArgument. You cannot simply compare desired == m because manifest comparisons are not deep. There is a weakness in this code in that it only handles generics that are 1 level deep. For example:
List[Int] can be matched with the following code but List[List[Int]] will match anything that is List[List[_]]. Making the method more generic is an exercise left to the reader.
- scala> import reflect._
- import reflect._
- /*
- This is the key class
- */
- scala> class Def[C](implicit desired : Manifest[C]) {
- | def unapply[X](c : X)(implicit m : Manifest[X]) : Option[C] = {
- | def sameArgs = desired.typeArguments.zip(m.typeArguments).forall {case (desired,actual) => desired >:> actual}
- | if (desired >:> m && sameArgs) Some(c.asInstanceOf[C])
- | else None
- | }
- | }
- defined class Def
- // first define what we want to match
- scala> val IntList = new Def[List[Int]]
- IntList: Def[List[Int]] = Def@6997f7f4
- /*
- Now use the object as an extractor. the variable l will be a typesafe List[Int]
- */
- scala> List(1,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res36: Int = 2
- scala> List(1.0,2,3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res37: Int = -1
- // 32 is not a list so it will fail to match
- scala> 32 match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res2: Int = -1
- scala> Map(1 -> 3) match { case IntList(l) => l(1) : Int ; case _ => -1 }
- res3: Int = -1
- /*
- The Def class can be used with any Generic type it is not restricted to collections
- */
- scala> val IntIntFunction = new Def[Function1[Int,Int]]
- IntIntFunction: Def[(Int) => Int] = Def@5675e2b4
- // this will match because it is a function
- scala> ((i:Int) => 10) match { case IntIntFunction(f) => f(3); case _ => -1}
- res38: Int = 10
- // no match because 32 is not a function
- scala> 32 match { case IntIntFunction(f) => f(3); case _ => -1}
- res39: Int = -1
- // Cool Map is a function so it will match
- scala> Map(3 -> 100) match { case IntIntFunction(f) => f(3); case _ => -1}
- res6: Int = 100
- /*
- Now we see both the power and the limitations of this solution.
- One might expect that:
- def func(a:Any) = {...}
- would work.
- However, if the function is defined with 'Any' the compiler does not pass in the required information so the manifest that will be created will be a Any manifest object. Because of this the more convoluted method declaration must be used so the type information is passed in.
- I will discuss implicit parameters in some other post
- */
- scala> def func[A](a:A)(implicit m:Manifest[A]) = {
- | a match {
- | case IntList(l) => l.head
- | case IntIntFunction(f) => f(32)
- | case i:Int => i
- | case _ => -1
- | }
- | }
- func: [A](a: A)(implicit m: Manifest[A])Int
- scala> func(List(1,2,3))
- res16: Int = 1
- scala> func('i')
- res17: Int = -1
- scala> func(4)
- res18: Int = 4
- scala> func((i:Int) => i+2)
- res19: Int = 34
- scala> func(Map(32 -> 2))
- res20: Int = 2
Wednesday, January 13, 2010
Comparing Manifests
Manifests are Scala's solution to Java's type erasure. It is not a complete solution as of yet but it does have several useful applications. Manifests were originally added to Scala 2.7 in an extremely limited form but have been improved a great deal for Scala 2.8. Now more powerful comparisons of manifests are possible. For another introduction to Manifests (2.7 manifests) see Manifests.
This post looks at a few ways to create manifests as well as how to compare manifests. The goal is to create a method that can be used in testing to require that a certain exception is thrown during a code block:
The code snippet above is a failure because println does not throw an exception. In addition to requiring manifests this code snippet also is a custom control structure, for more information on those see Control Structure Tag
But before writing the intercepts methods a short inspection of the new manifest comparison operators:
Now the implementation of intercepts:
This post looks at a few ways to create manifests as well as how to compare manifests. The goal is to create a method that can be used in testing to require that a certain exception is thrown during a code block:
- scala> intercepts[Exception] {println("hi :)")}
- hi :)
- Expected java.lang.Exception but instead no exception was raised
The code snippet above is a failure because println does not throw an exception. In addition to requiring manifests this code snippet also is a custom control structure, for more information on those see Control Structure Tag
But before writing the intercepts methods a short inspection of the new manifest comparison operators:
- scala> import scala.reflect.{
- | Manifest, ClassManifest
- | }
- import scala.reflect.{Manifest, ClassManifest}
- // from class creates a manifest object given a class object
- scala> import ClassManifest.fromClass
- import ClassManifest.fromClass
- // several comparisons using <:<
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Exception])
- res4: Boolean = true
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[RuntimeException])
- res5: Boolean = false
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[AssertionError])
- res6: Boolean = false
- // now the opposite operator >:>
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[AssertionError])
- res7: Boolean = false
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[RuntimeException])
- res8: Boolean = true
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Error])
- res9: Boolean = false
- scala> fromClass(classOf[Exception]) >:> fromClass(classOf[Throwable])
- res10: Boolean = false
- // the method singleType creates a manifest given an object
- scala> ClassManifest.singleType(new RuntimeException())
- res12: scala.reflect.Manifest[Nothing] = java.lang.RuntimeException.type
- scala> ClassManifest.singleType(new RuntimeException()) <:< fromClass(classOf[Throwable])
- res13: Boolean = true
- scala> fromClass(classOf[Exception]) <:< fromClass(classOf[Throwable])
- res14: Boolean = true
Now the implementation of intercepts:
- scala> import scala.reflect.{
- | Manifest, ClassManifest
- | }
- import scala.reflect.{Manifest, ClassManifest}
- scala>
- scala> import ClassManifest.singleType
- import ClassManifest.singleType
- scala> def intercepts[E <: Throwable](test : => Unit)(implicit m:Manifest[E]) : Unit = {
- | import Predef.{println => fail}
- | try {
- | test
- | // this is a failure because test is expected to throw an exception
- | fail("Expected "+m.toString+" but instead no exception was raised")
- | }catch{
- | // this checks that the expected type (m) is a superclass of the class of e
- | case e if (m >:> singleType(e)) => ()
- | // any other error is handled here
- | case e => fail("Expected "+m.toString+" but instead got "+e.getClass)
- | }
- | }
- intercepts: [E <: Throwable](test: => Unit)(implicit m: scala.reflect.Manifest[E])Unit
- scala> intercepts[Exception] {println("hi :)")}
- hi :)
- Expected java.lang.Exception but instead no exception was raised
- scala> intercepts[Exception] { throw new IllegalArgumentException("why not throw this :)")}
- scala> intercepts[Exception] { throw new AssertionError("The method should complain")}
- Expected java.lang.Exception but instead got class java.lang.AssertionError
Labels:
Advanced,
control structure,
manifest,
Scala
Sunday, November 22, 2009
Type aliasing and implicit conversions in harmony
This topic combines advanced two topics. types and implicit methods. This topic is not really intended to be instructional as much as a illustration of the cool stuff you can do with the language. As always becareful not to cut yourself :-)
In keeping with the uniform principle of Scala, types can defined as variables. This is useful for several reasons but this topic covers a cool/fun effect of having types be declared like variables. Type Aliasing. In the example below an alias | is created for the Either class. This allows the | to be used in place of Either.
In keeping with the uniform principle of Scala, types can defined as variables. This is useful for several reasons but this topic covers a cool/fun effect of having types be declared like variables. Type Aliasing. In the example below an alias | is created for the Either class. This allows the | to be used in place of Either.
- // Start of example 1. creating the type alias
- // define the | type
- scala> type |[A,B] = Either[A,B]
- defined type alias $bar
- // If a Type has 2 parameters it can be used in operator form
- scala> Array[Int | Long](Left(5),Right(12L))
- res0: Array[|[Int,Long]] = Array(Left(5), Right(12))
- // start of example 2. Simplify creation of an Array (or other collection) of either
- scala> implicit def makeLeft[A,B](a:A):Either[A,B] = Left(a)
- makeLeft: [A,B](a: A)Either[A,B]
- scala> implicit def makeRight[A,B](b:B):Either[A,B] = Right(b)
- makeRight: [A,B](b: B)Either[A,B]
- // Since there are implicits to convert to Right and Left the values in the Array will be wrapped
- // automatically. Makes the syntax MUCH cleaner and easier to read.
- scala> Array[Int|String](5,"Wurst",123,2,6)
- res1: Array[|[Int,String]] = Array(Left(5), Right(Wurst), Left(123), Left(2), Left(6))
Wednesday, October 28, 2009
Extractors 3 (Operator Style Matching)
One of the blessings and curses of Scala the several rules for creating expressive code (curse because it can be used for evil.) One such rule is related to extractors that allows the following style of match pattern:
The rule is very simple. An extractor object that returns Option[Tuple2[_,_]] (or equivalently Option[(_,_)]) can be expressed in this form.
In other words:
Example to extract out the vowels from a string:
Example for Matching the last element in a list. Thanks to 3-things-you-didnt-know-scala-pattern.html:
- List(1,2,3) match {
- case 1 :: _ => println("found a list that starts with a 1")
- case _ => println("boo")
- }
The rule is very simple. An extractor object that returns Option[Tuple2[_,_]] (or equivalently Option[(_,_)]) can be expressed in this form.
In other words:
object X {def unapply(in:String):Option[(String,String)] = ...}
can be used in a case statement like: case first X head => ...
or case "a" X head => ...
.Example to extract out the vowels from a string:
- scala> object X { def unapply(in:String):Option[(RichString,RichString)] = Some(in.partition( "aeiou" contains _ )) }
- defined module X
- scala> "hello world" match { case head X tail => println(head, tail) }
- (eoo,hll wrld)
- // This is equivalent but a different way of expressing it
- scala> "hello world" match { case X(head, tail) => println(head, tail) }
- (eoo,hll wrld)
Example for Matching the last element in a list. Thanks to 3-things-you-didnt-know-scala-pattern.html:
- scala> object ::> {def unapply[A] (l: List[A]) = Some( (l.init, l.last) )}
- defined module $colon$colon$greater
- scala> List(1, 2, 3) match {
- | case _ ::> last => println(last)
- | }
- 3
- scala> (1 to 9).toList match {
- | case List(1, 2, 3, 4, 5, 6, 7, 8) ::> 9 => "woah!"
- | }
- res12: java.lang.String = woah!
- scala> (1 to 9).toList match {
- | case List(1, 2, 3, 4, 5, 6, 7) ::> 8 ::> 9 => "w00t!"
- | }
- res13: java.lang.String = w00t!
Subscribe to:
Comments (Atom)