程式扎記: [Scala IA] The Basics : Ch2. Getting Started - RHEL & Scala Basic

標籤

2016年6月28日 星期二

[Scala IA] The Basics : Ch2. Getting Started - RHEL & Scala Basic

REPL with Scala interpreter
The easiest way to get started with Scala is by using the Scala interpreter, an interactive shell for writing Scala expressions and programs. To start the Scala interpreter in interactive mode, type scala at the command prompt. If everything goes fine with your installation, you’ll see something like the following:


This means your Scala installation is successful. I’m running Scala version 2.10.1, and all the code examples should work for this version and above. At the Scala prompt type 42 and press Enter, and you should see something like this:
scala> 42
res0: Int = 42

scala> res0
res1: Int = 42

The first line is what you typed. The Scala interpreter reads the input 42, evaluates it as an integer literal, creates an Int type object representing the number 42, and prints it back to the console. res0 is the name of the variable created by the Scala interpreter (the name of the variable could be different for you because the variable name is generated by the Scala interpreter at runtime) and it holds the value 42. If you type the variable name, in this case res0, at the prompt, you’ll get similar output. These steps together are called read-evaluate-print loop (REPL). You could loop readevaluate- print steps repeatedly inside the Scala interpreter. Now you’ll write your first “Hello World” program in Scala:
scala> println("Hello World")
Hello World

DEFINITION
println is a function defined in scala.Console, which in turn uses System.out.println to print messages to the console. Scala Predef (part of the standard library) maps println to Console.println for you so you don’t have to prefix it with Consolewhen using it.

In chapter 1, I mentioned that Scala integrates well with Java but didn’t provide an example. I’ll fix that now:
scala> val myList = new java.util.ArrayList[String]()
myList: java.util.ArrayList[String] = []

In this case you’re creating an instance of java.util.ArrayList that will hold String-type objects. If you don’t remember all the methods that you could possibly invoke on myList, don’t worry because the Scala interpreter will help you with that. Type in myList, followed by a period, and press Tab; the Scala interpreter lists all the methods you can invoke.

Not only does it list all the methods associated with a data type, it also autocompletes variable names and class names that are known to the interpreter. I encourage you to spend some time with the Scala interpreter, explore the available options, and keep it handy when working through the examples in this book. Consider REPL as an essential part of learning a new language because it gives quick feedback. Table 2.1 explains the REPL options available to you.

In this section I round out basic Scala information with examples so you can gradually get comfortable with the language. You’ll use the Scala REPL to try out the examples, but you can use any of the development environments mentioned in the previous section that suit you.

In the following sections you’ll explore basic Scala types, including String and the value types ByteShortIntLongFloatDoubleBoolean, and Char. You’ll learn about two types of Scala variables, var and val, how they’re used, and how they’re different. You’ll also learn about the Scala functions, how to define them, and ways you can invoke them. Let’s start with the basic data types in Scala.

Basic types
If you’re a Java programmer, you’ll be glad to know that Scala supports all the basic value types (primitives): ByteShortIntLongFloatDoubleBoolean, and Char. Table 2.2 shows all eight basic value types supported by Scala. In Scala all the basic types are objects, and they’re defined under the scala package.

In Scala all the basic types are declared using initial caps. Instead of declaring something as int, in Scala it’s declared as Int. In earlier versions of Scala, programmers could use lowercase and uppercase interchangeably, but from version 2.8 on, if you declare any variable with int, you’ll get a compiler error. Even though the full qualifying name of Int is scala.Int, you can use only Int because the scala package is automatically imported into every Scala source, so you don’t have to fully qualify the basic types.
A small fact about Scala Predef
The Scala compiler implicitly imports java.lang, the scala package, and an object called scala.Predef to every compilation unit or Scala program. In the case of .NET instead of java.lang, it imports the system package. The scala.Predef object defines standard functions and type aliases for Scala programs. Because this object is imported automatically, all members of this object are available to you. scala.Predef is interesting, and you can learn a lot about Scala by looking at the scaladoc or source of the scala.Predef object.

To see all packages that are automatically imported, use the :imports command inside REPL:
scala> :imports
1) import scala.Predef._ (162 terms, 78 are implicit)
...

INTEGER LITERALS
Of the basic types defined in table 2.2, the integer literals can represent decimal, hexadecimal, and octal numbers. They’re determined by the way the literal is defined. If the literal is 0 or any nonzero digits, it’s a decimal number:
scala> val decimal = 12345
decimal: Int = 12345

