Upload
alexander-podkhalyuzin
View
285
Download
0
Embed Size (px)
Citation preview
Scala #4Scopes, XML, Scala type system
Scopes
Scopes - это место в коде, где может быть объявлена переменная. ● { - начинает новый scope, кроме классов ● Каждый генератор в for statement
начинает новый scope ● Функция начинает новый scope для
параметров ● Case clause начинает новый scope Переменная объявленная внутри одного scope не видна снаружи.
Names shadowing
Во внутреннем scope можно скрывать имя определенное во внешнем scope при условии, что приоритет этого имени не ниже чем у имени объявленного снаружи.
Это ошибка объявить переменные с одним именем внутри одного и того же scope.
Существует два вида имен: expressions, types.
Think functional
Давайте напишем программу вывода таблицы умножения (см. Programming Scala)
Initialization
Инициализация происходит в обратом порядке, нежели линеаризация. Порядок важно понимать, иначе можно заработать NullPointerException:
Initializationobject InitializationNPE {
class A {
val x1 = "text"
val x2 = x1.length
}
class B extends A {
override val x1 = "text1"
}
def main(args: Array[String]) {
new B
}
}
Initialization
Есть два способа решить эту проблему● Early definitions● Lazy initialization
Early definitions
Если нужно определить что-то до вызова super конструктора, это можно сделать в early definitions:
class B(x: Int)
class A(s: String) extends {
val i = s.toInt
} with B(i * i)
Initialization (early defs)object InitializationNPE {
class A {
val x1 = "text"
val x2 = x1.length
}
class B extends {
override val x1 = "text1"
} with A
def main(args: Array[String]) {
new B
}
}
Lazy values
Scala поддерживает синтиаксис ленивых вычислений. От пользователя требуется лишь объявить переменную, как lazy.
Реализация double-checked locking, что приводит к определенным сложностям.
Initialization (lazy vals)object InitializationNPE {
class A {
lazy val x1 = "text"
val x2 = x1.length
}
class B extends A {
override lazy val x1 = "text1"
}
def main(args: Array[String]) {
new B
}
}
Few words about traits
Как же компилируются traits, рассмотрим простой пример:
trait Base {
def foo() = 1
}
class Child extends Base
Few words about traitspublic interface Base {
int foo();
}
public class Base$class {
public static int foo(Base base) {
return 1;
}
}
public class Child implements Base {
public int foo() {
return Base$class.foo(this);
}
}
Embedded XML
Практически любой валидный XML можно использовать как литерал:
val xml1 = <a>Some text<b/></a>val xml2 = <a>{3 + 4}</a>val xml3 = <a>{"</a>It's just String<a>"}</a>
Если надо написать {, то его можно заэскейпить {{.
XML API
Базовый класс scala.xml.Node.Доступ к subelement через \ “name”.К аттрибутам через \ “@name”.Рекурсивный поиск в глубину \\ “name”.
val xml = <a id="text"><b>S<c></c></b></a>
xml.text // S
xml \ "b" // <b>S<c></c></b>
xml \ "@id" // text
xml \\ "c" // <c></c>
Using XML
Встроенный XML легко использовать для сериализации:
val node = <x></x>
scala.xml.XML.save("File.xml", node)
scala.xml.XML.load("File.xml")
Pattern matching
В pattern matching есть особый синтаксис для XML:
val node = <x></x>
scala.xml.XML.save("File.xml", node)
scala.xml.XML.load("File.xml")
Scala type system
Basic types
● Primitive types● Type designators● Parameterized types● Tuple types● Function types● Infix types● Type projections● Singleton types● Annotated types
Advanced types
● Structural types● Existential types● Method types (internal type)● Polymorphic method types (internal type)● Type constructors (internal type)
Primitive types
Есть несколько важных фич:● Numeric Widening. Если работает weak
conformance, то он автоматический.● Literal Narrowing. Literal с ожидаемым
типом Byte, Short or Char конвертируется.● Value discarding. Если тип Uеnit, то для
любого выражения, есть конверсия.Value classes также наследуются от AnyVal.
Type designators
Это просто любой class, trait или object как тип. Ничего особенного здесь не подразумевается.
Parameterized types
Для type designator и его type parameters есть два правила:● Число type parameters должно of underli
совпадать с числом параметорв соответствующего класса type designator.
● Каждый type parameter должен подходить к type parameter bounds
Tuple types
Синтаксический сахар для TupleN[T1, ..., Tn] типа. Subtyping разрешается также, как и subtyping соответствующего TupleN класс согласно вариантности.Поэтому этот код и не скомпилируется:
object A {
def foo(x: (Int, Int)) = x._1 + x._2
def foo(x: (String, String)) = x._1 + x._2
}
Automatic tupling
Позволяет избежать лишних скобок, особенно это полезно для infix нотации.
object A {
def foo(x: (Int, Int)) = x._1 + x._2
}
A.foo((1, 2))
A.foo(1, 2)
A foo (1, 2)
println(1, 2, 3)
Function type
Уже подробно изученный нами синтаксический сахар. В силу недосмотра в компиляторе это несовсем синтаксический сахар:
val x1: Function1[Int, Int] = x => x + 1
val x2: Int => Int = _ + 1
val y1: Function1[Seq[Int], Int] = x => x.length
val y2: (Int*) => Int = x => x.length
Annotated types
Любой тип можно проаннотировать. С точки зрения системы типов эта аннотация будет проигнорирована:
class A
val a: A @serializable = new A
Infix types
Синтаксический сахар (опять?..)
class :::[T, S]
val x: :::[Int, Int] = new :::
val y: Int ::: Int = new :::
Infix types
Может быть полезно для операторов над типами:
type n[A] = A => Nothing
type ++[T, U] = n[n[T] with n[U]]
type nn[A] = n[n[A]]
type |+|[T, U] = { type λ[X] = nn[X] <:< (T ++ U) }
def size[T : (Int |+| String)#λ] (t: T) = t match {
case i: Int => i
case s: String => s.length
}