Scala is Functional

Download Report

Transcript Scala is Functional

Getting Functional
Functional Programming (FP) in Scala

In FP,

Functions are first-class objects. That is, they are values, just like other objects are
values, and can be treated as such



Functions should only transform their inputs into their outputs



Functions can be assigned to variables, passed as parameters to higher-order functions,
returned as results of functions
There is some way to write function literals
A function should have no side effects
 It should not do any input/output
 It should not change any state (any external data)
Given the same inputs, a function should produce the same outputs, every time--it
is deterministic
If a function is side-effect free and deterministic, it has referential
transparency—all calls to the function could be replaced in the program text by
the result of the function

But we need random numbers, date and time, etc.
2
Creating Lists










scala> List('a', 'b', 'c')
res0: List[Char] = List(a, b, c)
scala> "abc" toList
res1: List[Char] = List(a, b, c)
scala> "Welcome to Scala" split(" ")
res2: Array[java.lang.String] = Array(Welcome, to, Scala)
scala> val scala = "Scala" toList
scala: List[Char] = List(S, c, a, l, a)
scala> "Hello" :: scala
res3: List[Any] = List(Hello, S, c, a, l, a)
3
Nil and ::












scala> List()
res4: List[Nothing] = List()
scala> Nil
res5: scala.collection.immutable.Nil.type = List()
scala> List() == Nil
res6: Boolean = true
scala> List[String]()
res7: List[String] = List()
scala> "xyz" :: Nil
res8: List[java.lang.String] = List(xyz)
scala> "abc" :: "xyz" :: Nil
res9: List[java.lang.String] = List(abc, xyz)
4
head, tail, and isEmpty












scala> val penn = "Pennsylvania" toList
penn: List[Char] = List(P, e, n, n, s, y, l, v, a, n, i, a)
scala> penn head
res10: Char = P
scala> penn tail
res11: List[Char] = List(e, n, n, s, y, l, v, a, n, i, a)
scala> penn isEmpty
res12: Boolean = false
scala> Nil isEmpty
res13: Boolean = true
scala> Nil head
java.util.NoSuchElementException: head of empty list (plus many more lines!)
5
take, drop, and splitAt










scala> penn
res16: List[Char] = List(P, e, n, n, s, y, l, v, a, n, i, a)
scala> penn take 4
res17: List[Char] = List(P, e, n, n)
scala> penn drop 4
res18: List[Char] = List(s, y, l, v, a, n, i, a)
scala> penn splitAt 4
res19: (List[Char], List[Char]) = (List(P, e, n, n),List(s, y, l, v, a, n, i, a))
scala> penn.splitAt(4)
res20: (List[Char], List[Char]) = (List(P, e, n, n),List(s, y, l, v, a, n, i, a))
6
toString and mkString












scala> List(1, 2, 3).toString
res25: String = List(1, 2, 3)
scala> List(1, 2, 3).toString == "List(1, 2, 3)"
res26: Boolean = true
scala> List(1, 2, 3) mkString(" is less than ")
res27: String = 1 is less than 2 is less than 3
scala> List(1, 2, 3) mkString("*")
res28: String = 1*2*3
scala> List(1, 2, 3) mkString("<: ", "--", " :>")
res29: String = <: 1--2--3 :>
scala> List(1, 2, 3) mkString("(", ", ", ")")
res30: String = (1, 2, 3)
7
zip and unzip












scala> val words = "one two three" split " "
words: Array[java.lang.String] = Array(one, two, three)
scala> val numbers = List(1, 2, 3, 4, 5)
numbers: List[Int] = List(1, 2, 3, 4, 5)
scala> val z = words zip numbers
z: Array[(java.lang.String, Int)] = Array((one,1), (two,2), (three,3))
scala> val zz = numbers zip words
zz: List[(Int, java.lang.String)] = List((1,one), (2,two), (3,three))
scala> z toMap
res31: scala.collection.immutable.Map[java.lang.String,Int] = Map((one,1), (two,2),
(three,3))
scala> zz unzip
res32: (List[Int], List[java.lang.String]) = (List(1, 2, 3),List(one, two, three))
8
Higher-order functions








The basic syntax of a function literal is
parameter_list => function_body
A higher-order function is one that takes a function as a parameter, or returns a function as a result
scala> val brag = "Scala is great!" toList

brag: List[Char] = List(S, c, a, l, a, , i, s, , g, r, e, a, t, !)
scala> brag count((ch: Char) => ch == 'a')

res34: Int = 3
scala> brag count((ch: Char) => !(ch isLetter))

res35: Int = 3
scala> "Scala is great!".toList.count((ch: Char) => ch < 'f')

res36: Int = 9
scala> "aeiou" contains 'e'

res37: Boolean = true
scala> "Scala is great!".toList.count((ch: Char) => "aeiou" contains ch)

res38: Int = 5
9
Abbreviations

In a literal function, you can usually omit the type (and, if there’s only one parameter,
the parentheses)




In fact, if there is only one parameter, used once, you can omit the parameter and the
=> and just use _ to stand in for the parameter