DEFINITION
literal is a shorthand way to describe an object. The shorthand expression matches the structure of the project. You could create a string object by using the string literal "one" and also using the new keyword, as in new String("one").

Because integer literals are usually integers, Scala infers the type as integer, but if you want a Long type, you could add the suffix L or l:
scala> val decimal = 12345L
decimal: Long = 12345

Hexadecimal numbers start with 0x, and octal numbers should be prefixed with 0 (SI-5205: deprecate leading 0 for octal):
scala> val hexa = 0x23
hexa: Int = 35

scala> val octa = 023
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
octa: Int = 19

One thing to note while using hexadecimal and octal numbers is that the Scala interpreter always evaluates the result as a decimal number. You can specify the type of variable (sometimes called only value; more on this later) that you want when you think Scala type inference isn’t producing the result you are seeking. If you declare a variable with an integer literal, for example, Scala creates a variable of type integer unless it can’t fit the value within the range of integer values. In that case the Scala compiler throws an error. What do you do if you want a Byte type variable? You declare the type Byte when creating the variable:
scala> val i = 1
i: Int = 1

scala> val i2:Byte = 1
i2: Byte = 1

FLOATING-POINT LITERALS
Floating-point literals are composed of digits with a decimal point and an exponent part. But both the decimal point and the exponent part are optional. Floating-point literals are of type Float when they’re suffixed with F or f and areDouble otherwise:
scala> val d = 0.0
d: Double = 0.0

scala> val f = 0.0f
f: Float = 0.0

You can also create a Double variable with an exponent part. To declare a variable with the value of 1 times 10 to the power of 30 (1 times 10^30), it would look like this:
scala> val exponent = 1e30
exponent: Double = 1.0E30

CHARACTER LITERALS
A character literal is a single character enclosed in quotes. The character can be a printable Unicode character or an escape sequence:
scala> val capB = '\102'
capB: Char = B

scala> val capA = 'A'
capA: Char = A

Scala takes programming with Unicode characters to the next level. You can use not only literals but also printable Unicode characters as variable and method names. To create a variable name ans with the value 42 using Unicode characters, you would do something like the following:
scala> val \u0061\u006e\u0073 = 42
ans: Int = 42

Using Unicode characters for naming variables or functions in a program is a way to get yelled at by your peers, but in some contexts it improves the readability of the code.2 In the following example Unicode characters are used in the variable and method name. Before trying out these examples, make sure your editor supports Unicode encoding:
scala> var \u01b5 = 10
Ƶ: Int = 10

scala> def \u00f8 = { println("Infinity") }
ø: Unit

scala> (\u00f8)
Infinity

scala> (\u01b5)
res12: Int = 10

STRING LITERALS
A string literal is a sequence of characters in double quotes. The characters are either printable Unicode characters or escape sequences. If the string literal contains a double-quote character, it must be escaped with a slash (\):
scala> val bookName = "Scala in \"Action\""
bookName: java.lang.String = Scala in "Action"

