Введение в
Что такое Scala?
- SCAlable LAnguage
- появился в 2003
- компирируется в byte code JVM
- оказали влияние: Java, Pizza, Haskell, Erlang, Standard ML, Objective Caml, Smalltalk, Scheme, Algol68, Lisp
- мультипарадигменный
- строгая статическая типизация*
- type inference
- true OOP
- sbt
- REPL
*type erasure
Привет, Мир!
object HelloWorld { def main(args: Array[String]) = { println("Привет, МИР!") } }
$ scalac hello.scala
$ scala HelloWorld
Привет, МИР!
Типы
Byte | 8 bit signed value. Range from -128 to 127 |
Short | 16 bit signed value. Range -32768 to 32767 |
Int | 32 bit signed value. Range -2147483648 to 2147483647 |
Long | 64 bit signed value. -9223372036854775808 to 9223372036854775807 |
Float | 32 bit IEEE 754 single-precision float |
Double | 64 bit IEEE 754 double-precision float |
Char | 16 bit unsigned Unicode character. Range from U+0000 to U+FFFF |
String | A sequence of Chars |
Boolean | Either the literal true or the literal false |
Unit | Corresponds to no value |
Null | null or empty reference |
Nothing | The subtype of every other type; includes no values |
Any | The supertype of any type; any object is of type Any |
AnyRef | The supertype of any reference type |
REPL
$ scala
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_51).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object HelloWorld {
| def main(args: Array[String]) = {
| println("Привет, МИР!")
| }
| }
defined module HelloWorld
scala> HelloWorld.main(Array())
Привет, МИР!
scala> val a:Int = 1
a: Int = 1
scala> val a = 2
a: Int = 2
scala> val a = 2.0
a: Double = 2.0
scala> val a = 2/3
a: Int = 0
scala> val a = 2.0/3
a: Double = 0.6666666666666666
val vs var
scala> var a = 1
a: Int = 1
scala> a = 2
a: Int = 2
scala> val b = 1
b: Int = 1
scala> b = 2
:8: error: reassignment to val
b = 2
^
всё - объекты
scala> val b = 1
b: Int = 1
scala> b.toString
res0: String = 1
scala> val s = "1"
s: String = 1
scala> a.toInt
res1: Int = 2
StringOpsscala> val str = "hello"
scala> str.
+ asInstanceOf charAt codePointAt codePointBefore codePointCount compareTo compareToIgnoreCase
concat contains contentEquals endsWith equalsIgnoreCase getBytes getChars indexOf
intern isEmpty isInstanceOf lastIndexOf length matches offsetByCodePoints regionMatches
replace replaceAll replaceFirst split startsWith subSequence substring toCharArray
Функции
scala> def sqr(x: Int): Int = {
x * x
}
sqr: (x: Int)Int
scala> sqr(3)
res0: Int = 9
scala> def sqr(x: Int) = {
x * x
}sqr: (x: Int)Int
scala> def sqr(x: Int) = x*x
sqr: (x: Int)Int
scala> def foo(x: Int) {
println("x = " + x)
}
foo: (x: Int)Unit
Функции высшего порядка
// lambda
scala> val increment = (x: Int) => x + 1
increment: Int => Int = <function1>
scala> increment(9)
res0: Int = 10
// closure
scala> val base = 10
base: Int = 10
scala> val closure = (x: Int) => x + base
closure: Int => Int = <function1>
scala> closure(4)
res1: Int = 14
Немного математики
def add(a: Double, b: Double) = a + b
add: (a: Double, b: Double)Double
def multiply(a: Double, b: Double) = a * b
multiply: (a: Double, b: Double)Double
def pifagor(a: Double, b: Double) = Math.sqrt(a*a + b*b)
pifagor: (a: Double, b: Double)Double
def math(a: Double, b: Double, op: (Double, Double) => Double):Double = op(a, b)
math: (a: Double, b: Double, op: (Double, Double) => Double)Double
scala> math(1,2,add)
res0: Double = 3.0
scala> math(1,2,multiply)
res1: Double = 2.0
scala> math(1,2,pifagor)
res2: Double = 2.23606797749979
Curring
def math(a: Double, b: Double)(op: (Double, Double) => Double): Double = op(a,b)
math: (a: Double, b: Double)(op: (Double, Double) => Double)Double
scala> math(1,2)(add)
res0: Double = 3.0
scala> def math(op: (Double, Double) => Double)(a: Double, b: Double) = op(a,b)
math: (op: (Double, Double) => Double)(a: Double, b: Double)Double
scala> val m = math(multiply)_
m: (Double, Double) => Double = <function2>
scala> m(3,3)
res1: Double = 9.0
scala> val hip = math(pifagor)_
hip: (Double, Double) => Double = <function2>
scala> hip(3,4)
res2: Double = 5.0
//
scala> val hip = math{(a: Double, b: Double) => Math.sqrt(a*a + b*b)}_
hip: (Double, Double) => Double = <function2>
scala> hip(3,4)
res3: Double = 5.0
scala> var a = 3
a: Int = 3
scala> while(a > 0) { println(a); a -= 1 }
3
2
1
scala> for (i <- 1 to 3) { println(i) }
1
2
3
scala> val a = for (i <- 1 to 3) yield i
a: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
Что под капотом for?
scala> 1 to 3
res12: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)
scala> 1 to 3 map (i => i)
res16: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
scala> (1 to 3).map(i => i)
res21: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
Условия
scala> val str = "hello"
scala> def notEmpty = if(str.isEmpty) false else true
isEmpty: Boolean = false
scala> def notEmpty = str.isEmpty
isEmpty: Boolean = false
scala> val v = if(str.isEmpty) 1 else "2"
v: Any = 1
scala> val v: Int = if(str.isEmpty) 1 else "2"
<console>:7: error: type mismatch;
found : String("2")
required: Int
val v: Int = if(str.isEmpty) 1 else "2"
Классы
class Foo(val bar: String, baz: String)
defined class Foo
val foo = new Foo("one", "two")
foo: Foo = Foo@4356367f
scala> foo.
asInstanceOf bar isInstanceOf toString
scala> class Foo(val bar: String, baz: String) {
| override def toString = s"Foo(" + bar + ", "+ baz+")"
| }
defined class Foo
scala> val foo = new Foo("one", "two")
foo: Foo = Foo(one, two)
Объекты
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Foo {
def apply(bar: String, baz: String) = new Foo(bar, baz)
}
class Foo(val bar: String, baz: String) {
override def toString = s"Foo(" + bar + ", "+ baz+")"
}
<ctrl>+<d>
// Exiting paste mode, now interpreting.
defined module Foo
defined class Foo
scala> Foo("one", "two")
res0: Foo = Foo(one, two)
Еще объекты
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Foo {
private var baz: Int = 0
def apply(bar: String) = new Foo(bar)
private def getNextBaz:Int = { baz += 1; baz }
}
class Foo(val bar: String) {
private val baz: Int = Foo.getNextBaz
override def toString = s"Foo(" + bar + ", "+ baz+")"
}
// Exiting paste mode, now interpreting.
defined module Foo
defined class Foo
scala> Foo("one")
res0: Foo = Foo(one, 1)
scala> Foo("one")
res1: Foo = Foo(one, 2)
scala> case class Foo(bar: String)
defined class Foo
scala> Foo("one")
res0: Foo = Foo(one)
scala> res0.
asInstanceOf bar canEqual copy isInstanceOf productArity productElement productIterator productPrefix
toString
Наследование
abstract class Animal
trait Legs {
def walk = ???
}
trait Flippers{
def swim = ???
}
class Cat extends Animal with Legs
class Dog extends Animal with Legs
class Whale extends Animal with Flippers
trait Undead
class Zombie extends Undead with Legs
class Monster extends Flippers with Undead
Type check
scala> val lassie = new Dog
lassie: Dog = Dog@4a55ac8e
scala> val frank = new Monster
frank: Monster = Monster@5c47f49a
scala> def breaths(creature: Animal) = true
breaths: (creature: Animal)Boolean
scala> breaths(lassie)
res0: Boolean = true
scala> breaths(frank)
<console>:14: error: type mismatch;
found : Monster
required: Animal
breaths(frank)
scala> val mobyDick = new Whale
mobyDick: Whale = Whale@25963401
scala> val lassie = new Dog
lassie: Dog = Dog@4acaa278
scala> def canSwim(creature: Flippers) = true
canSwim: (creature: Flippers)Boolean
scala> canSwim(lassie)
<console>:14: error: type mismatch;
found : Dog
required: Flippers
canSwim(lassie)
^
scala> canSwim(mobyDick)
res0: Boolean = true
val vs lazy val vs def
class Foo {
val a = { println("a"); 1 }
lazy val b = { println("b"); 2 }
def c = { println("c"); 3 }
}
scala> val foo = new Foo
a
foo: Foo = Foo@22cc1ba7
scala> foo.b
b
res0: Int = 2
scala> foo.b
res1: Int = 2
scala> foo.c
c
res2: Int = 3
scala> foo.c
c
res3: Int = 3
Idexed Sequenses
val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> l(1)
res0: Int = 2
scala> val v = Vector(1,2,3)
v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
scala> v(2)
res1: Int = 3
scala> v.apply(2)
res2: Int = 3
val a = Array("one","two")
a: Array[String] = Array(one, two)
scala> a(0)
res3: String = one
Maps
scala> val capitals = Map("Russia" -> "Moscow", "France" -> "Paris")
capitals: scala.collection.immutable.Map[String,String] = Map(Russia -> Moscow, France -> Paris)
scala> capitals("Russia")
res0: String = Moscow
scala> capitals("russia")
java.util.NoSuchElementException: key not found: russia
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:58)
at scala.collection.MapLike$class.apply(MapLike.scala:141)
at scala.collection.AbstractMap.apply(Map.scala:58)
at .<init>(<console>:9)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:756)
at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)
at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713)
at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:577)
at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:878)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
scala> capitals.get("russia")
res1: Option[String] = None
scala> capitals.get("Russia")
res2: Option[String] = Some(Moscow)
scala> capitals.getOrElse("russia", "not found")
res14: String = not found
scala> capitals.getOrElse("Russia", "not found")
res15: String = Moscow
val capitals = Map("Russia" -> "Moscow", "France" -> "Paris")
capitals: scala.collection.immutable.Map[String,String] = Map(Russia -> Moscow, France -> Paris)
def showCapital(country: String):String = {
capitals.get(country) match {
case Some(city: String) => s"The capital of $country is $city"
case None => s"I don't know such country: $country"
}
}
showCapital: (country: String)String
scala> showCapital("Russia")
res0: String = The capital of Russia is Moscow
scala> showCapital("Great Britain")
res1: String = I don't know such country: Great Britain
val a = ("one", 2, Foo)
a: (String, Int, Foo.type) = (one,2,Foo$@8c96c01)
scala> val (b,c,d) = a
b: String = one
c: Int = 2
d: Foo.type = Foo$@8c96c01
scala> val (b,c,_) = a
b: String = one
c: Int = 2
Создаем тест
// build.sbt
name := "unit test"
libraryDependencies ++= Seq(
"org.specs2" %% "specs2" % "2.3.10" % "test"
)
// src/main/scala/MyMath.scala
package util
class MyMath {
def add(a: Double, b: Double) = a + b
}
// src/test/scala/MyMathSpec.scala
import org.specs2.mutable._
import utils.MyMath
class MyMathSpec extends Specification {
"method add" should {
"return addition result" in {
val math = new MyMath()
math.add(1, 2) should equalTo(3)
}
}
}
Запускаем тест
$ sbt
[info] Set current project to unit test (in build file:/Users/heoh/scala/unittest/)
> test
[info] MyMathSpec
[info]
[info] method add should
[info] + return addition result
[info]
[info] Total for specification MyMathSpec
[info] Finished in 31 ms
[info] 1 example, 0 failure, 0 error
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[success] Total time: 3 s, completed 06.04.2014 13:24:44
>
// build.sbt
name := "unit test"
libraryDependencies ++= Seq(
"org.specs2" %% "specs2" % "2.3.10" % "test",
"org.scalatest" %% "scalatest" % "1.9.1" % "test",
"org.scalacheck" %% "scalacheck" % "1.11.3" % "test"
)
// src/test/scala/CheckMyMath.scala
import org.scalacheck._
import utils.MyMath
object CheckMyMath extends Properties("Int") {
import Prop.forAll
val a = Gen.choose(0,1000)
val b = Gen.choose(0,1000)
val math = new MyMath
property("addition") = forAll(a,b) { (a: Int, b: Int) =>
math.add(a, b) == a+b
}
}
Rerun tests
$ sbt test
[info] Set current project to unit test (in build file:/Users/heoh/scala/unittest/)
[info] + Int.addition: OK, passed 100 tests.
[info] MyMathSpec
[info]
[info] method add should
[info] + return addition result
[info]
[info] Total for specification MyMathSpec
[info] Finished in 41 ms
[info] 1 example, 0 failure, 0 error
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2
[success] Total time: 3 s, completed 06.04.2014 13:54:51
// src/test/scala/MyMathATSpec.scala
import org.specs2._
import utils.MyMath
class MyMathATSpec extends Specification {
def is = s2"""
Эта спецификация должна проверять корректность класса MyMath
Метод add должен складывать 2 числа типа double
при сложении 2+2 мы должны получать 4 $e2
"""
val math = new MyMath
def e2 = math.add(2, 2) must equalTo(4)
}
$ sbt "testOnly MyMathATSpec"
[info] Set current project to unit test (in build file:/Users/heoh/scala/unittest/)
[info] Compiling 1 Scala source to /Users/heoh/scala/unittest/target/scala-2.10/test-classes...
[info] MyMathATSpec
[info]
[info] Эта спецификация должна проверять корректность класса MyMath
[info]
[info] Метод add должен складывать 2 числа типа double
[info] + при сложении 2+2 мы должны получать 4
[info]
[info] Total for specification MyMathATSpec
[info] Finished in 29 ms
[info] 1 example, 0 failure, 0 error
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[success] Total time: 11 s, completed 06.04.2014 14:13:45
Работа с XML
scala> val xhtmlResponse =
<html>
<header>
<title>Some Title</title>
</header>
<body>
<h1>header</h1>
<p>Lorem ipsum</p>
</body>
</html>
xhtmlResponse: scala.xml.Elem =
<html>
<header>
<title>Some Title</title>
</header>
<body>
<h1>header</h1>
<p>Lorem ipsum</p>
</body>
</html>
(xhtmlResponse \\ "header").text
res0: String =
"
Some Title
"
scala> xhtmlResponse \ "body"
res1: scala.xml.NodeSeq =
NodeSeq(<body>
<h1>header</h1>
<p>Lorem ipsum</p>
</body>)
Frameworks
http://www.jetbrains.com/idea/
http://typesafe.com/activator
http://scala-ide.org/
https://github.com/sublimescala/sublime-ensime
http://www.emacswiki.org/emacs/ScalaMode
Ссылки
Введение в scala
By Sergey Lobin
Введение в scala
- 385