scala> brag
res40: List[Char] = List(S, c, a, l, a, , i, s, , g, r, e, a, t, !)
scala> brag count((ch: Char) => ch == 'a')
res41: Int = 3
scala> brag count (ch => ch == 'a')
res42: Int = 3
scala> brag count (_ == 'a')
res44: Int = 3
You can use underscores to stand for multiple parameters, provided each is used once,
and they are used in order

scala> (1 to 10).foldLeft(0)(_ + _)
res27: Int = 55
10
sortWith

scala> brag sortWith((x, y) => x < y)
res49: List[Char] = List( , , !, S, a, a, a, c, e, g, i, l, r, s, t)

Since there are two parameters, we can use two underscores


scala> brag sortWith (_ < _)
res50: List[Char] = List( , , !, S, a, a, a, c, e, g, i, l, r, s, t)

Order matters!



scala> brag sortWith (_ > _)
res52: List[Char] = List(t, s, r, l, i, g, e, c, a, a, a, S, !, , )
11
forall and exists











Whereas count returns an Int, forall and exists return a Boolean
scala> val n = List(3, 1, 4, 1, 6)
n: List[Int] = List(3, 1, 4, 1, 6)
scala> n forall(x => x < 8)
res53: Boolean = true
scala> n forall(x => x < 5)
res54: Boolean = false
scala> n exists(_ < 5)
res55: Boolean = true
scala> n exists(_ > 8)
res56: Boolean = false
12
foreach








foreach returns the (uninteresting) Unit value
scala> val brag = List("Scala", "is", "great!")
brag: List[java.lang.String] = List(Scala, is, great!)
scala> brag foreach (println(_))
Scala
is
great!
scala> brag foreach(println)
Scala
is
great!
scala> List(3, 1, 4, 1, 6) foreach (x => if (x > 1) println(x))
3
4
6
13
Least upper bound

The following list contains only integers:


Now let’s add a non-integer to it


scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> "hello" :: list
res0: List[Any] = List(hello, 1, 2, 3)
The type of this new list is the lowest class in the hierarchy that is
a superclass of all the elements of the list
14
Closures












scala> var c = 5
c: Int = 5
scala> val mult = (x: Int) => c * x
mult: (Int) => Int = <function1>
scala> mult(3)
res63: Int = 15
scala> c = 7
c: Int = 7
scala> mult(10)
res64: Int = 70
What will happen if we pass the mult function into another function in another context?
It continues to “link to” (enclose?) the original variable c, not it’s value
15
map







map produces a new list by applying the given function to each element of the
given list
scala> val n = List(1, 2, 3, 4, 5)
n: List[Int] = List(1, 2, 3, 4, 5)
scala> n map(x => x * x)
res65: List[Int] = List(1, 4, 9, 16, 25)
scala> n map (_ >= 3)
res66: List[Boolean] = List(false, false, true, true, true)
16
flatMap







flatMap produces a new list by applying the given function to each element of the
given list, and “flattening” the result
scala> n
res70: List[Int] = List(1, 2, 3, 4, 5)
scala> n map (x => List(x, x*x))
res72: List[List[Int]] = List(List(1, 1), List(2, 4), List(3, 9), List(4, 16), List(5,
25))
scala> n flatMap(x => List(x, x*x))
res73: List[Int] = List(1, 1, 2, 4, 3, 9, 4, 16, 5, 25)
17
filter







filter produces a new list consisting of the values that pass the given test
scala> n
res67: List[Int] = List(1, 2, 3, 4, 5)
scala> n filter (_ < 4)
res68: List[Int] = List(1, 2, 3)
scala> n filter (_ % 2 == 1)
res69: List[Int] = List(1, 3, 5)
18
Some Map methods


These methods have little to do with “being functional,” so think of them as a bonus :-)
We’re going to use a map that takes small numbers (1..5) to their squares:

scala> val m = Map(1 -> 1, 2 -> 4, 3 -> 9, 4 -> 16, 5 -> 25)
m: scala.collection.immutable.Map[Int,Int] = Map((5,25), (1,1), (2,4),
(3,9), (4,16))





scala> m(4)
res1: Int = 16
scala> m(10)
java.util.NoSuchElementException: key not found: 10
...and many more lines of error message
scala> m.get(4)
res3: Option[Int] = Some(16)
scala> m.get(10)
res4: Option[Int] = None
scala> m.get(4) match {
| case Some(x) => x
| case None => -999
|}
res5: Int = 16
19
Another Map method





scala> m
res6: scala.collection.immutable.Map[Int,Int] = Map((5,25), (1,1),
(2,4), (3,9), (4,16))
scala> m.getOrElse(4, -999)
res7: Int = 16
scala> m.getOrElse(10, -999)
res8: Int = -999
Now consider the following carefully: Given that
 scala> "hello" :: List(1, 2, 3)
res9: List[Any] = List(hello, 1, 2, 3)
What do you think the type and value of the following will be?
 scala> val x = m.getOrElse(4, "Hello")
20
“Houston, we have a problem.”



scala> val x = m.getOrElse(4, "Hello")
x: Any = 16
That’s pretty scary--but it gets worse:
scala> x + 1
<console>:8: error: type mismatch;
found : Int(1)
required: String
x+1
^
21
The End
If I were to pick a language to use today
other than Java, it would be Scala.
--James Gosling, creator of Java
22