The value of the string literal is of type String. Unlike other basic types, String is an instance of java.lang.String. As mentioned earlier, it’s automatically imported for you. Scala also supports a special multiline string literal that’s enclosed in triple quotes ("""). The sequence of characters is arbitrary, except that it may not contain a triple quote, and it doesn’t even necessarily have to be printable

The output of the multiLine variable has leading whitespaces, and maybe you don’t want that. There’s an easy fix:
scala> multiLine.split("\n").map(line=>line.trim()).mkString("\n")
res7: String =
This is a
multi line
string

Above code just split string into array and apply trim() to all single line(s). Afterward, just join all single line together by mkString.

I said that a Scala String object is nothing but a representative of a java.lang.String object; where did we get this new stripMargin method? There’s no method called stripMargin in java.lang.String. Again Predef is doing a little bit of magic by wrapping java.lang.String to another type called scala.collection.immutable.StringLike. If you look up the Scala documentation, you’ll see the stripMargin method with many other useful methods that Scala provides to string objects, along with the ones defined in the java.lang.String class.
scala> multiLine.strip // Enter Tab to show available methods
stripLineEnd stripMargin stripPrefix stripSuffix

XML LITERALS
Typically, working with XML means using third-party parsers and libraries, but in Scala it’s part of the language. Scala supports XML literals, where you can have XML fragments as part of the code:
scala> val book = Scala in ActionNilanjan Raychaudhuri
book: scala.xml.Elem = Scala in ActionNilanjan Raychaudhuri

Scala converts the XML literal to a scala.xml.Elem type object. It doesn’t stop here. You can put valid Scala code inside curly braces, {}, within the XML tags, and it works out great when you have to generate the XML dynamically:

Defining variables
You’ve already seen many examples about defining variables. In Scala there are two ways you can define variables: val and var. A val is a single assignment variable, sometimes called value. Once initialized a val can’t be changed or reassigned to some other value (similar to final variables in Java). On the other hand, var is reassignable; you can change the value of the variable over and over again after initial assignment:

The Scala interpreter does a good job of inferring the type of variable based on the value, but there are times when you’d like to, or have to, specify the type. You can specify the type of variable after the variable name, separating it by a colon (:). There’ll be situations where you want to declare a variable without assigning a value because you don’t yet know the value. In cases like these you can use the Scala placeholder character (_) to assign a default value:
scala> var willKnowLater:String = _
willKnowLater: String = null

scala> var willKnowLater:Int = _
willKnowLater: Int = 0

scala> var willKnowLater:Double = _
willKnowLater: Double = 0.0

Because the default value for String is null, in this example the value of willKnowLater is null. As an exercise, try using Scala placeholder characters with other basic types and see what values you get back. One point to note here is that when declaring variables (both val and var), you have to specify the value or _ (Scala placeholder); otherwise, the Scala interpreter will complain. The only case where you can have variables (only vars because val always needs a value when declared) without values assigned to them is when the variables are declared inside a class.

Sometimes you may want to declare a type whose value gets calculated based on some time-consuming operation, and you don’t want to do that when you declare the variable; you want to initialize it lazily because by default Scala evaluates the value assigned to var or val when it’s declared. To change this default behavior, use lazy val:
scala> lazy val forLater = someTimeConsumingOperation()
forLater: Unit =

The someTimeConsumingOperation() will be called when the variable forLater is used in any expression. Here’s another example that demonstrates the laziness:
scala> var a = 1
a: Int = 1

scala> lazy val b = a + 1
b: Int =

scala> a = 5
a: Int = 5

scala> b
res3: Int = 6

The lazy keyword is allowed only with val; you can’t declare lazy var variables in Scala. The variable declaration can sometimes have a pattern on the left side. Say you want to extract the first element of a List and assign it to a variable. You can do that using a pattern on the left side along with the variable declaration:
scala> var list = List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> 0::list // Adds element 0 at the beginning of this list.
res9: List[Int] = List(0, 1, 2, 3)

scala> list
res10: List[Int] = List(1, 2, 3)

scala> val first :: rest = list
first: Int = 1
rest: List[Int] = List(2, 3)

List is an immutable sequence type of collection (similar to List in Java and C#) in Scala, and in this case it holds a collection of integers from 1 to 3. The pattern on the left side matches the first element of the List, in this case 1, to the variable first and the rest to the tail of the list, 2 and 3. The :: (called cons) is a method defined in List. I cover more about pattern matching later in this chapter.

Defining functions
Functions are building blocks in Scala, and in this section you’re going to explore that topic. To define a function in Scala, use the def keyword followed by the method name, parameters, optional return type, =, and the method body. Figure 2.1 shows the syntax of the Scala function declaration.

Use a colon (:) to separate the parameter list from the return type. Multiple parameters are separated by commas (,). The equals sign (=) is used as a separator between the method signature and the method body. Let’s drop the parameter for the time being; you’ll come back to parameters later. You’ll create your first Scala function without parameters:
scala> def myFirstMethod():String = { "exciting times ahead" }
myFirstMethod: ()String

The return type of a Scala function is optional because Scala infers the return type of a function automatically. There are situations where it doesn’t work, but don’t worry about that until later. Improve the myFirstMethodmethod by removing the return type:
scala> def myFirstMethod() = { "exciting times ahead" }
myFirstMethod: ()String

scala> myFirstMethod()
res0: String = exciting times ahead

The significance of = after the method signature isn’t only to separate the signature from the method body but also to tell the Scala compiler to infer the return type of your function. If you omit that, Scala won’t infer your return type:
scala> def myFirstMethod(){ "exciting times ahead" }
myFirstMethod: ()Unit
scala> myFirstMethod()

In this case when you invoke the function using the function name and (), you’ll get no result. In the REPL output, notice that the return type of your function is no longer java.lang.String; it’s UnitUnit in Scala is like void in Java, and it means that the method doesn’t return anything.
TIP
Scala type inference is powerful, but use it carefully. If you’re creating a library and plan to expose your functions as a public API, it’s a good practice to specify the return type for the users of the library. In any case, if you think it’s not clear from the function what its return type is, either try to improve the name so that it communicates its purpose better or specify the return type.

Your myFirstMethod is simple: it returns the string "exciting times ahead" and when you have a function like that, you also drop the curly braces from the method body:
scala> def myFirstMethod() = "exciting times ahead"
myFirstMethod: ()String

If you invoke the function, you’ll get the same result. In Scala it’s always possible to take out unnecessary syntax noise from the code. Because you aren’t passing any parameters, you can take out the unused () from the declaration, and it almost looks like a variable declaration, except that instead of using var or val you’re using def:
scala> def myFirstMethod = "exciting times ahead"
myFirstMethod: java.lang.String
scala> myFirstMethod // When calling the function you can also leave out the parentheses
res1: String = exciting times ahead

Returning to function parameters, the following function called max takes two parameters and returns the one that’s the greater of the two:
scala> def max(a:Int, b:Int) = if (a > b) a else b
max: (a: Int, b: Int)Int

scala> max(5, 4)
res2: Int = 5 
scala> max(5, 7)
res3: Int = 7

By now you probably have figured out that specifying return is optional in Scala. You don’t have to specify the return keyword to return anything from the function. It will return the value of the last expression.In the previous case, if the if condition evaluates to true, then a is the last expression that is executed, so a is returned; otherwise b is returned. Even though the return type is optional, you do have to specify the type of the parameters when defining functions. Scala type inference will figure out the type of parameters when you invoke the function but not during the function declaration.

Sometimes it becomes necessary to create a function that will take an input and create a List from it. But the problem is you can’t determine the type of input yet. Someone could use your function to create a List of Int, and another person could use it to create a List of String. In cases like this you create a function in Scala by parameterized type. The parameter type will be decided when you invoke the function:
scala> def toList[A](value:A) = List(value)
toList: [A](value: A)List[A]

scala> toList(1)
res4: List[Int] = List(1)

scala> toList("Scala rocks")
res5: List[String] = List(Scala rocks)

When declaring the function, you denote the unknown parameterized type as A. Now when your toList is invoked, it replaces the A with the parameter type of the given parameter. In the method body you create an instance of immutableList by passing the parameter, and from the REPL output it’s clear that List is also using a parameterized type.

FUNCTION LITERALS
In Scala you can also pass a function as a parameter to another function, and most of the time in those cases I provide an inline definition of the function. This passing of functions as a parameter is sometimes loosely called closure(passing a function isn’t always necessarily closure; you’ll look into that in chapter 4). Scala provides a shorthand way to create a function in which you write only the function body, called function literals. Put that to a test. In this test you want to add all the elements of a List using function literals. This demonstrates a simple use of function literals in Scala. Here you’re creating a List of even numbers:
scala> val evenNumbers = List(2,4,6,8,10)
evenNumbers: List[Int] = List(2, 4, 6, 8, 10)

To add all the elements of List (scala.collection.immutable.List), you can use the foldLeft method defined in List. The foldLeft method takes two parameters: an initial value and a binary operation. It applies the binary operation to the given initial value and all the elements of the list. It expects the binary operation as a function that takes two parameters of its own to perform the operation, which in our case will be addition. If you can create a function that will take two parameters and add them, you’re finished with the test. The foldLeft function will call your function for every element in the List, starting with the initial value:
scala> evenNumbers.foldLeft(0){(a:Int, b:Int) => a + b}
res6: Int = 30

In this case the function (a: Int, b:Int) => a + b is called an anonymous function, or a function without a predefined name. You can improve your function by taking advantage of Scala’s type inference:
scala> evenNumbers.foldLeft(0){ (a,b) => a + b }
res7: Int = 30

Usually you have to specify the type of the parameter for top-level functions because Scala can’t infer the parameter types when declared, but for anonymous functions Scala inference can figure out the type from the context.In this case you’re using a list of integers and 0 as your initial value, and based on that Scala can easily infer the type of a and b as an integer. Scala allows you to go even further with your anonymous function: you can drop the parameters and only have the method body to make it a function literal. But in this case the parameters will be replaced with underscores (_). An underscore has a special meaning in Scala, and in this context it’s a placeholder for a parameter; in your case, use two underscores:
scala> evenNumbers.foldLeft(0) { _ + _ }
res8: Int = 30

Each underscore represents a parameter in your function literal. You’ve already seen another use of the underscore when assigning a default value to variables. In Scala you can use underscores in various places, and their meaning is determined solely by the context and where they’re used. Sometimes it gets a little confusing, so always remember that the value of the underscore is based on where it’s being used. You’ll see other uses of underscores throughout the book. Function literals are a common idiom in Scala, and you’ll find occurrences of them in Scala libraries and codebases.

In chapter 1 you saw the following example but without enough explanation of what’s going on with the code. Now, with your new knowledge of function literals, it should be pretty obvious that _.isUpper is a function literal:
  1. val hasUpperCase = name.exists(_.isUpper)  
In this case you’re invoking the given function literals for each character in the name string; when it finds an uppercase character, it will exit. The underscore in this context represents a character of name string.

USING SCALA CLOSURE AND FIRST-CLASS FUNCTIONS: AN EXAMPLE
Before moving to the next section, leaving the “defining function” section without a small working example of closure would be unfair. A closure is any function that closes over the environment in which it’s defined. For example, closure will keep track of any variable changes outside the function that are being referred to inside the function.

In the example you’ll try to add support for the word break. I haven’t talked about Scala keywords yet, but Scala doesn’t have break or continue. Once you get comfortable with Scala, you won’t miss them because Scala’s support of functional programming style reduces the need for break or continue. But assume you have a situation where you think having break would be helpful. Scala is an extensible programming language, so you can extend it to support break.

Use the Scala exception-handling mechanism to implement break in Scala. Throwing an exception will help you break the sequence of execution, and the catch block will help you reach the end of the call. Because break isn’t a keyword, you can use it to define your function so that it will throw an exception:
  1. def break = new RuntimeException("break exception")  
Another subject not yet covered is exception handling, but if you’ve used it in Java, C#, or Ruby, it should be easy to follow. In any case, you’ll read about exception handling in a later part of the chapter. Now create the main function that will take the operation that needs a breakable feature. Make it obvious and call it breakable:
  1. def breakable(op: => Unit) { ... }  
What’s this op: => UnitThe special right arrow (=>lets Scala know that the breakable function expects a function as a parameter. The right side of the => defines the return type of the function—in this case it’s Unit(similar to Java void)—and op is the name of the parameter. Because you haven’t specified anything on the left side of the arrow, it means that the function you’re expecting as a parameter doesn’t take any parameter for itself. But if you expect a function parameter that takes two parameters, such as foldLeft, you have to define it as follows:
  1. def foldLeft(initialValue: Int, operator: (Int, Int) => Int)= { ... }  
The breakable function that you declared takes a no-parameter function and returns Unit. Now, using these two functions, you could simulate the break. Let’s look at an example function that needs to break when the environment variable SCALA_HOME isn’t set; otherwise, it must do the work:
  1. def install = {  
  2.     val env = System.getenv("SCALA_HOME")  
  3.     if(env == nullbreak  
  4.     println("found scala home lets do the real work")  
  5. }  
Now inside the breakable function we need to catch the exception that will get raised when break is called from the install function:
  1. try {  
  2.     op  
  3. catch { case _ => }  
That’s it. The following listing holds the complete code.
- Listing 2.1 breakable, break, and install functions
  1. val breakException = new RuntimeException("break exception")  
  2. def breakable(op: => Unit) {  
  3.     try {  
  4.         op  
  5.     } catch { case _ => }  
  6. }  
  7. def break = throw breakException  
  8. def install = {  
  9.     val env = System.getenv("SCALA_HOME")  
  10.     if(env == nullbreak  
  11.         println("found scala home lets do the real work")  
  12. }  
  13.   
  14. breakable(install)  
In Scala if the last argument of a function is of function type, you can pass it as closure. This syntax sugar is useful in creating DSLs. In the next chapter you’ll look into how closures are converted into objects; remember, everything in Scala is an object.
NOTE 
Scala already provides breakable as part of the library. Look for scala.util.control.Breaks. You should use Breaks if you have a need for break. Again, I’d argue that once you look into functional programming with Scala in detail, you’ll probably never have a need for break.


沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!