Showing posts with label structural types. Show all posts
Showing posts with label structural types. Show all posts
Tuesday, February 9, 2010
Structural Types: Multiple Methods and Type Aliasing
There are two more aspects related to structural typing that are useful to look at. Structural types with multiple methods and type aliases.
For background on this topic also look at:
Structural Types are not limited to defining a single method. In that regard they are very similar to interfaces without the binary incompatibility issues. However do not be fooled into thinking they are the same thing. For one reason reflection is used so performance can be an issue in certain cases and also interfaces/traits have semantics that structural types do not.
Pretty unexpected I would say. A feature of Scala which complements Structural types are type aliases. They are useful in many situations and one is with use with Structural Types:
For background on this topic also look at:
Structural Types are not limited to defining a single method. In that regard they are very similar to interfaces without the binary incompatibility issues. However do not be fooled into thinking they are the same thing. For one reason reflection is used so performance can be an issue in certain cases and also interfaces/traits have semantics that structural types do not.
- /*
- Defining a types that has both a length and charAt method.
- Just a warning. If you leave off the () after length this will not work. This is not a bug. Martin kindly left a comment on why it is not.
- */
- scala> def foreach(t : {def length():Int; def charAt(i:Int):Char}, f : Char => Unit) = {
- | 0 until t.length foreach {i => f(t.charAt(i))}
- | }
- foreach: (t: AnyRef{def length(): Int; def charAt(i: Int): Char},f: (Char) => Unit)Unit
- // A string matches the structural type
- scala> foreach ("hello", println _)
- h
- e
- l
- l
- o
Pretty unexpected I would say. A feature of Scala which complements Structural types are type aliases. They are useful in many situations and one is with use with Structural Types:
- /*
- With type aliasing you can assign a structural type a name
- */
- scala> type IChar = {def length():Int
- | def charAt(i:Int):Char}
- defined type alias IChar
- scala> def print( t : IChar) = 0 until t.length() foreach {i => println(t.charAt(i))}
- print: (t: IChar)Unit
- scala> print("gurk")
- g
- u
- r
- k
Labels:
intermediate,
Scala,
structural types,
type,
type alias
Monday, February 8, 2010
Dynamic calls using Structural Types
Using reflection can be a real pain in Java since the API is a Java API and consists of many gets and searches through collections not to mention so many exceptions that need to be handled. In Scala there is a wonderful way to clean up a reflective call down to a single line (assuming you don't want to worry about handling exceptions.) Here structural typing can really be a pleasure.
There are restrictions to this. For example implicits will not work:
More examples:
Use with care :-D
- // I am assigning a string to an Any reference
- scala> val s:Any = "hello :D"
- s: Any = hello :D
- // Any does not have a length method
- scala> s.length
- < console>:6: error: value length is not a member of Any
- s.length
- ^
- /*
- But I can cast it to a structural type with a length method
- */
- scala> s.asInstanceOf[{def length:Int}].length
- res2: Int = 8
There are restrictions to this. For example implicits will not work:
- /*
- The method r is part of StringLike (or RichString in Scala 2.7)
- and there is an implicit conversion from String to RichString/StringLike.
- The structural type does not try to apply the implicits because implicits are a
- compile time artifact and that information is not kept at run time. Perhaps this
- will be added in the future but it is not present now.
- */
- scala> s.asInstanceOf[{def r:util.matching.Regex}].r
- java.lang.NoSuchMethodException: java.lang.String.r()
More examples:
- scala> val s:Any = "hello :D"
- s: Any = hello :D
- scala> s.asInstanceOf[{def charAt(x:Int):Char}].charAt(2)
- res9: Char = l
- /*
- This is interesting. The actual method returns Char, but Char is
- compatible with Any so this cast works.
- */
- scala> s.asInstanceOf[{def charAt(x:Int):Any}].charAt(2)
- res10: Any = l
- // Now lets test to see if that lenience is available for parameters:
- scala> class X
- defined class X
- scala> class Y extends X
- defined class Y
- scala> class T {
- | def x(x:X):Int = 1
- | }
- defined class T
- scala> val a:Any = new T()
- a: Any = T@687c3b99
- scala> a.asInstanceOf[{def x(x:X):Any}].x(new Y())
- res11: Any = 1
- /*
- Ok so return values can be subclasses but not params
- */
- scala> a.asInstanceOf[{def x(x:Y):Any}].x(new Y())
- java.lang.NoSuchMethodException: T.x(Y)
- at java.lang.Class.getMethod(Class.ja
Use with care :-D
Labels:
casting,
intermediate,
Scala,
structural types
Friday, February 5, 2010
Introducing Structural Types
Structural types allows one to declare types based on the methods the type has. For example you could define a method that takes a class containing a close method. This is fairy analogous to duck-typing in dynamic languages. Except that it is statically enforced.
The main example used here was from a comment on the Code Monkeyism Blog. The commenter further explains that this example is in fact from Beginning Scala chapter 4 (which I would like to read but have not yet had the time.)
That is extremely powerful and the consequences will be visited more in the future. But because structural typing is statically enforced it is not quite as flexible as dynamic language's version of duck typing. For example you cannot do:
An alternative to the first using example is to use call by name to construct the closeable. The reason one might want to do that is because it allows currying of the method:
The main example used here was from a comment on the Code Monkeyism Blog. The commenter further explains that this example is in fact from Beginning Scala chapter 4 (which I would like to read but have not yet had the time.)
- /*
- A can be any object that has a close method.
- This is statically typed which makes some restrictions which are explained later
- */
- scala> def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B =
- | try {
- | f(param)
- | } finally {
- | try {
- | param.close()
- | } catch { case _ => () }
- | }
- using: [A <: AnyRef{def close(): Unit},B](param: => A)(f: (A) => B)B
- scala> using(new java.io.ByteArrayInputStream("hello world".getBytes)){ in =>
- | io.Source.fromInputStream(in) foreach (print)
- | }
- hello world
- scala> using(new java.io.ByteArrayInputStream("hello world".getBytes)){ in =>
- | io.Source.fromInputStream(in) mkString ""
- | }
- res8: String = hello world
That is extremely powerful and the consequences will be visited more in the future. But because structural typing is statically enforced it is not quite as flexible as dynamic language's version of duck typing. For example you cannot do:
- scala> val i:Any = new java.io.ByteArrayInputStream("hello world".getBytes)
- i: Any = java.io.ByteArrayInputStream@145a25f3
- /*
- This does not work because 'i' is defined as type Any. Not a type that is closeable.
- Casting can be used to get around this issue. I will address that in its own post
- */
- scala> using(i){ in =>
- | io.Source.fromInputStream(in) mkString ""
- | }
- < console>:8: error: inferred type arguments [Any,B] do not conform to method using's type parameter bounds [A <: AnyRef{def close(): Unit},B]
- using(i){ in =>
An alternative to the first using example is to use call by name to construct the closeable. The reason one might want to do that is because it allows currying of the method:
- scala> def using[A <: {def close(): Unit}, B](param: =>A)(f: A => B): B =
- | val closeable = param // notice param is only referenced once
- | try {
- | f(closeable)
- | } finally {
- | try {
- | closeable.close()
- | } catch { case _ => () }
- | }
- using: [A <: AnyRef{def close(): Unit},B](param: => A)(f: (A) => B)B
- /*
- if this was accessing a database a new connection would be made automatically each time this function was used
- */
- scala> val usingTheWorld = using[BStream,Int](new java.io.ByteArrayInputStream("hello world".getBytes))_
- usingTheWorld: ((java.io.ByteArrayInputStream) => Int) => Int = < function1>
- scala> usingTheWorld { s => io.Source.fromInputStream(s).length}
- res3: Int = 11
- scala> usingTheWorld { s => io.Source.fromInputStream(s).getLines().length}
- res5: Int = 0
Labels:
control structure,
intermediate,
Scala,
structural types
Tuesday, December 8, 2009
Labelled Multiple Returns
A cool idea I found at http://stackoverflow.com/questions/1827672/is-there-a-way-to-have-tuples-with-named-fields-in-scala-similar-to-anonymous-cl. A very simple idea but very useful.
To summarize, you want to return multiple values but want to assign names to the values rather than use a tuple which more or less assigns indices to the different values. Here are a couple of solutions:
Using structured types. Simplest solution with the least extra code.
Using case classes. Returns a product which has properties that may be useful.
To summarize, you want to return multiple values but want to assign names to the values rather than use a tuple which more or less assigns indices to the different values. Here are a couple of solutions:
Using structured types. Simplest solution with the least extra code.
- scala> def multipleReturns1 = {
- | new { val first = 1; val second = 2 }
- | }
- multipleReturns1: java.lang.Object{def first: Int; def second: Int}
- scala> multipleReturns1
- res0: java.lang.Object{def first: Int; def second: Int} = $anon1ドル@549b6976
- scala> res0.first
- res1: Int = 1
- scala> res0.second
- res2: Int = 2
- scala> import res0._
- import res0._
- scala> first
- res3: Int = 1
- scala> second
- res4: Int = 2
Using case classes. Returns a product which has properties that may be useful.
- scala> def moreReturns = {
- | case class Returns (one:Int, two:Int)
- | Returns(1,2)
- | }
- moreReturns: java.lang.Object with ScalaObject with Product{def one: Int; def two: Int}
- scala> moreReturns
- res5: java.lang.Object with ScalaObject with Product{def one: Int; def two: Int} = Returns(1,2)
- scala> res5.one
- res6: Int = 1
- scala> res5.two
- res7: Int = 2
- scala> import res5._
- import res5._
- scala> one
- res8: Int = 1
- scala> two
- res9: Int = 2
- scala> res5.productIterator foreach println _
- 1
- 2
- scala> res5.productPrefix
- res15: java.lang.String = Returns
Labels:
case-classes,
intermediate,
Scala,
structural types
Subscribe to:
Comments (Atom)