Friday 16 November 2018

An In-Depth Look at the HBase Architecture


HBase Architectural Components


Physically, HBase is composed of three types of servers in a master slave type of architecture. Region servers serve data for reads and writes. When accessing data, clients communicate with HBase RegionServers directly. Region assignment, DDL (create, delete tables) operations are handled by the HBase Master process. Zookeeper, which is part of HDFS, maintains a live cluster state.

The Hadoop DataNode stores the data that the Region Server is managing. All HBase data is stored in HDFS files. Region Servers are collocated with the HDFS DataNodes, which enable data locality (dumping the data close to where it is needed) for the data served by the Region Servers. HBase data is local when it is written, but when a region is moved, it is not local until compaction.

The NameNode maintains metadata information for all the physical data blocks that comprise the files.




Regions


HBase Tables are divided horizontally by row key range into “Regions.” A region contains all rows in the table between the region’s start key and end key. Regions are assigned to the nodes in the cluster, called “Region Servers,” and these serve data for reads and writes. A region server can serve about 1,000 regions.




HBase HMaster

Region assignment, DDL (create, delete tables) operations are handled by the HBase Master.
A master is responsible for:
  • Coordinating the region servers
    - Assigning regions on startup , re-assigning regions for recovery or load balancing
    - Monitoring all RegionServer instances in the cluster (listens for notifications from zookeeper)
  • Admin functions
    - Interface for creating, deleting, updating tables

ZooKeeper: The Coordinator

HBase uses ZooKeeper as a distributed coordination service to maintain server state in the cluster. Zookeeper maintains which servers are alive and available, and provides server failure notification. Zookeeper uses consensus to guarantee common shared state. Note that there should be three or five machines for consensus.


How the Components Work Together

Zookeeper is used to coordinate shared state information for members of distributed systems. Region servers and the active HMaster connect with a session to ZooKeeper. The ZooKeeper maintains ephemeral nodes for active sessions via heartbeats.




Each Region Server creates an ephemeral node. The HMaster monitors these nodes to discover available region servers, and it also monitors these nodes for server failures. HMasters vie to create an ephemeral node. Zookeeper determines the first one and uses it to make sure that only one master is active. The active HMaster sends heartbeats to Zookeeper, and the inactive HMaster listens for notifications of the active HMaster failure.
If a region server or the active HMaster fails to send a heartbeat, the session is expired and the corresponding ephemeral node is deleted. Listeners for updates will be notified of the deleted nodes. The active HMaster listens for region servers, and will recover region servers on failure. The Inactive HMaster listens for active HMaster failure, and if an active HMaster fails, the inactive HMaster becomes active.

HBase First Read or Write


There is a special HBase Catalog table called the META table, which holds the location of the regions in the cluster. ZooKeeper stores the location of the META table.
This is what happens the first time a client reads or writes to HBase:
  1. The client gets the Region server that hosts the META table from ZooKeeper.
  2. The client will query the .META. server to get the region server corresponding to the row key it wants to access. The client caches this information along with the META table location.
  3. It will get the Row from the corresponding Region Server.
For future reads, the client uses the cache to retrieve the META location and previously read row keys. Over time, it does not need to query the META table, unless there is a miss because a region has moved; then it will re-query and update the cache.



HBase Meta Table




  • This META table is an HBase table that keeps a list of all regions in the system.


  • The .META. table is like a b tree.


  • The .META. table structure is as follows:
  •               - Key: region start key,region id
                - Values: RegionServer




    Region Server Components



    HBase Write Steps (1)


    When the client issues a Put request, the first step is to write the data to the write-ahead log, the WAL:

    - Edits are appended to the end of the WAL file that is stored on disk.

    - The WAL is used to recover not-yet-persisted data in case a server crashes.





    HBase Write Steps (2)


    Once the data is written to the WAL, it is placed in the MemStore. Then, the put request acknowledgement returns to the client.

    HBase MemStore


    The MemStore stores updates in memory as sorted KeyValues, the same as it would be stored in an HFile. There is one MemStore per column family. The updates are sorted per column family.




    HBase Region Flush


    When the MemStore accumulates enough data, the entire sorted set is written to a new HFile in HDFS. HBase uses multiple HFiles per column family, which contain the actual cells, or KeyValue instances. These files are created over time as KeyValue edits sorted in the MemStores are flushed as files to disk.
    Note that this is one reason why there is a limit to the number of column families in HBase. There is one MemStore per CF; when one is full, they all flush. It also saves the last written sequence number so the system knows what was persisted so far.
    The highest sequence number is stored as a meta field in each HFile, to reflect where persisting has ended and where to continue. On region startup, the sequence number is read, and the highest is used as the sequence number for new edits.

    HBase HFile

    Data is stored in an HFile which contains sorted key/values. When the MemStore accumulates enough data, the entire sorted KeyValue set is written to a new HFile in HDFS. This is a sequential write. It is very fast, as it avoids moving the disk drive head.

    HBase HFile Structure

    An HFile contains a multi-layered index which allows HBase to seek to the data without having to read the whole file. The multi-level index is like a b+tree:
    • Key value pairs are stored in increasing order
    • Indexes point by row key to the key value data in 64KB “blocks”
    • Each block has its own leaf-index
    • The last key of each block is put in the intermediate index
    • The root index points to the intermediate index
    The trailer points to the meta blocks, and is written at the end of persisting the data to the file. The trailer also has information like bloom filters and time range info. Bloom filters help to skip files that do not contain a certain row key. The time range info is useful for skipping the file if it is not in the time range the read is looking for.

    HFile Index


    The index, which we just discussed, is loaded when the HFile is opened and kept in memory. This allows lookups to be performed with a single disk seek.

    HBase Read Merge

    We have seen that the KeyValue cells corresponding to one row can be in multiple places, row cells already persisted are in Hfiles, recently updated cells are in the MemStore, and recently read cells are in the Block cache. So when you read a row, how does the system get the corresponding cells to return? A Read merges Key Values from the block cache, MemStore, and HFiles in the following steps:
    1. First, the scanner looks for the Row cells in the Block cache - the read cache. Recently Read Key Values are cached here, and Least Recently Used are evicted when memory is needed.
    2. Next, the scanner looks in the MemStore, the write cache in memory containing the most recent writes.
    3. If the scanner does not find all of the row cells in the MemStore and Block Cache, then HBase will use the Block Cache indexes and bloom filters to load HFiles into memory, which may contain the target row cells.
    HBase Read Merge

    As discussed earlier, there may be many HFiles per MemStore, which means for a read, multiple files may have to be examined, which can affect the performance.  This is called read amplification.

    HBase Minor Compaction

    HBase will automatically pick some smaller HFiles and rewrite them into fewer bigger Hfiles. This process is called minor compaction. Minor compaction reduces the number of storage files by rewriting smaller files into fewer but larger ones, performing a merge sort.

    HBase Major Compaction

    Major compaction merges and rewrites all the HFiles in a region to one HFile per column family, and in the process, drops deleted or expired cells. This improves read performance; however, since major compaction rewrites all of the files, lots of disk I/O and network traffic might occur during the process. This is called write amplification.
    Major compactions can be scheduled to run automatically. Due to write amplification, major compactions are usually scheduled for weekends or evenings. Note that MapR-DB has made improvements and does not need to do compactions. A major compaction also makes any data files that were remote, due to server failure or load balancing, local to the region server.

    Region = Contiguous Keys

    Let’s do a quick review of regions:
    • A table can be divided horizontally into one or more regions. A region contains a contiguous, sorted range of rows between a start key and an end key
    • Each region is 1GB in size (default)
    • A region of a table is served to the client by a RegionServer
    • A region server can serve about 1,000 regions (which may belong to the same table or different tables)

    Region Split

    Initially there is one region per table. When a region grows too large, it splits into two child regions. Both child regions, representing one-half of the original region, are opened in parallel on the same Region server, and then the split is reported to the HMaster. For load balancing reasons, the HMaster may schedule for new regions to be moved off to other servers.

    Read Load Balancing

    Splitting happens initially on the same region server, but for load balancing reasons, the HMaster may schedule for new regions to be moved off to other servers. This results in the new Region server serving data from a remote HDFS node until a major compaction moves the data files to the Regions server’s local node. HBase data is local when it is written, but when a region is moved (for load balancing or recovery), it is not local until major compaction.

    HDFS Data Replication

    All writes and Reads are to/from the primary node. HDFS replicates the WAL and HFile blocks. HFile block replication happens automatically. HBase relies on HDFS to provide the data safety as it stores its files. When data is written in HDFS, one copy is written locally, and then it is replicated to a secondary node, and a third copy is written to a tertiary node.

    HDFS Data Replication (2)

    The WAL file and the Hfiles are persisted on disk and replicated, so how does HBase recover the MemStore updates not persisted to HFiles? See the next section for the answer.

    HBase Crash Recovery

    When a RegionServer fails, Crashed Regions are unavailable until detection and recovery steps have happened. Zookeeper will determine Node failure when it loses region server heart beats. The HMaster will then be notified that the Region Server has failed.
    When the HMaster detects that a region server has crashed, the HMaster reassigns the regions from the crashed server to active Region servers. In order to recover the crashed region server’s memstore edits that were not flushed to disk. The HMaster splits the WAL belonging to the crashed region server into separate files and stores these file in the new region servers’ data nodes. Each Region Server then replays the WAL from the respective split WAL, to rebuild the memstore for that region.

    Data Recovery

    WAL files contain a list of edits, with one edit representing a single put or delete. Edits are written chronologically, so, for persistence, additions are appended to the end of the WAL file that is stored on disk.
    What happens if there is a failure when the data is still in memory and not persisted to an HFile? The WAL is replayed. Replaying a WAL is done by reading the WAL, adding and sorting the contained edits to the current MemStore. At the end, the MemStore is flush to write changes to an HFile.

    Apache HBase Architecture Benefits

    HBase provides the following benefits:
    • Strong consistency model
      - When a write returns, all readers will see same value
    • Scales automatically
      - Regions split when data grows too large
      - Uses HDFS to spread and replicate data
    • Built-in recovery
      - Using Write Ahead Log (similar to journaling on file system)
    • Integrated with Hadoop
      - MapReduce on HBase is straightforward

    Apache HBase Has Problems Too…

    • Business continuity reliability:
      - WAL replay slow
      - Slow complex crash recovery
      - Major Compaction I/O storms

    MapR-DB with MapR-FS does not have these problems

    The diagram below compares the application stacks for Apache HBase on top of HDFS on the left, Apache HBase on top of MapR's read/write file system MapR-FS in the middle, and MapR-DB and MapR-FS in a Unified Storage Layer on the right.
    MapR-DB exposes the same HBase API and the Data model for MapR-DB is the same as for Apache HBase. However the MapR-DB implementation integrates table storage into the MapR file system, eliminating all JVM layers and interacting directly with disks for both file and table storage.
    MapR-DB offers many benefits over HBase, while maintaining the virtues of the HBase API and the idea of data being sorted according to primary key. MapR-DB provides operational benefits such as no compaction delays and automated region splits that do not impact the performance of the database. The tables in MapR-DB can also be isolated to certain machines in a cluster by utilizing the topology feature of MapR. The final differentiator is that MapR-DB is just plain fast, due primarily to the fact that it is tightly integrated into the MapR file system itself, rather than being layered on top of a distributed file system that is layered on top of a conventional file system.

    Key differences between MapR-DB and Apache HBase


    Monday 29 October 2018

    Scala Documentation with Worked Examples

    REPL(READ EVALUATE PROCESS LOAD)
    ---------------------------------
    Scalaís REPL provides support for evaluating and executing code one line at a time with helpful feedback

     You can enter single lines of code to evaluate and compile, and any variables you create are available for the lifetime of your session. A multiline paste mode supports entering multiple lines of code to be compiled together (instead of individually), and external source code and libraries can be loaded at any time.

    Here is how the input and response should appear in the REPL:

    scala> println("Hello, World") Hello, World
    scala> Congratulations, you have now written and executed Scala code.

    The println() function, universally available to all Scala code, prints a message to the JVMís stdout stream

    REPL history is stored between sessions, so you can quit, run the scala command again, and press up arrow to access your previous commands.

    scala> 5 * 7
           res0: Int = 35

    Now that res0 contains the output of the multiplication command, lets make use of it. Type 2 * res0 at a fresh prompt and press Return. You should see something like this:
    scala> 2 * res0
             res1: Int = 70



    Working with Data: Literals, Values, Variables, and Types
                 ********************************************************************

    we will cover the core data and variable types in Scala. Letís start with the definitions of the terms literal, value, variable, and type:
    ï A literal (or literal data) is data that appears directly in the source code, like the number 5, the character A, and the text ìHello, World.î
    ï A value is an immutable, typed storage unit. A value can be assigned data when it is defined, but can never be reassigned.
    ï A variable is a mutable, typed storage unit. A variable can be assigned data when it is defined and can also be reassigned data at any time.
     Ã¯ A type is the kind of data you are working with, a definition or classification of data. All data in Scala corresponds to a specific type, and all Scala types are defined as classes with methods that operate on the data.
    The data stored in values and variables in Scala will get automatically deallocated by the Java Virtual Machineís garbage collection when they are no longer used. There is no ability, or need, to deallocate them manually.

      Values
    ----------------
    Values are immutable, typed storage units, and by convention are the default method for storing data.
    You can define a new value using the val keyword.

    Syntax: Defining a Value
    ----------------------------
    val <identifier>[: <type>] = <data>


    Values require both a name and assigned data, but they do not require an explicit type. If the type is not specified (i.e., the ì: <type>î syntax is not included), the Scala compiler will infer the type based on the assigned data.
     Here are some examples of defining values with their type in the Scala REPL:

    scala> val x: Int = 20 x: Int = 20
    scala> val greeting: String = "Hello, World" greeting: String = Hello, World
    scala> val atSymbol: Char = '@' atSymbol: Char = @


     The Scala compiler will then discern the type of the value from its assignment, a process known as type inference. Values defined without a type are not typeless; they are assigned the proper type just as if the type had been included in the definition.

    The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.

      val x: Int = "Hello"
    The error here affirms that an Int type cannot be used to store a String.


    VARIBLES:
    -------------------

    In computer science the term variable typically refers to a unique identifier corre- sponding to an allocated or reserved memory space, into which values can be stored and from which values can be retrieved. As long as the memory space is reserved, it can be assigned new values over and over again. Thus, the contents of the memory space are dynamic, or variable.

     Variables are dynamic, mutable, and reassignable (with the exception of those defined with special restrictions such as Javaís final keyword).

    an immutable value will be more stable and less prone to errors than mutable data that may be modified at unexpected times.

    The var keyword is used to define a variable with a given name, type, and assignment.
    Syntax: Defining a Variable
    var <identifier>[: <type>] = <data>


    NAMING:
    --------------
    Scala names can use letters, numbers, and a range of special operator characters. This makes it possible to use standard mathematical operators (e.g., * and :+) and constants (e.g., p and f) in place of longer names to make the code more expressive

    while periods are reserved for access to the fields and methods of objects (instantiated types).

    Here are the rules for combining letters, numbers, and characters into valid identifiers in Scala:
    1. A letter followed by zero or more letters and digits.
    Naming | 13
    2. A letter followed by zero or more letters and digits, then an underscore (_), and then one or more of either letters and digits or operator characters.
     3. One or more operator characters.
     4. One or more of any character except a backquote, all enclosed in a pair of back- quotes.

    Names enclosed in backquotes can, unlike the other names, be re- served keywords in Scala such as true, while, =, and var
    Letís try out some of these naming rules in the REPL:
    scala> val p = 3.14159                                                  
    p: Double = 3.14159
    scala> val $ = "USD currency symbol"
    $: String = USD currency symbol
    scala> val o_O = "Hmm"
    o_O: String = Hmm
    scala> val 50cent = "$0.50"
              <console>:1: error: Invalid literal number
            val 50cent = "$0.50"^
    scala> val a.b = 25                                                      
    <console>:7: error: not found:
          value a       val a.b = 25
    scala> val `a.b` = 4                                                    
    a.b: Int = 4
    The special character ìpî is a valid Scala identifier.
    The value name ì50centî is invalid because names cannot start with numbers. In this case the compiler started parsing the name as a literal number and ran into problems at the letter ìcî.
     The value name ìa.bî is invalid because a period isnít an operator character. Rewriting this value with backquotes fixes the problem, although the aesthetics of using backquotes isnít that great.
    Value and variable names, by convention, should start with a lowercase letter and then capitalize additional words. This is popularly known as camel case, and though not

    Types
    -----------------

    Scala has both numeric (e.g., Int and Double) and nonnumeric types (e.g., String) that can be used to define values and variables. These core types are the building blocks for all other types including objects and collections, and are themselves objects that have methods and operators that act on their data.
     Scala only supports its own integer class, Int.
    Numeric Data Types Table 2-1 displays Scalaís numeric data types.
    Table 2-1. Core numeric types

    Name Description Size Min Max
    Byte Signed integer 1 byte ñ127 128
    Short Signed integer 2 bytes ñ32768 32767 Int Signed integer 4 bytes ñ231 231ñ1 Long Signed integer 8 bytes ñ263 263ñ1
    Float Signed floating point 4 bytes n/a n/a
    Double Signed floating point 8 bytes n/a n/a

    Scala does not allow automatic conversion from higher ranked types to lower ranked types. This makes sense, because you could otherwise lose data if you convert to a type with less storage. Here is an example of trying to automatically convert a higher ranked type to a lower ranked type and the ensuing error:
    scala> val l: Long = 20 l: Long = 20
    scala> val i: Int = l
    <console>:8: error: type mismatch; found   : Long required: Int       val i: Int = l You can choose to manually convert between types using the toType methods available on all numeric types.

    For example, here is a Long value that can be safely converted to type Int using the toInt method, because its data is within the storage bounds of an Int:
    scala> val l: Long = 20 l: Long = 20
    scala> val i: Int = l.toInt i: Int = 20 An alternative to using explicit types is to specify the type of your literal data directly, using Scalaís notation for literal types.


    Strings
    ---------------
    The String type represents ìstringsî of text, one of the most common core types in any programming language. Scalaís String is built on Javaís String and adds unique fea- tures like multiline literals and string interpolation.

    Write String literals using double quotes, with special characters escaped with back- slashes:

    scala> val hello = "Hello There" hello: String = Hello There

    scala> val signature = "With Regards, \nYour friend" signature: String = With Regards, Your friend

    Like numeric types, the String type supports the use of math operators.
    For example, use the equals operator (==) to compare two String values. Unlike Java, the equals operator (==) checks for true equality, not object reference equality:
    scala> val greeting = "Hello, " + "World" greeting: String = Hello, World
    scala> val matched = (greeting == "Hello, World") matched: Boolean = true
    scala> val theme = "Na " * 16 + "Batman!" // what do you expect this to print?

    String interpolation Building a String based on other values is reasonably easy to do with string addition.

    Here is a String built by adding text before and after the Float value:
    scala> val approx = 355/113f approx: Float = 3.141593

    scala> println("Pi, using 355/113, is about " + approx + "." ) Pi, using 355/113, is about 3.141593. A more direct way to combine your values or variables inside a String is with string interpolation, a special mode where external value and variable names are recognized and resolved. The Scala notation for string interpolation is an ìsî prefix added before the first double quote of the string. Then dollar sign operators ($) (with optional braces) can be used to note references to external data.

    Here is the example again using string interpolation:
    -------------------------
    scala> println(s"Pi, using 355/113, is about $approx." )
    Pi, using 355/113, is about 3.141593.


    scala> s"How do you like them ${item}s?" res0: String = How do you like them apples?

    scala> s"Fish n chips n vinegar, ${"pepper "*3}salt" res1: String = Fish n chips n vinegar, pepper pepper pepper salt

     To use printf notation change the prefix to an ìfî and follow the end of the reference immediately with the printf notation:

    scala> f"Enjoying this $item ${355/113.0}%.5f times today"
    res3: String = Enjoying this apple 3.14159 times today
    These printf notations make the references a little harder to read than in the previous examples, but do provide essential control over the output.


    Regular expressions
    --------------------------------

    regular expressions may be different from the format you have used with other languages and tools.
    The String type provides a number of built-in operations that support regular expres- sions. Table 2-3 displays a selection of these operations

    Table 2-3. Regular expression operations

    Name Example Description
    --------           -------------------- --------------------------------------------
    matches "Froggy went a' courting" matches ".* courting" Returns true if the regular expression matches the entire string.
    replaceAll "milk, tea, muck" replaceAll ("m[^ ] +k", "coffee") Replaces all matches with replacement text.
    replaceFirst "milk, tea, muck" replaceFirst ("m[^ ] +k", "coffee") Replaces the first match with replacement text.


    Syntax: Capturing Values with Regular Expressions
    val <Regex value>(<identifier>) = <input string>

    overview variable datatypes
    ----------------------
    Figure 2-1 shows the hierarchy of Scalaís core (numeric and nonnumeric) types.





    At the bottom of the Scala type hierarchy are the Nothing and Null types. Nothing is a subtype of every other type and exists to provide a compatible return type for operations that significantly affect a programís flow

    Type operations
    ----------------------
    Table 2-5 displays the operations available on all types in Scala. The toString and hashCode methods are required on all JVM instances.
    Table 2-5. Common type operations
    Name Example Description
    asInstanceOf[<type>]    5.asInstanceOf[Long] converts the value to a value of the desired type. Causes an error if the value is not compatible with the new type.

    getClass (7.0 / 5).getClass Returns the type (i.e., the class) of a value.
    isInstanceOf (5.0).isInstanceOf[Float] Returns true if the value has the given type.
    hashCode "A".hashCode     Returns the hash code of the value, useful for hash- based collections.
    to<type> 20.toByte; 47.toFloat Conversion functions to convert a value to a compatible value.
    toString (3.0 / 4.0).toString Renders the value to a String.

    NOTE:
    ----------
    Avoid asInstanceOf The asInstanceOf operation will cause an error if the value can- not be converted to the requested type. To avoid runtime errors with this operation, prefer the to<type> typed conversion operations when possible

    Tuples
    ---------------------
    A tuple is an ordered container of two or more values, all of which may have different types. You may be familiar with this term from working with relational databases, where a single row of a table is considered its own tuple. Tuples can be useful when you need to logically group values, representing them as a coherent unit. Unlike lists and arrays, however, there is no way to iterate through elements in a tuple. Its purpose is only as a container for more than one value. You can create a tuple by writing your values separated by a comma and surrounded by a pair of parentheses.

    Syntax: Create a Tuple( <value 1>, <value 2>[, <value 3>...] )
    For example, here is a tuple containing Int, String, and Boolean values:
    scala> val info = (5, "Korben", true) info: (Int, String, Boolean) = (5,Korben,true)
    You can access an individual element from a tuple by its 1-based index (e.g., where the first element is 1, second is 2, etc.):
    scala> val name = info._2 name: String = Korben
    An alternate form of creating a 2-sized tuple is with the relation operator (->). This is a popular shortcut for representing key-value pairs in tuples:
    scala> val red = "red" -> "0xff0000" red: (String, String) = (red,0xff0000)
    scala> val reversed = red._2 -> red._1 reversed: (String, String) = (0xff0000,red)
    Types | 25
    Tuples provide a generic means to structure data, and are useful when you need to group discrete elements for handling.
    scala>val info="sss" -> "hi"

    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Expressions and Conditionals
    ****************************************************************************************************************************************************

    The term expression as used in this book indicates a unit of code that returns a value after it has been executed. One or more lines of code can be considered an expression if they are collected together using curly braces ({ and }). This is known as an expression block. Expressions provide a foundation for functional programming because they make it possible to return data instead of modifying existing data (such as a variable). This enables the use of immutable data, a key functional programming concept where new data is stored in new values instead of in existing variables.
     The return values of expressions will be passed to other expressions or stored into values. As you migrate from using variables, your functions and expressions will have fewer side effects. In other words, they will purely act on the input you give them without affecting any data other than their return value. This is one of the main goals and benefits of functional programming

    Defining Values and Variables with Expressions
    -----------------------------------------------------------

    Syntax: Defining Values and Variables, Using Expressions
    val <identifier>[: <type>] = <expression> var <identifier>[: <type>] = <expression>

    Expression Blocks
    --------------------------

     The last expression in the block is the return value for the entire block.

    Expression blocks can span as many lines as you need. The preceding example could have been rewritten without the semicolons as follows:
    scala> val amount = {     |   val x = 5 * 20     |   x + 10     | }
    amount: Int = 110 Expression blocks are also nestable, with each level of expression block having its own scoped values and variables.
    Here is a short example demonstrating a three-deep nested expression block:
    scala> { val a = 1; { val b = a * 2; { val c = b + 4; c } } }
    res5: Int = 6

    Statements
    ----------------
    A statement is just an expression that doesnít return a value

    scala> val x = 1
    x: Int = 1
    The REPL repeats the definition of x but there is not actual data returned that can be used to create a new value. A statement block, unlike an expression block, does not return a value. Because a state- ment block has no output, it is commonly used to modify existing data or make changes outside the scope of the application

    If..Else Expression Blocks
    ------------------------------------

    As a matter of practice you can write these same ìif .. else if .. elseî blocks in Scala and they will work just as you have experienced them in Java and other languages. As a matter of formal syntax, however, Scala only supports a single ìifî and optional ìelseî block, and does not recognize the ìelse ifî block as a single construct. So how do ìelse ifî blocks still work correctly in Scala? Because ìif .. elseî blocks are based on expression blocks, and expression blocks can be easily nested, an ìif .. else if .. elseî expression is equivalent to a nested ìif .. else { if .. else }î expression. Logically this is exactly the same as an ìif .. else if .. elseî block, and as a matter of syntax Scala recognizes the second ìif elseî as a nested expression of the outer ìif .. elseî block

    If Expressions Syntax: Using an If Expression
    if (<Boolean expression>) <expression>

    If the Boolean expression returns false, what do you expect the if block to return?
    scala> val result = if ( false ) "what does this return?"
    result: Any = ()
    The type of the result value in this example is unspecified so the compiler used type inference to determine the most appropriate type. Either a String or Unit could havebeen returned, so the compiler chose the root class Any. This is the one class common to both String (which extends AnyRef) and to Unit (which extends AnyVal).

    If-Else Expressions Syntax: If .. Else Expressions
    if (<Boolean expression>) <expression> else <expression>
    Here is an example:
    scala> val x = 10; val y = 20
    x: Int = 10 y: Int = 20
    scala> val max = if (x > y) x else y
    max: Int = 20
    --->Some wonder why Scala doesnít have a ternary expression (popular in C and Java) where the punctuation characters ? and : act as a one-line if and else expression

    ---> if expressions without an else should always use curly braces, because they tend to be statements that create side effects.

    ---->if..else blocks are a simple and common way to write conditional logic. There are other, more elegant ways to do so in Scala, however, using match expressions

    ----------------------------------------------------------
    Match Expressions
    ---------------------------------------------------------

    Match expressions are akin to Cís and Javaís ìswitchî statements, where a single input item is evaluated and the first pattern that is ìmatchedî is executed and its value re- turned.

    --->in scala matching expressions are maily in two types and one is alternative expression.they are

    ---->In fact, most Scala developers prefer match expressions over ìif .. elseî blocks because of their expressiveness and concise syntax

    Syntax: Using a Match Expressio<expression> match {  case <pattern match> => <expression>  [case...] }

    scala> val status = 500
    status: Int = 500

    scala> val message = status match {  
     |     case 200 =>            "ok"  
     |     case 400 => {  
    |         println("ERROR - we called the service incorrectly")  
    |         "error"  
    |  
              }
    |  
           case 500 => {  
    |      println("ERROR - the service encountered an erro
    |         "error"
    |
            }
    |
        }
     ERROR - the service encountered an error
    message: String = error


     There is no limit to the number of statements and expressions you can have inside a case block, although only the last expression will be used for the match expressionís return value.

    Syntax: A Pattern Alternative
    -------------------------------------
    case <pattern 1> | <pattern 2> .. => <one or more expressions>

    scala> val day = "MON" day: String = MON

    scala> val kind = day match {     |   case "MON" | "TUE" | "WED" | "THU" | "FRI" =>     |     "weekday"     |
         case "SAT" | "SUN" =>     |     "weekend"     |
               }
    kind: String = weekday

    Matching with Wildcard Patterns
    -------------------------------------------

    Syntax: A Value Binding Pattern
    case <identifier> => <one or more expressions>
    Here is an example that tries to match a specific literal and otherwises uses value binding to ensure all other possible values are matched:
    scala> val message = "Ok"
    message: String = Ok
    scala> val status = message match {     |   case "Ok" => 200     |
     case other => {     |     println(s"Couldn't parse $other")     |     -1     |
               }
       |
       }
                  status: Int = 200

    The value other is defined for the duration of the case block and is assigned the value of message, the input to the match expression. The other type of wildcard pattern is the use of the wildcard operator. This is an un- derscore (_) character that acts as an unnamed placeholder for the eventual value of an expression at runtime. As with value binding, the underscore operator doesnít provide a pattern to match against, and thus it is a wildcard pattern that will match any input value.

    Syntax: A Wildcard Operator Pattern
    -----------------------------------------------
    case _ => <one or more expressions>

    The wildcard cannot be accessed on the right side of the arrow, unlike with value bind- ing. If you need to access the value of the wildcard in the case block, consider using a value binding, or just accessing the input to the match expression (if available).


    Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores. For example, the equation 5 * _ = 15 is a problem that must be solved for _, the missing value.
    Here is a similar example to the one earlier only

    Here is a similar example to the one earlier only with a wildcard operator instead of a bound value:
    scala> val message = "Unauthorized"
    message: String = Unauthorized
    scala> val status = message match {     |   case "Ok" => 200     |   case _ => {     |     println(s"Couldn't parse $message")     |     -1     |   }     | } Couldn't parse Unauthorized             status: Int = -1
                  In this case the underscore operator matches the runtime value of the input to the match expression.
    However, it canít be accessed inside the case block as a bound value would, and thus the input to the match expression is used to create an informative println statement.

    Matching with Pattern Guards
    ---------------------------------------
    A pattern guard adds an if expression to a value-binding pattern, making it possible to mix conditional logic into match expressions. When a pattern guard is used the pattern will only be matched when the if expression returns true.

    Syntax: A Pattern Guard
    -------------------------------
    case <pattern> if <Boolean expression> => <one or more expressions>

    Unlike regular if expressions, the if expression here doesnít require parentheses (( and )) around its Boolean expression. Regular if expressions require the parentheses in order to simplify the job of parsing the full command and delineate the Boolean expression from the conditional expression. In this case the arrow (=>) handles that task and simplifies parsing. You can, however, add the parentheses around the Boolean ex- pression if you wish. Letís use a pattern guard to differentiate between a nonnull and a null response and report the correct message:
    scala> val response: String = null
    response: String = null
    scala> response match {     |   case s if s != null => println(s"Received '$s'")     |   case s => println("Error! Received a null response")     | }
    Error! Received a null response

    Matching Types with Pattern Variables
    ------------------------------------------------

    Another way to do pattern matching in a match expression is to match the type of the input expression. Pattern variables, if matched, may convert the input value to a value with a different type. This new value and type can then be used inside the case block.

    Syntax: Specifying a Pattern Variable
    ----------------------------------------------
    case <identifier>: <type> => <one or more expressions>

    port of polymorphic types in Scala should be a clue to a match expressionís utility. A value of type Int may get assigned to another value of type Any, or it may be returned as Any from a Java or Scala library call

    Letís reproduce this situation by creating an Int, assigning it to an Any, and using a match expression to resolve its true type:
    scala> val x: Int = 12180
    x: Int = 12180
    scala> val y: Any = x
    y: Any = 12180
    scala> y match {     |   case x: String => s"'x'"     |   case x: Double => f"$x%.2f"     |   case x: Float => f"$x%.2f"     |   case x: Long => s"${x}l"     |   case x: Int => s"${x}i"     |      }
    res9: String = 12180i


    Nested Iterators
    -----------------------
    Nested iterators are extra iterators added to a for-loop, multiplying the total number of iterations by their iterator count. They are called nested iterators because adding them to an existing loop has the same effect as if they were written as a separate nested loop. Because the total number of iterations is the product of all of the iterators, adding a nested loop that will iterate once will not change the number of iterations, whereas a nested loop that does not iterate will cancel all iterations

    Here is an example of a for-loop with two iterators:
    scala> for { x <- 1 to 2     |       y <- 1 to 3 }     | { print(s"($x,$y) ") }
    scala>(1,1) (1,2) (1,3) (2,1) (2,2) (2,3)
     Because the product of the two iterators is six iterations, the print statement is called six times.


    While and Do/While Loops
    ---------------------------------------

    In addition to for-loops Scala also supports ìwhileî and ìdo/whileî loops, which repeat a statement until a Boolean expression returns false. These are not as commonly used as for-loops in Scala, however, because they are not expressions and cannot be used to yield values.

    Syntax: A While Loop
    ----------------------------
    while (<Boolean expression>) statement

    As a very simple example here is a while loop that decrements a number repeatedly until it is no longer greater than zero:
    scala> var x = 10; while (x > 0) x -= 1

    The ìdo/whileî loop is similar but the statement is executed before the Boolean expres- sion is first evaluated. In this example I have a Boolean expression that will return false, but is only checked after the statement has had a chance to run:
    scala> val x = 0 x: Int = 0
    scala> do println(s"Here I am, x = $x") while (x > 0)
    Here I am, x = 0
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


    FUNCTIONS
    ****************************************************************

    ---> Functions are the core building blocks of reusable logic.

    ----> In Scala, functions are named, reusable expressions. They may be parameterized and they may return a value, but neither of these features are required. These features are, however, useful for ensuring maximum reusability and composability. They will also help you write shorter, more readable, and more stable applications. Using parameter- ized functions you can normalize duplicated code, simplifying your logic and making it more discoverable. Testing your code becomes easier, because normalized and para- meterized logic is easier to test than denormalized logic repeated throughout your code

    Syntax: Defining an Input-less Function   (SOME TIMES ITS NOWN TO BE UNNUMOUS FUNCTIONS)
    ------------------------------------------------
    def <identifier> = <expression>  OR      def() <identifier> = <expression>

    NOTE:A Scala convention for input-less functions is that they should be defined with empty parentheses if they have side effects (i.e., if the function modifies data outside its scope). For example, an input-less function that writes a message to the console should be defined with empty parentheses


    EXAMPLE:
    --------------
    scala> def hi = "hi"
              hi: String
    scala> hi
        res0: String = hi

    OR

    scala> def hi(): String = "hi"
    hi: ()String
    scala> hi()
    res1: String = hi
    scala> hi r
    es2: String = hi

    Syntax: Defining a Function with a Return Type
    -------------------------------------------------------------
    def <identifier>: <type> = <expression>

    scala> def hi: String = "hi"
                 hi: String

    NAMED FUNCTIONS
    ------------------------------
    Now weíre ready to look at a full function definition.

    Syntax: Defining a Function

    def <identifier>(<identifier>: <type>[, ... ]): <type> = <expression>

     Letís try creating a function that performs an essential mathematical operation:

    scala> def multiplier(x: Int, y: Int): Int = { x * y }  //HERE multiplier is the function name
    multiplier: (x: Int, y: Int)Int
    scala> multiplier(6, 7)
    res0: Int = 42

    A common use of an early function exit is to stop further execution in the case of invalid or abnormal input values.
    For example, this ìtrimî function validates that the input value is nonnull before calling the JVM Stringís ìtrimî method:

    scala> def safeTrim(s: String): String = {  
     |   if (s == null) return null  
     |   s.trim()  
     |
    }
    safeTrim: (s: String)String

    -----------------------
    PROCEDURES
    ---------------------

    A procedure is a function that doesnít have a return value


    Here is a simple logging procedure, defined with an implicit return type and then with an explict return type:

    scala> def log(d: Double) = println(f"Got value $d%.2f")
    log: (d: Double)Unit
    scala> def log(d: Double): Unit = println(f"Got value $d%.2f")
    log: (d: Double)Unit
    scala> log(2.23535)
    Got value 2.24


    deprecated syntax you will see for procedures is to define them without the Unit return type and without an equals sign before the pro- cedure body. With this syntax the example log() method would be written like this:
    scala> def log(d: Double) { println(f"Got value $d%.2f") }
    log: (d: Double)Unit

    -------------------------------------------------------
    Function Invocation with Expression Blocks
    -------------------------------------------------------

    Function Invocation with Expression Blocks

    One example where using an expression block to invoke a function may be preferable is when you have to send a calculated value to the function. Instead of calculating the amount and storing it in local values to be passed to the function, you can do the cal- culations inside the expression block. The expression block will be evaluated before the function is called and the blockís return value will be used as the function argument. Here is an example of calculating values inside a function block used for invoking a function:

    scala> def formatEuro(amt: Double) = f"Ä$amt%.2f"
    formatEuro: (amt: Double)String
    scala> formatEuro(3.4645)
    res4: String = Ä3.46
    scala> formatEuro { val rate = 1.32; 0.235 + 0.7123 + rate * 5.32 }
    res5: String = Ä7.97

    -------------------------
    Recursive Functions
    -----------------------------
    A recursive function is one that may invoke itself, preferably with some type of parameter or external condition that will be checked to avoid an infinite loop of function invoca- tion. Recursive functions are very popular in functional programming because they offer a way to iterate over data structures or calculations without using mutable data, because each function call has its own stack for storing function parameters. Hereís an example of a recursive function that raises an integer by a given positive exponent:

    scala> def power(x: Int, n: Int): Long = {  
    |   if (n >= 1) x * power(x, n-1)  
    |   else 1  
    |
    }
          power: (x: Int, n: Int)Long

    scala> power(2, 8)
    res6: Long = 256
    scala> power(2, 1)
    res7: Long = 2
    scala> power(2, 0)
    res8: Long = 1

    One problem with using recursive functions is running into the dreaded ìStack Over- flowî error, where invoking a recursive function too many times eventually uses up all of the allocated stack space. To prevent this scenario, the Scala compiler can optimize some recursive functions with tail-recursion so that recursive calls do not use additional stack space. With tail- recursionñoptimized functions, recursive invocation doesnít create new stack space but instead uses the current functionís stack space. Only functions whose last statement is the recursive invocation can be optimized for tail-recursion by the Scala compiler. If the result of invoking itself is used for anything but the direct return value, a function canít be optimized. Fortunately there is a function annotation available to mark a function as being intended to be optimized for tail-recursion. A function annotation is special syntax carried over
    50 | Chapter 4: Functions
    from the Java programming language where the ìatî sign (@) and an annotation type is placed before a function definition to mark it for special use. A function marked with the tail-recursion function annotation will cause an error at compilation time if it cannot be optimized for tail-recursion.
    To mark a function as intended for tail-recursion, add the text @annotation.tailrec before the function definition or on the previous line. Hereís the same example again only marked with the ìtailrecî annotation, to let the Scala compiler know we expect it to be optimized for tail-recursion and that if it cannot be, the compiler should treat it as an error:

    scala> @annotation.tailrec  
    | def power(x: Int, n: Int): Long = {     |   if (n >= 1) x * power(x, n-1)     |   else 1     | }
    <console>:9: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position      
    if (n >= 1) x * power(x, n-1) Ah,
    the function couldnít be optimized because the recursive call is not the last statement in the function. This is understandable. Iíll switch the ìifî and ìelseî conditions and try again:

    scala> @annotation.tailrec  
    | def power(x: Int, n: Int): Long = {     |   if (n < 1) 1     |   else x * power(x, n-1)     | }
    <console>:11: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position      
    else x * power(x, n-1)              
    ^ Hmm, the recursive call is the last item in the function. Oh I see, weíre taking the result of the recursive call and multiplying it by a value, so that multiplication is actually the last statement in the function, not the recursive call. A good way to fix this is to move the multiplication into the beginning of the invoked function instead of multiplying its result. Now the end of the function is a simple un- touched result from the recursive call:

    scala> @annotation.tailrec     | def power(x: Int, n: Int, t: Int = 1): Int = {     |   if (n < 1) t     |   else power(x, n-1, x*t)     | }
    power: (x: Int, n: Int, t: Int)Int
    Recursive Functions | 51

    scala> power(2,8)
    res9: Int = 256 Success!

    The ìtailrecî annotation and successful compile guarantees that the function will be optimized with tail-recursion, so that each successive call will not add more stack frames. Although this example may seem challenging to get through, recursion and tail- recursion are still valuable methods for iterating without using mutable data. Youíll find that many of the data structures weíll explore later in the book will be rich with functions that are implemented with tail-recursion.
    -----------------------
    Nested Functions
    --------------------------
     Functions are named, parameterized expression blocks and expression blocks are ne- stable, so it should be no great surprise that functions are themselves nestable. There are times when you have logic that needs to be repeated inside a method, but would not benefit from being extrapolated to an external method. In these cases defining an internal function inside another function, to only be used in that function, may be worthwhile. Letís have a look at a method that takes three integers and returns the one with the highest value:
    scala> def max(a: Int, b: Int, c: Int) = {     |   def max(x: Int, y: Int) = if (x > y) x else y     |   max(a, max(b, c))     | }
    max: (a: Int, b: Int, c: Int)Int
    scala> max(42, 181, 19)
    res10: Int = 181

    The logic inside the max(Int, Int) nested function was defined once but used twice inside the outer function, making it possible to reduce duplicated logic and simplify the overall function. The nested function here has the same name as its outer function, but because their parameters are different (the nested one only takes two integers) there is no conflict between them. Scala functions are differentiated by their name and the list of their parameter types. However, even if the names and parameter types were the same there would be no confict because the local (nested) one takes precedence over the outer one

    NOTE: we must be variate the function name in inner and outer functions by applay the diffrent function name or diffrent parameterrs.

    ----------------------------------------------------
    Calling Functions with Named Parameters
    -----------------------------------------------------

    Syntax: Specifying a Parameter by Name

    <function name>(<parameter> = <value>)

    In this example, a simple two-parameter function is invoked twice, first using the con- vention of specifying parameters by their order and then by assigning values by parameter name:
    scala> def greet(prefix: String, name: String) = s"$prefix $name"
    greet: (prefix: String, name: String)String
    scala> val greeting1 = greet("Ms", "Brown")
    greeting1: String = Ms Brown
    scala> val greeting2 = greet(name = "Brown", prefix = "Mr")
    greeting2: String = Mr Brown
    ------------------------------------------------------------------------
    Parameters with Default Values  or   Function Overloading
    ------------------------------------------------------------------------

    A common problem when defining functions is deciding which input parameters they should take to maximize reuse. In Scala, Java, and other languages, a common solution is to provide multiple versions of the same function with the same name but different lists of input parameters. This practice is known as function overloading due to the functionís name being reused for different inputs. The common practice is to copy a function with x number of parameters to a new function with xñ1 parameters that invokes the original function using a default value for the missing parameter.

    Syntax: Specifying a Default Value for a Function Parameter
    def <identifier>(<identifier>: <type> = <value>): <type>


    scala> def greet(prefix: String = "", name: String) = s"$prefix$name"
    greet: (prefix: String, name: String)String

    scala> val greeting1 = greet(name = "Paul")
    greeting1: String = Paul

    ala> def greet(name: String, prefix: String = "") = s"$prefix$name"
    greet: (name: String, prefix: String)String
    scala> val greeting2 = greet("Ola")
    greeting2: String = Ola
    -------------------------
    Vararg Parameters
    --------------------------
    scala also supports vararg parameters, so you can define a function with a variable number of input arguments. The vararg parameter cannot be followed by a nonvararg parameter because there would be no way to distinguish them. Inside the function, the vararg parameter, implemented as a collection (which weíll study in Chapter 6), can be used as an iterator in for loops. To mark a function parameter as matching one or more input arguments, add an asterisk symbol (*) after the parameterís type in the function definition. Here is an example of using a vararg parameter to create a summing function that returns a sum of all of its input integers:

    scala> def sum(items: Int*): Int = {     |   var total = 0     |   for (i <- items) total += i     |   total     | }
    sum: (items: Int*)Int
    scala> sum(10, 20, 30)
    res11: Int = 60
    scala> sum()
    res12: Int = 0
    -----------------------------------------------------------------------------------
    Parameter Groups  OR Curried Functions
    ------------------------------------------------------------------------------------
     So far we have looked at parameterized function definitions as a list of parameters surrounded by parentheses. Scala provides the option to break these into groups of parameters, each separated with their own parentheses. Here is an example of the ìmaxî function where the two input parameters have been split into their own parameter groups:

    scala> def max(x: Int)(y: Int) = if (x > y) x else y
    max: (x: Int)(y: Int)Int
    scala> val larger = max(20)(39)
    larger: Int = 39

    *******************************************************************************

    -----------------------------------------------------------------------------------------------------------------------
    FIRST CLASS FUNCTION
    -----------------------------------------------------------------------------------------------------------------------

    ---> Functions that accept other functions as parameters and/or use functions as return values are known as higher-order functions. You may have heard of two of the most famous higher-order functions, map() and reduce().

    -->Scala has full support for first-class functions, higher-order functions, and the use of declarative programming. As with other types of data such as String or Int, functions have types based on the types of their input arguments and their return value. A function can be stored in a value or variable, passed to a function, returned from a function, and used with data structures to support map(), reduce(), fold(), and filter(), among many other higher-order functions.

    --------------------------------------------------------------------------------------------------------------
    Function Types and Values
    ---------------------------------------------------------------------------------------------------------------

    The type of a function is a simple grouping of its input types and return value type, arranged with an arrow indicating the direction from input types to output type.

    Syntax: A Function Type

    ([<type>, ...]) => <type>

    For example, the function def double(x: Int): Int = x * 2 has the function type Int => Int, indicating that it has a single Int parameter and returns an Int. The function name, ìdouble,î is an identifier and isnít part of the type. The body of the function, a simple multiplication of the input by 2, does not affect the type of the func- tion. The rest of the information is the input types and return type, and so these make up the function type itself.
    Letís try using a function type in the REPL, by creating a function and then assigning it to a function value:

    scala> def double(x: Int): Int = x * 2
      double: (x: Int)Int

    scala> double(5)
    res0: Int = 10

    scala> val myDouble: (Int) => Int = double
                 myDouble: Int => Int = <function1>

    scala> myDouble(5)
                res1: Int = 10

    Syntax: Assigning a Function with the Wildcard Operator
    ----------------------------------------------------------------------
    val <identifier> = <function name> _

    Letís try this out with the ìmyDoubleî function value:

    scala> def double(x: Int): Int = x * 2
    double: (x: Int)Int

    scala> val myDouble = double _
    myDouble: Int => Int = <function1>

    scala> val amount = myDouble(20)
    amount: Int = 40


    -->This time, the explicit function type for myDouble wasnít required to distinguish it from a function invocation. The underscore (_) served as a placeholder for a future invocation of the function, returning a function value that we could store in myDouble

    Hereís an example of a function value defined with an explicit function type using mul- tiple parameters, enclosed with parentheses:

    scala> def max(a: Int, b: Int) = if (a > b) a else b
    max: (a: Int, b: Int)Int
    scala> val maximize: (Int, Int) => Int = max maximize: (Int, Int) => Int = <function2>

    scala> maximize(50, 30)
    res3: Int = 50

    HIGHORDER FUNCTION SAMPLE EXAMPLE
    ---------------------------------------------------------------

    scala> def logStart() = "=" * 50 + "\nStarting NOW\n" + "=" * 50
    logStart: ()String

    scala> val start: () => String = logStart
    start: () => String = <function0>
    scala> println( start() ) ==================================================
    Starting NOW
    ==================================================
    ---->This was a gentle introduction to how functions may be treated as data, by storing them in values and assigning them static types
    --->the next few sections on higher-order functions and function literals are going to build on this knowledge and cover some challenging new syntax


    ------------------------------------------------------------
    Higher-Order Functions
    ---------------------------------------------------------------

    DESCRIPTION: We have already defined values that have a function type. A higher-order function is a function that has a value with a function type as an input parameter or return value.


    EXAMPLE:
    ---------------
    scala> def safeStringOp(s: String, f: String => String) = {  
    |   if (s != null) f(s) else s  
    |
    }
    safeStringOp: (s: String, f: String => String)String
    scala> def reverser(s: String) = s.reverse
    reverser: (s: String)String
    scala> safeStringOp(null, reverser)
    res4: String = null
    scala> safeStringOp("Ready", reverser)
    res5: String = ydaeR

    --->The call with ìnullî safely returned the same value back, whereas the call with a valid String returned the reverse of the input value

    ---------------------------------------
    Function Literals
    ---------------------------------------

    Now weíll tackle a difficult concept covered by a variety of names by starting with an easy example. In this example we will create a function literal, a working function that lacks a name, and assign it to a new function value:
    scala> val doubler = (x: Int) => x * 2 doubler: Int => Int = <function1>

    scala> val doubled = doubler(22)
    doubled: Int = 44
    The function literal in this example is the syntax (x: Int) => x * 2, which defines a typed input argument (x) and the function body (x * 2). Function literals can be stored in function values and variables, or defined as part of a higher-order function invocation. You can express a function literal in any place that accepts a function type.

    Syntax: Writing a Function Literal
    -------------------------------------------
    ([<identifier>: <type>, ... ]) => <expression>

    Letís define a function value and assign it a new function literal:
    scala> val greeter = (name: String) => s"Hello, $name"
    greeter: String => String = <function1>
    scala> val hi = greeter("World")
    hi: String = Hello, World
     If you think about it, a function literal is essentially a parameterized expression. We know about expressions that return a value, but now have a way to parameterize their input.

    -------------------------------------------
    Placeholder Syntax
    ------------------------------------------
    Placeholder syntax is a shortened form of function literals, replacing named parameters with wildcard operators (_). It can be used when
     (a) the explicit type of the function is specified outside the literal and
      (b) the parameters are used no more than once

    Here is an example of a doubling function literal using wildcard operators in place of named parameters:
    scala> val doubler: Int => Int = _ * 2
    doubler: Int => Int = <function1>

    --->Placeholder syntax is valid here because the input parameter is only used once and the literalís type has an external explicit definition (in the value).

    Letís demonstrate how this ordering of placeholders works by trying an example with two placeholders:
    scala> def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y)
    combination: (x: Int, y: Int, f: (Int, Int) => Int)Int
    scala> combination(23, 12, _ * _)
    res13: Int = 276

    Letís kick the number of placeholders up for the last time, from two to three. Is this example still easily readable?
    scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
    tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int

    scala> tripleOp(23, 92, 14, _ * _ + _)
    res14: Int = 2130
    --->The tripleOp function takes four parameters: three Int values and a function that can reduce them down to a single Int. The actual function body is far shorter than the parameter list, and applies the function to the input values.
    --->This example function, tripleOp, is limited to integer values. However, wouldnít it be more useful if it was generic and supported type parameters?

     one for the common input type and one for the single return value type. This will give us some flexibility to call the tripleOp function with any type of inputs or anonymous functions that

    scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
    tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B

    scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
    res15: Int = 2130
    scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
    res16: Double = 0.017857142857142856
    scala> tripleOp[Int,Boolean](93, 92, 14, _ > _ + _)
    res17: Boolean = false


    ***USEAGE***
    ---------------------
    Placeholder syntax is especially helpful when working with data structures and collec- tions. Many of the core sorting, filtering, and other data structure methods tend to use first-class functions, and placeholder syntax reduces the amount of extra code required to call these methods.


    ---------------------------------------------------------------------------------------------------------------------
    Partially Applied Functions and Currying
    ----------------------------------------------------------------------------------------------------------------------

     What if you wanted to reuse a function invocation and retain some of the parameters to avoid typing them in again?

    A cleaner way to partially apply functions is to use functions with multiple parameter lists. Instead of breaking up a parameter list into applied and unapplied parameters, apply the parameters for one list while leaving another list unapplied. This is a technique known as currying the function

    To demonstrate this answer, Iíll use the example of a two-parameter function that checks if a given number is a factor of the other number:
    scala> def factorOf(x: Int, y: Int) = y % x == 0
    factorOf: (x: Int, y: Int)
    Boolean If you want a shortcut to the function without retaining any parameters, you can use the wildcard operator (_) assignment we covered in this chapterís introduction:

    scala> val f = factorOf _
    f: (Int, Int) => Boolean = <function2>

    scala> val x = f(7, 20)
    x: Boolean = false
    If you want to retain some of the parameters, you can partially apply the function by using the wildcard operator to take the place of one of the parameters. The wildcard operator here requires an explicit type, because it is used to generate a function value with a declared input type:

    scala> val multipleOf3 = factorOf(3, _: Int)
    multipleOf3: Int => Boolean = <function1>
    scala> val y = multipleOf3(78)
    y: Boolean = true
    The new function value, multipleOf3, is a partially applied function, because it contains some but not all of the parameters for the factorOf() function

    -------------------------------------------------------------------------------------
    Invoking Higher-Order Functions with Function Literal Blocks
    -------------------------------------------------------------------------------------

    a higher-order function can wrap a given expression block in a single database session or transaction
    Iíll use the ìsafeStringOpsî function to demonstrate when this syntax may be desirable and how to use it. To start, here is the ìsafeStringOpsî function used with a regular function literal, before converting it to the desired syntax:
    scala> def safeStringOp(s: String, f: String => String) = {     |   if (s != null) f(s) else s     | } safeStringOp: (s: String, f: String => String)String
    scala> val uuid = java.util.UUID.randomUUID.toString      
               uuid: String = bfe1ddda-92f6-4c7a-8bfc-f946bdac7bc9
    scala> val timedUUID = safeStringOp(uuid, { s =>     |   val now = System.currentTimeMillis                                  |   val timed = s.take(24) + now       |   timed.toUpperCase     | })
    timedUUID: String = BFE1DDDA-92F6-4C7A-8BFC-1394546043987
     A UUID utility in Javaís java.util package, accessible (as are all JDK classes) from Scala. System.currentTimeMillis provides the epoch time (elapsed time since January 1, 1970 GMT) in milliseconds, useful for creating timestamps. The take(x) method returns the first x items from the String, in this case the first four sections of the UUID


    *******************************************************************************

    ---------------------------------------------------------------------------------------------------------------------
    Common Collections
    ----------------------------------------------------------------------------------------------------------------------

    A collections framework provides data structures for collecting one or more values of a given type such as arrays, lists, maps, sets, and trees. Most of the popular programming languages have their own collections framework (or, at the least, lists and maps) because these data structures are the building blocks of modern software projects.

    ---> Because Scala is a JVM language, you can access and use the entire Java collections library from your Scala code.

    --> which make switching between immutable data (for stability) and mutable data (when necessary) convenient.

    ---.The root of all iterable collections, Iterable, provides a common set of methods for (you guessed it) iterating through and manipulating collection data

    -------------------------------------------------
    Lists, Sets, and Maps
    --------------------------------------------------

    Letís start with the List type, an immutable singly linked list. You can create a list by invoking it as a function, passing in its contents in the form of comma-separated pa- rameters:
    scala> val numbers = List(32, 95, 24, 21, 17)
    numbers: List[Int] = List(32, 95, 24, 21, 17)

    scala> val colors = List("red", "green", "blue")
    colors: List[String] = List(red, green, blue)
    scala> println(s"I have ${colors.size} colors: $colors")
    I have 3 colors: List(red, green, blue)

    --->The size method, available on all collections and String instances, returns the number of items in the collection. Defined without parentheses  the size method is simply invoked by name

    Use the Lisp-style head() and tail() methods to access the first and remaining ele- ments of a list, respectively. To access a single element directly, invoke the list as a func- tion and pass it the zero-based index of that element:
    scala> val colors = List("red", "green", "blue")
    colors: List[String] = List(red, green, blue)
    scala> colors.head res0: String = red

    scala> colors.tail
    res1: List[String] = List(green, blue)

    scala> colors(1)
    res2: String = green

    scala> colors(2)
    res3: String = blue

    ------------------------------------------
    USING RANGE COLLECTION
    ------------------------------------------

    Letís try out using for-loops to iterate over the ìnumbersî and ìcolorsî lists:
    scala> val numbers = List(32, 95, 24, 21, 17)
    numbers: List[Int] = List(32, 95, 24, 21, 17)
    scala> var total = 0; for (i <- numbers) { total += i }
    total: Int = 189
    scala> val colors = List("red", "green", "blue")
    colors: List[String] = List(red, green, blue)
    scala> for (c <- colors) { println(c) }

    red
    green
    blue



    Hereís an example of the foreach(), map(), and reduce() higher-order functions avail- able in List and other collections. Respectively, these functions iterate over the list, convert the list, and reduce the list down to a single item. For each method, a function literal is passed in, including the input parameter in parentheses and its function body:

    scala> val colors = List("red", "green", "blue")
      colors: List[String] = List(red, green, blue)

    scala> colors.foreach( (c: String) => println(c) )
    red
    green
    blue
    scala> val sizes = colors.map( (c: String) => c.size )
    sizes: List[Int] = List(3, 5, 4)
    scala> val numbers = List(32, 95, 24, 21, 17)
    numbers: List[Int] = List(32, 95, 24, 21, 17)
    scala> val total = numbers.reduce( (a: Int, b: Int) => a + b )
    total: Int = 189
    foreach() takes a function (a procedure, to be accurate) and invokes it with every item in the list. map() takes a function that converts a single list element to another value and/or type. reduce() takes a function that combines two list elements into a single element

    --------------------------------------
    MAP
    --------------------------------------

    A Map is an immutable key-value store, also known as a hashmap, dictionary, or asso- ciative array in other languages. Values stored in a Map with a given unique key may beretrieved using that key. The key and the value are type-parameterized; you can just as easily create a mapping from strings to integers as a mapping from integers to strings.


    When creating a Map, specify the key-value pairs as tuples (see ìTuplesî on page 25). You can use the relation operator (->) to specify the key and value tuple.

    EXAMPLE
    ----------------
    scala> val colorMap = Map("red" -> 0xFF0000, "green" -> 0xFF00,  "blue" -> 0xFF) colorMap: scala.collection.immutable.Map[String,Int] =  Map(red -> 16711680, green -> 65280, blue -> 255)
    scala> val redRGB = colorMap("red")
    redRGB: Int = 16711680
    scala> val cyanRGB = colorMap("green") | colorMap("blue")
    cyanRGB: Int = 65535
    scala> val hasWhite = colorMap.contains("white") hasWhite: Boolean = false

    scala> for (pairs <- colorMap) { println(pairs) }
    (red,16711680)
    (green,65280)

    For example, you can create a collection of collections:
    scala> val oddsAndEvents = List(List(1, 3, 5), List(2, 4, 6))
    oddsAndEvents: List[List[Int]] = List(List(1, 3, 5), List(2, 4, 6))
     Or you can have a collection of 2-sized tuples, and create a List that looks similar to a Map:

    scala> val keyValues = List(('A', 65), ('B',66), ('C',67))
    keyValues: List[(Char, Int)] = List((A,65), (B,66), (C,67)) (blue,255)

    You can access a single element from a list by invoking it as a function with a (zero- based) index number. Here is an example of accessing the first and fourth elements of a List by their index:
    scala> val primes = List(2, 3, 5, 7, 11, 13)
    primes: List[Int] = List(2, 3, 5, 7, 11, 13)
    scala> val first = primes(0)
    first: Int = 2
    scala> val fourth = primes(3)
    fourth: Int = 7
    You can decompose a list into its head, the first item in the list, and its tail, the remaining items in the list:
    scala> val first = primes.head
    first: Int = 2
    scala> val remaining = primes.tail
    remaining: List[Int] = List(3, 5, 7, 11, 13)

    Creating a new, empty list will actually return Nil instead of a fresh instance. Because Nil is immutable, there is essentially no difference between it and a fresh, empty list instance. Likewise, creating a new list that has a single entry just creates a single list element that points to Nil as its tail.
    Letís demonstrate these points with some examples:
    scala> val l: List[Int] = List()
    l: List[Int] = List()
    scala> l == Nil
    res0: Boolean = true
    scala> val m: List[String] = List("a")
    m: List[String] = List(a)
    scala> m.head res1: String = a
    scala> m.tail == Nil res2: Boolean = true
    I have used lists of two explicit types, Int and String, to demonstrate that regardless of the type of their data, a List will always end with Nil.


    --------------------------------------
    The Cons Operator  (::)
    ---------------------------------------

    There is an alternate way to construct lists that takes advantage of this relationship with Nil. As another nod to Lisp, Scala supports use of the cons (short for construct) operator to build lists. Using Nil as a foundation and the right-associative cons operator :: for binding elements, you can build a list without using the traditional List(Ö) format.

    Here is an example of building a list with the cons operator:
    scala> val numbers = 1 :: 2 :: 3 :: Nil numbers: List[Int] = List(1, 2, 3)

     You could use traditional dot notation with the cons operator, but it would look a bit odd. OK, letís try it out anyway:
    scala> val first = Nil.::(1) first: List[Int] = List(1)
    scala> first.tail == Nil res3: Boolean = true


     This time we will use it to prepend (insert at the front) a value to an existing list, thus creating a brand new list. We have actually tried this before, because it is the same as the operation of prepending a value to Nil, the empty list:

    scala> val second = 2 :: first second: List[Int] = List(2, 1)

    scala> second.tail == first
    res4: Boolean = true
     Although the ìsecondî list includes the ìfirstî list, both are valid lists that can be used independently. This example of building one list by adding a value to another demon- strates the recursive and reusable nature of Scalaís immutable lists, and provides a good summary of this section


    ------------------------------------------------
    List Arithmetic
    -------------------------------------------------
     In this section we will focus on basic arithmetic oper- ations on lists. By ìarithmetic,î a term I use loosely, I mean operations that add, remove, split, combine, and otherwise modify the organization of lists without changing the list elements (i.e., their contents) themselves. And of course by ìmodifyî I mean ìreturn a new list with the requested changesî because List is an immutable collection

    Table 6-1. Arithmetic operations on lists
    ---------------------------------------------------

    Name Example  Description
    ---------------------------------------------------------------------------------------------------------------

    :: 1 :: 2 :: Nil     Appends individual elements to this list. A right- associative operator.


    :::    List(1, 2) ::: List(2, 3) Prepends another list to this one. A right- associative operator.

    ++ List(1, 2) ++ Set(3, 4, 3)        Appends another collection to this list.


    == List(1, 2) == List(1, 2)      Returns true if the collection types and contents are equal.

    distinct  List(3, 5, 4, 3, 4).distinct      Returns a version of the list without duplicate elements.


    drop List('a', 'b', 'c', 'd') drop 2 Subtracts the first n elements from the list.


    filter List(23, 8, 14, 21) filter (_ > 18)     Returns elements from the list that pass a true/ false function.

    flatten List(List(1, 2), List(3, 4)).flatten     Converts a list of lists into a single list of elements.

    partition List(1, 2, 3, 4, 5) partition (_ < 3)    Groups elements into a tuple of two lists based on the result of a true/false function


    reverse List(1, 2, 3).reverse Reverses the list.

    slice        List(2, 3, 5, 7) slice (1, 3)          Returns a segment of the list from the first index up to but not including the second index

    sortBy List("apple", "to") sortBy (_.size) Orders the list by the value returned from he given function.

    sorted List("apple", "to").sorted Orders a list of core Scala types by their natural value

    splitAt List(2, 3, 5, 7) splitAt 2 Groups elements into a tuple of two lists based on if they fall before or after the given index

    take List(2, 3, 5, 7, 11, 13) take 3 Extracts the first n elements from the list.

    zip List(1, 2) zip List("a", "b") Combines two lists into a list of tuples of elements at each index.

    -----------------------------------------------------------------------------------------------------------------------

    NOTE: some examples used operator notation (e.g., list drop 2) whereas others used dot notation (e.g., list.flatten). Selecting the right notation is a personal choice, except where dot notation is required due to lack of an operation parameter (as in list.flatten).


    Collection methods that are also higher-order functions, such as filter, map, and partition, are excellent candidates for using placeholder syntax. The function param- eter they take as input acts on a single element in their list. Thus the underscore (_) in an anonymous function sent to one of these methods represents each item in the list

    An important point to make about these arithmetic methods is that ::, drop, and take act on the front of the list and thus do not have performance penalties. Recall that List is a linked list, so adding items to or removing items from its front does not require a full traversal. A list traversal is a trivial operation for short lists, but when you start getting into lists of thousands or millions of items, an operation that requires a list traversal can be a big deal

    The corollary operations to ::, drop, and take are +: (a left-associative operator), dropRight, and takeRight. The arguments to these operators are the same as to their corollary operations. Here are examples of these list-appending operations:

    scala> val appended = List(1, 2, 3, 4) :+ 5
    appended: List[Int] = List(1, 2, 3, 4, 5)
    scala> val suffix = appended takeRight 3
    suffix: List[Int] = List(3, 4, 5)
    scala> val middle = suffix dropRight 2
    middle: List[Int] = List(3)


    -----------------------------------
    MAP
    ------------------------------------
    Map methods are those that take a function and apply it to every member of a list, collecting the results into a new list. In set theory, and the field of mathematics in general, to map is to create an assocation between each element in one set to each element in another set. In a sense, both definitions describe what the map methods in List are doing: mapping each item from one list to another list, so that the other list has the same size as the first but with different data or element types.

     List mapping operations
    -------------------------------

    Name Example  Description
    -------------------------------------------------------------------------------------------------------------------
    collect List(0, 1, 0) collect {case 1 => "ok"} Transforms each element using a partial function, retaining applicable elements
    flatMap List("milk,tea") flatMap (_.split(','))      Transforms each element using the given function and ìflattensî the list of results into this list.

    map List("milk","tea") map (_.toUpperCase)    Transforms each element using the given function
    ---------------------------------------------------------------------------------------------------------------------

    Letís see how these list-mapping operators work in the REPL:
    scala> List(0, 1, 0) collect {case 1 => "ok"}
    res0: List[String] = List(ok)
    scala> List("milk,tea") flatMap (_.split(','))
    res1: List[String] = List(milk, tea)
    scala> List("milk","tea") map (_.toUpperCase)
    res2: List[String] = List(MILK, TEA)
    The flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list. Fortunately, Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level, a list of string arrays can be safely flattened into a list of strings.



    ------------------------------------------------------------------
    reducing operations
    ------------------------------------------------------------------

    Scalaís collections support mathematical reduction operations (e.g., finding the sum of a list) and Boolean reduction operations (e.g., determining if a list contains a given element). They also support generic higher-order operations known as folds that you can use to create any other type of list reduction algorithm.

     Math reduction operations
    ----------------------------------
    Name Example Description
    -------------------------------------------------------------------------------------------------------------
    max List(41, 59, 26).max Finds the maximum value in the list.

    min List(10.9, 32.5, 4.23, 5.67).min Finds the minimum value in the list.

    product         List(5, 6, 7).product Multiplies the numbers in the list.

    sum         List(11.3, 23.5, 7.2).sum Sums up the numbers in the list.

     Boolean reduction operations
    ---------------------------------------
    contains         List(34, 29, 18) contains 29 Checks if the list contains this element

    endsWith         List(0, 4, 3) endsWith List(4, 3) Checks if the list ends with a given list.

    exists         List(24, 17, 32) exists (_ < 18) Checks if a predicate holds true for at least one element in the list.

    forall       List(24, 17, 32) forall (_ < 18) Checks if a predicate holds true for every element in the list.

    startsWith           List(0, 4, 3) startsWith List(0)      Tests whether the list starts with a given list.


    -----------------------------------------------------
    converting collections
    -----------------------------------------------------

     Because a List.toList() operation would be silly (but possible), the examples demonstrate converting from one type to a completely different type.
    -------------------------------------------------
     Operations to convert collections
    ----------------------------------------------
    Name Example Description
    --------------------------------------------------------------------------------------------------------------------
    mkString List(24, 99, 104).mkString(", ")       Renders a collection to a Set using the given delimiters

    toBuffer  List('f', 't').toBuffer Converts an immutable collection to a mutable one.

    toList Map("a" -> 1, "b" -> 2).toList Converts a collection to a List.

    toMap Set(1 -> true, 3 -> true).toMap Converts a collection of 2-arity (length) tuples to a Map.

    toSet List(2, 5, 5, 3, 2).toSet Converts a collection to a Set.

    toString List(2, 5, 5, 3, 2).toString Renders a collection to a String, including the collectionís type.


    -----------------------------------------------------------
    pattern matching with collection
    -------------------------------------------------------------

    scala> val statuses = List(500, 404) s
    tatuses: List[Int] = List(500, 404)
    scala> val msg = statuses.head match {  
    |   case x if x < 500 => "okay"  
    |   case _ => "whoah, an error"  
    |
    }
     msg: String = whoah, an error With a pattern guard (see ìMatching with Pattern Guardsî on page 36), you could also match a single value inside a collection:

    scala> val msg = statuses match {     |   case x if x contains(500) => "has error"     |   case _ => "okay"     | }
    msg: String = has error

    scala> val msg = statuses match {  case List(404, 500) => "not found & error"     |   case List(500, 404) => "error & not found"     |   case List(200, 200) => "okay"     |   case _ => "not sure what happened"     | }
    msg: String = error & not found


    *******************************************************************************

    MORE COLLECTIONS
    **************************************

    Weíll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then weíll move on to arrays, streams, and other collections


    ---------------------------------------
    Mutable Collections
    ---------------------------------------

    The List, Set, and Map immutable collections we are familiar with cannot be changed after they have been created (see the definition of ìimmutableî). They can, however, be transformed into new collections. For example, we can create an immutable map, and then transform it by removing one mapping and adding another:
    scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
    m: scala.collection.immutable.Map[String,Int] =  Map(AAPL -> 597, MSFT -> 40)
    scala> val n = m - "AAPL" + ("GOOG" -> 521)
         n: scala.collection.immutable.Map[String,Int] =  Map(MSFT -> 40, GOOG -> 521)
    scala> println(m)                
              Map(AAPL -> 597, MSFT -> 40) A new map with ìAAPLî and ìMSFTî keys. Removing ìAPPLî and adding ìGOOGî gives us a different collectionÖ Ö
    while the original collection in ìmî remains the same.
    107
    What you end up with is a completely new collection stored in ìnî. The original collec- tion, stored in the ìmî value, remains untouched. And this is exactly the point of im- mutable data, namely that data and data structures should not be mutable or change their state in order to improve code stability and prevent bugs. As an example, data structures that are rigid and never change state are safer to use with concurrent code than data structures that may change at any point and are prone to corruption (e.g., reading a data structure while it is undergoing a state change).

    ---------------------------------------------
    Creating New Mutable Collections
    ---------------------------------------------
    The most straightforward way to modify collections is with a mutable collection type. See Table 7-1 for the mutable counterparts(meening PRATIRUPALU) to the standard immutable List, Map, and Set types.
    ---------------------------------------------------------------------------------------------------
    Table 7-1. Mutable collection types
    ---------------------------------------------------------------------------------------------------
    Immutable type Mutable counterpart
    --------------------        --------------------------------------------
    collection.immutable.List collection.mutable.Buffer
    collection.immutable.Set collection.mutable.Set
    collection.immutable.Map collection.mutable.Map

    ----------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------

    Whereas the collection.immutable package is automatically added to the current namespace in Scala, the collection.mutable package is not. When creating mutable collections, make sure to include the full package name for the type.
    The collection.mutable.Buffer type is a general-purpose mutable sequence, and supports adding elements to its beginning, middle, and end.


    Example
    ----------
    scala> val nums = collection.mutable.Buffer(1)
    nums: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)
    scala> for (i <- 2 to 10) nums += i
    scala> println(nums)
    Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    You can convert your mutable buffer back to an immutable list at any time with the toList method:
    scala> println(nums)
    Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    scala> val l = nums.toList l:
    List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    Likewise for sets and maps, use the toSet and toMap methods to convert these mutable collections to their immutable counterparts.


    Here is an example of converting an immutable map to a mutable one and then changing it back:

    scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
    m: scala.collection.immutable.Map[String,Int] =  Map(AAPL -> 597, MSFT -> 40)
    scala> val b = m.toBuffer                            
    b: scala.collection.mutable.Buffer[(String, Int)] =  ArrayBuffer((AAPL,597), (MSFT,40))
    scala> b trimStart 1                                  
    scala> b += ("GOOG" -> 521)
     ArrayBuffer((MSFT,40), (GOOG,521))
    scala> val n = b.toMap                            
       n: scala.collection.immutable.Map[String,Int] =  Map(MSFT -> 40, GOOG -> 521)
    The map, containing key-value pairs, is now a sequence of tuples. trimStart removes one or more items from the start of the buffer. After removing the ìAAPLî entry weíll add a ìGOOGî entry. This buffer of tuples is now an immutable map again

    ---------------------------------
    Using Collection Builders
    ----------------------------------


    A Builder is a simplified form of a Buffer, restricted to generating its assigned collec- tion type and supporting only append operations.
    To create a builder for a specific collection type, invoke the typeís newBuilder method and include the type of the collectionís element. Invoke the builderís result method to convert it back into the final Set. Here is a
    scala> val b = Set.newBuilder[Char]
    b: scala.collection.mutable.Builder[Char,scala.collection.immutable.  Set[Char]] = scala.collection.mutable.SetBuilder@726dcf2c
    scala> b += 'h'    
    res3: b.type = scala.collection.mutable.SetBuilder@d13d812
    scala> b ++= List('e', 'l', 'l', 'o')
    res4: b.type = scala.collection.mutable.SetBuilder@d13d812
    scala> val helloSet = b.result  
    helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o) Adding a single item, one of two append operations. Adding multiple items, the second of two append operations. Unlike with buffers, a builder knows its immutable counterpart.

    So, why use Builder versus Buffer or one of the mutable collection types? The Build er type is a good choice if you are only building a mutable collection iteratively in order to convert it to an immutable collection. If you need Iterable operations while building your mutable collection, or donít plan on converting to an immutable collection, using one of the Buffer or other mutable collection types is a better match.

    ------------------------------
    ARRAY
    ------------------------------
    An Array is a fixed-size, mutable, indexed collection. Itís not officially a collection, be- cause it isnít in the ìscala.collectionsî package and doesnít extend from the root Itera ble type (although it has all of the Iterable operations like map and filter). The Array type is actually just a wrapper around Javaís array type with an advanced feature called an implicit class allowing it to be used like a sequence. Scala provides the Array type for compatibility with JVM libraries and Java code and as a backing store for indexed col- lections, which really require an array to be useful. Here are some examples of working with arrays, demonstrating their cell mutability and support for Iterable operations:
    scala> val colors = Array("red", "green", "blue") colors: Array[String] = Array(red, green, blue)
    scala> colors(0) = "purple"
    scala> colors res0: Array[String] = Array(purple, green, blue)
    scala> println("very purple: " + colors) very purple: [Ljava.lang.String;@70cf32e3
    scala> val files = new java.io.File(".").listFiles files: Array[java.io.File] = Array(./Build.scala, ./Dependencies.scala,  ./build.properties, ./JunitXmlSupport.scala, ./Repositories.scala,  ./plugins.sbt, ./project, ./SBTInitialization.scala, ./target)
    scala> val scala = files map (_.getName) filter(_ endsWith "scala") scala: Array[String] = Array(Build.scala, Dependencies.scala,  JunitXmlSupport.scala, Repositories.scala, SBTInitialization.scala) Use a zero-based index to replace any item in an Array. The Scala REPL knows how to print an Array Ö Ö but not println(), which can only call a typeís toString() method. The listFiles method in java.io.File, a JDK class, returns an array that we can easily map and filter


    ----------------------------------
    Seq and Sequences
    ----------------------------------
    Seq is the root type of all sequences, including linked lists like List and indexed (direct- access) lists like Vector. The Array type, if it were a collection, could be considered an indexed sequence because its elements are directly accessible without traversal. As a root type, Seq itself cannot be instantiated, but you can invoke it as a shortcut for creating a List:
    scala> val inks = Seq('C','M','Y','K') inks: Seq[Char] = List(C, M, Y, K)


    ----> you can access items in a Vector directly by their index

    ---->The Seq shortcut for List linked lists and the IndexedSeq shortcut for Vector indexed lists are only marginally useful, because the savings for writing them is one character and negative four characters, respectively


    Hereís an example of using String as a subtype of Iterable and as a java.lang.String wrapper, using methods from both types:
    scala> val hi = "Hello, " ++ "worldly" take 12 replaceAll ("w","W") hi: String = Hello, World

    ---------------------
    Streams
    ---------------------

    The Stream type is a lazy collection, generated from one or more starting elements and a recursive function. Elements are added to the collection only when they are accessed for the first time, in constrast to other immutable collections that receive 100% of their contents at instantiation time. The elements that a stream generates are cached for later retrieval, ensuring that each element is only generated once. Streams can be unbounded, theoretically infinite collections where elements are only realized upon access. They can also be terminated with Stream.Empty, a counterpart to List.Nil

    Here is an example function that builds and recursively generates a new stream. By incrementing the starting integer value, it will end up creating a collection of consecu- tively increasing integers:
    scala> def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1)) inc: (i: Int)Stream[Int]
    scala> val s = inc(1) s: Stream[Int] = Stream(1, ?)


    ------------------------------------
    Try Collections
    ----------------------------------
    The util.Try collection turns error handling into collection management. It provides a mechanism to catch errors that occur in a given function parameter, returning either the error or the result of the function if successful

    To throw an exception, use the throw keyword with a new Exception instance. The text message provided to Exception is optional:
    scala> throw new Exception("No DB connection, exiting...") java.lang.Exception: No DB connection, exiting...  ... 32 elided

    Scala does support try {} .. catch {} blocks, where the catch block contains a series of case statements that attempt to match the thrown error. I recommend using util.Try() exclusively because it offers a safer, more expressive, and fully monadic approach to handling er- rors.

    *******************************************************************************


     *****************************************************************************
    PART II
     
         Object-Oriented Scala

    *****************************************************************************

    --------------------------------------------------------------------------------------------------------------------
    Classes
    ---------------------------------------------------------------------------------------------------------------------


    In Part 1 of this book you learned about Scalaís core types and how to group them into collections. Now it is time to build your own types with classes. Classes are the core building block of object-oriented languages, a combination of data structures with functions (ìmethodsî). A class defined with values and variables can be instantiated as many times as needed, each one initialized with its own input data. With inheritance classes can extend other classes, creating a hierarchy of subclasses and su- perclasses. Polymorphism makes it possible for these subclasses to stand in for their parent classes, while encapsulation provides privacy controls to manage the outward appearance of a class


    Weíll start by defining the simplest possible class and instantiating it:

    scala> class User
    defined class User
    scala> val u = new User
    u: User = User@7a8c8dcf
    scala> val isAnyRef = u.isInstanceOf[AnyRef]
    isAnyRef: Boolean = true



    Letís redesign our User class and make it more useful. Weíll add a value and some methods that operate on the value. Weíll also override the default toString method and provide a more informative version:
    scala> class User {
     |   val name: String = "Yubaba"
     |   def greet: String = s"Hello from $name"
      |   override def toString = s"User($name)"
     | }

    defined class User

    scala> val u = new User
    u: User = User(Yubaba)
    scala> println( u.greet )
    Hello from Yubaba


     In Scala, class parameters (if any) are specified after the name of the class, much like a functionís parameters follow the name of the function in its definition:
    scala> class User(n: String) {  
    |   val name: String = n  
    |   def greet: String = s"Hello from $name"  
    |   override def toString = s"User($name)"  
    | }

    defined class User

    scala> val u = new User("Zeniba")
    u: User = User(Zeniba)
    scala> println(u.greet)
    Hello from Zeniba

    Class parameters are available for initializ- ing fields (values and variables in a class) or for passing to functions, but once the class has been created the parameters arenít available. Instead of using a class parameter for intitialization purposes, we can instead declare one of the fields as a class parameter. By adding the keywords val or var before a class parameter, the class parameter then becomes a field in the class. Letís try this by moving the ìnameî field to the class parameters:

    scala> class User(val name: String) {
        |   def greet: String = s"Hello from $name"  
         |   override def toString = s"User($name)"  
         | }

    defined class User
    Now that we have a short and useful class, letís put it to use. Hereís an example of using this new class with lists:

    scala> val users = List(new User("Shoto"), new User("Art3mis"),  new User("Aesch"))
    users: List[User] = List(User(Shoto), User(Art3mis), User(Aesch))
    scala> val sizes = users map (_.name.size)                      
    sizes: List[Int] = List(8, 7, 5)
    scala> val sorted = users sortBy (_.name)
    sorted: List[User] = List(User(Aesch), User(Art3mis), User(Shoto))
    scala> val third = users find (_.name contains "3")
                    third: Option[User] = Some(User(Art3mis))
    scala> val greet = third map (_.greet) getOrElse "hi"          
    greet: String = Hello from Art3mis  //Can you see why String is the correct result of a combination of map and getOrElse against our Option[String]?


    Letís round up our introduction to classes by working through examples of inheritance and polymorphism. A class can extend up to one other class in Scala with the extends keyword, and override (i.e., supplant) the behavior of an inherited method with the override keyword. The fields and methods in a class can be accessed (if strictly necessary) with the this keyword, while the fields and methods in the parent class(es) can be accessed with the super keyword. The super keyword is especially useful when a method needs to still access the similar method in its parent class that it is overriding.
    Iíll demonstrate this with a parent class, ìA,î and subclass, ìC,î and a class situtated between these two, ìBî:

    scala> class A {  
    |   def hi = "Hello from A"  
    |   override def toString = getClass.getName  
    | }

    defined class A

    scala> class B extends A
    defined class B
    scala> class C extends B { override def hi = "hi C -> " + super.hi }
    defined class C
    scala> val hiA = new A().hi
    hiA: String = Hello from A
    scala> val hiB = new B().hi
    hiB: String = Hello from A
    scala> val hiC = new C().hi
    hiC: String = hi C -> Hello from A

    Letís put this knowledge to use to create a list of instances of A, B, and C. What should we declare as the type of such a list, A, B, or C? To ensure that the list can include instances of each of these classes, we should define the list as List[A], which is compatible with all of these classes:
    scala> val misc = List(new C, new A, new B)
    misc: List[A] = List(C, A, B)
    scala> val messages = misc.map(_.hi).distinct.sorted
    messages: List[String] = List(Hello from A, hi C -> Hello from A)

    Whoops! Despite my warning to define the list as List[A], I forgot to add an explicit type. Fortunately, the Scala compiler was able to infer the common type of the three instances as being A, the parent class, and set the listís type parameter correctly. Come to think of it, thatís a great use for the compilerís type inference featureófinding the lowest (most specific) common denominator of one or more instances.

    ----------------------------------------------------
    Defining Classes
    -----------------------------------------------------

    Syntax: Defining a Class with Input Parameters

    class <identifier> ([val|var] <identifier>: <type>[, ... ])
                [extends <identifier>(<input parameters>)]
               [{ fields and methods }]
    A class with input parameters gives a programmer a reason to create multiple instances, because each instance can have its own unique contents.

     Letís try creating a class with both value and variable fields as parameters:

    scala> class Car(val make: String, var reserved: Boolean) {
       |   def reserve(r: Boolean): Unit = { reserved = r }  
    | }

    defined class Car

    scala> val t = new Car("Toyota", false)
    t: Car = Car@4eb48298
    scala> t.reserve(true)

    scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
    My Toyota is now reserved? true


    Syntax: Defining a Class with Input Parameters and Default Values
    -----------------------------------------------------------------------------------
    class <identifier> ([val|var] <identifier>: <type> = <expression>[, ... ]) [extends <identifier> (<input parameters>)]
      [{
    fields and methods
    }]


    EXAMPLE
    ---------------
    scala> class Car(val make: String, var reserved: Boolean = true,val year: Int = 2015) {
                 |   override def toString = s"$year $make, reserved = $reserved"  
                | }

              defined class Car

    scala> val a = new Car("Acura") (1)
                  a: Car = 2015 Acura, reserved = true
    scala> val l = new Car("Lexus", year = 2010) (2)
                          l: Car = 2010 Lexus, reserved = true
    scala> val p = new Car(reserved = false, make = "Porsche") (3)
    p: Car = 2015 Porsche, reserved = false

    1)Only the first parameter is required, and we can invoke it by position.
     2)Here, the first and third parameters are specified. Because the third parameter is out of order weíll have to specify it by name.
     3)This time none of the parameters are invoked by position, with the final one skipped.


    List[String] may contain String instances and support operations that take and re- turn a String.

    Syntax: Defining a Class with Type Parameters
    -----------------------------------------------------------
    class <identifier> [type-parameters]  ([val|var] <identifier>: <type> = <expression>[, ... ])                   [extends <identifier>[type-parameters](<input parameters>)]                
    [{ fields and methods }]


    Letís create our own collection and use a type parameter to ensure type safety. The new collection will extend Traversable[A], the parent class of Iterable

    scala> class Singular[A](element: A) extends Traversable[A] {          (1)
    |   def foreach[B](f: A => B) = f(element)                        (2)
    | }

             defined class Singular

    scala> val p = new Singular("Planes") (3)
    p: Singular[String] = (Planes)                                  
    scala> p foreach println (4)
    Planes
    scala> val name: String = p.head (5)
                   name: String = Planes

    1)A good example of passing a type parameter to the parent class in the class definition.
    2)By defining a foreach() operation, Traversable will ensure our class is a real collection and can use this to enable every other collection operation.
    3)Here is a validation of our type-parameterized class, with the REPL printing the class name and the name of the parameterized type used to instantiate it (a String).
    4)An example usage of the foreach method we defined, reduced to its most unadorned invocation.
    5)Another example usage of foreach, indirectly this time, as we access Traversa ble.head, which invokes foreach for us. By extending Traversable we can access head and a range of other standard collection operations.



    ---------------------------------------------------
    Abstract Classes
    ----------------------------------------------------

    An abstract class is a class designed to be extended by other classes but not instantiated itself. Abstract classes are designated so by the abstract keyword, placed before the class keyword when defining the class.

    An abstract class can be used to define the core fields and methods required by its subclasses without providing an actual implementation. Thanks to polymorphism, a value with the type of the abstract class can actually point to an instance of one of its nonabstract subclasses, and invoke methods that actually end up being invoked on the subclass.

    Abstract classes provide unimplemented fields and methods by declaring them without defining them. A declared field or method will include the name and parameters but not a starting value or implementation, respectively. A class that extends an abstract class with declared fields and methods, and is not itself marked as abstract, must provide their implementations. An abstract class can also have its own implemented fields and methods, which would not require implementations in subclasses. Letís create our own abstract class with a declared value and method, and experiment with implementations:

    scala> abstract class Car {  
    val year: Int
          val automatic: Boolean = true  
      def color: String  
    }

    defined class Car

    scala> new Car()

    <console>:9: error: class Car is abstract; cannot be instantiated          
     new Car()

    scala> class RedMini(val year: Int) extends Car {
      def color = "Red"  
    }

    defined class RedMini

    scala> val m: Car = new RedMini(2005)
    m: Car = RedMini@5f5a33ed

    An experiment with instantiating our abstract class Car by itself didnít work, for the obvious reason that it is abstract and uninstantiatable. Still, itís nice to see that Scalaís compiler pointed this out with a helpful message.
    Creating a subclass that extends Car but adds a value parameter and a concrete imple- mentation of the color method solved the problem. The RedMini class is a successful implementation of its parent abstract class and can be instantiated with only its year as a parameter.

    --------------------------------------------------
    Anonymous Classes
    --------------------------------------------------

    With anonymous classes, class definitions donít need to be stable or reusable. When a subclass will only be needed once, the anonymous class syntax can help to simplify your code base.

    Hereís a more illustrative example of when you may find it useful to create an anonymous class. We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation. This should look familiar to those with JavaScript experience, especially if you have worked on jQuery-style event handlers:

    scala> abstract class Listener { def trigger }
    defined class Listener
    scala> class Listening {     |   var listener: Listener = null     |   def register(l: Listener) {
    listener = l }  
                |   def sendNotification() { listener.trigger }  
    | }

    defined class Listening

    scala> val notification = new Listening()
    notification: Listening = Listening@66596c4c
    scala> notification.register(new Listener {  
    |   def trigger { println(s"Trigger at ${new java.util.Date}") }  
    | })
    scala> notification.sendNotification
    Trigger at Fri Jan 24 13:15:32 PDT 2014

    Overloaded Methods
    ------------------------------

    An overloaded method is a strategy for providing choices to callers. A class may have two or more methods with the same name and return value but with different arrange- ments of input parameters. By overloading a method name with multiple implemen- tations, multiple choices for invoking a method with a specific name are made available.

    Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:
    scala> class Printer(msg: String) {
     |   def print(s: String): Unit = println(s"$msg: $s")  
    |   def print(l: Seq[String]): Unit = print(l.mkString(", "))
     | }

    defined class Printer

    scala> new Printer("Today's Report").print("Foggy" :: "Rainy" :: "Hot" :: Nil)
    Today's Report: Foggy, Rainy, Hot

    It is not possible to have two methods with the same name and input parameters, but different return values. Doing so will cause a Scala compiler error, because there is no way for only one of the methods to be specifically selected during compilation

    Apply Methods
    ----------------------

    Methods named ìapply,î sometimes referred to as a default method or an injector meth- od, can be invoked without the method name. The apply method is essentially a shortcut, providing functionality that can be triggered using parentheses but without a method name.
    Letís try it with a class that multiplies numbers by a predefined amount:

    scala> class Multiplier(factor: Int) {  
      def apply(input: Int) = input * factor  
    }
    defined class Multiplier
    scala> val tripleMe = new Multiplier(3)
    tripleMe: Multiplier = Multiplier@339cde4b
    scala> val tripled = tripleMe.apply(10)
    tripled: Int = 30
    scala> val tripled2 = tripleMe(10)
    tripled2: Int = 30
    Our ìtripleMeî instance can be used with or without the ìapplyî name to triple a given number.
    You might remember this syntax from retrieving an element from a list by its index, which happens to use the List.apply method:
    scala> val l = List('a', 'b', 'c')
    l: List[Char] = List(a, b, c)
    scala> val character = l(1)
    character: Char = b
    Here, the List.apply(index) method provides access to an element by index, an op- eration so common that it makes a good candidate for being the default method of lists. One potential disadvantage to making a method be the default one is if it makes the code look odd. Accessing the default method should be natural, like the accessor method for lists. Try to only use the apply method where it makes sense, like an accessor method for a list.

    Lazy Values
    -------------------
    Lazy values are a great way to ensure that time- or performance-sensitive operations can be executed only once in a classís lifetime. They are popularly used to store infor- mation such as file-based properties, open database connections, and other immutable data that should only be initialized if it is really necessary. By initializing this data in a lazy valís expression, you can ensure that it will only operate if the lazy val is accessed at least once in the class instanceís lifetime

    This concept is perhaps better explained with an example. Hereís one that shows when a regular value is calculated versus a lazy value:
    scala> class RandomPoint {     |   val x = { println("creating x"); util.Random.nextInt }  
      lazy val y = { println("now y"); util.Random.nextInt }
    | }
    defined class RandomPoint

    scala> val p = new RandomPoint()
    creating x
    p: RandomPoint = RandomPoint@6c225adb   //here x created but not the y
    scala> println(s"Location is ${p.x}, ${p.y}")   //here y also created when first access time
    now y
    Location is 2019268581, -806862774
    scala> println(s"Location is ${p.x}, ${p.y}")
    Location is 2019268581, -806862774


    Our class, RandomPoint, initializes its two fields with expressions that print a message before returning their randomly generated number. The ìxî field, a regular value, is initialized when our instance ìpî is created. The ìyî field, a lazy value, is initialized the first time we access it, but only the first time. In the second printout, both values have been initialized and are stable
    ---------------
    Packaging
    --------------
    Packages are Scalaís (and Javaís) system for code organization. They make it possible to organize Scala code by directory using period-separated paths. Use the package key-word
    at the top of a Scala source file to declare all classes in that file to be included in the package.


    Packaging Syntax
    ----------------------
    package <identifier> { <class definitions> }   (or)

    package <identifier>
    Scala source files should be stored in directories that match their packages. For example, a ìDateUtilitiesî class in the ìcom.netflix.utilitiesî package should be stored under com/ netflix/utilities/DateUtilities.scala. The Scala compiler will store the generated .class files (the standard binary format for JVM-executable code) in a directory structure that matches the package. Letís try this out by creating a source file with a package and compiling it. Weíll use the scalac command to compile the source file and generate a class file local to the current directory:
    $ mkdir -p src/com/oreilly
    $ cat > src/com/oreilly/Config.scala package com.oreilly class Config(val baseUrl: String = "http://localhost")
    $ scalac src/com/oreilly/Config.scala
    $ ls com/oreilly/Config.class com/oreilly/Config.class
    The src directory is a nice way to separate the source code from whatever else is in the current directory, but it wasnít actually used by the compiler. It took the relative path to the source file, compiled it, and generated a class file relative to the directory you launched the compiler from

    Accessing Packaged Classes
    --------------------------------------

    Letís try this out by accessing the JDKís Date class, located in the java.util package:
    scala> val d = new java.util.Date
    d: java.util.Date = Wed Jan 22 16:42:04 PDT 2014

    A more convenient way to access classes in other packages is to import them into the current namespace. That way, the class can be accessed without its package prefix. To import a class, use the import keyword followed by the full package and name of the class.

    Syntax: Importing a Packaged Class
    -----------------------------------------
    import <package>.<class>
    Letís create a new Date, but only after importing the class into the namespace so we can refer to it by name:
    scala> import java.util.Date import java.util.Date
    scala> val d = new Date
    d: java.util.Date = Wed Jan 22 16:49:17 PDT 2014
    The Date class we are instantiating still lives in its java.util package, but is now also part of the current namespace.
    The import command is a statement, because it doesnít return a value. Unlike in Java (which has a similar import keyword), an import can be placed anywhere in your code where you might use a statement.
    Letís exercise the ability to place imports wherever we might use any other statement. In this example Iíll add an import for Javaís UUID class in the middle of a println call:
    scala> println("Your new UUID is " + {import java.util.UUID; UUID.randomUUID})
    Your new UUID is 47ba6844-3df5-403e-92cc-e429e614c9e5
    You may not always want to add imports in the restricted scope of a function call. However, adding your imports near the code where you are using the imported classes helps to make the intent of the import more clear. It may also prevent name conflicts caused by importing multiple classes of the same name from different packages. By adding the conflicting imports in separate scopes, as opposed to adding them at the top of the file, the classes can be used without conflict.
    An alternative to importing the full package and class is to import part of the pack- age, reducing the need to refer to the full package but not quite importing a class. Scalaís imports are accumulative, so importing a package allows us to remove that package from the full path of a class in the package.

    Scala also supports importing the entire contents of a package at once with the under- score (_) operator. After doing so, every class in that package will be added to the name- space. You might remember when we used this to import multiple Future helper classes (see ìHandling futures synchronouslyî on page 129) without importing them one at a time

    Letís use the import-all feature to import all of the mutable collections into our current namespace, and then experiment with the ArrayBuffer and Queue collections in that package:
    scala> import scala.collection.mutable._
    scala> val b = new ArrayBuffer[String]
    b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
    scala> b += "Hello" res0:
    b.type = ArrayBuffer(Hello)
    scala> val q = new Queue[Int]
    q: scala.collection.mutable.Queue[Int] = Queue()
    scala> q.enqueue(3, 4, 5)
    scala> val pop = q.dequeue
    pop: Int = 3
    scala> println(q)
    Queue(4, 5)

    Speaking of ArrayBuffer, did you notice that we imported everything in the collection.mutable package but the REPL printed its full class name as scala.collection.mutable.ArrayBuffer? Scala does its own automatic imports in every Scala class, importing the entire scala._ and java.lang._ packages. This makes it possible to access the  classes and packages in scala and java.lang directly without using the full path.

    IMPORT GROUP
    ------------------------
    An alternative to importing a full package is to use an import group. With this feature, you can list a group of class names to import instead of a complete package.
    Syntax: Using an Import Group
    import <package>.{<class 1>[, <class 2>...]} With an import group I could have imported the Queue and ArrayBuffer collections directly without importing the mutable Map:
    scala> import scala.collection.mutable.{Queue,ArrayBuffer}
    scala> val q = new Queue[Int]
    q: scala.collection.mutable.Queue[Int] = Queue()
    scala> val b = new ArrayBuffer[String]
    b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
    scala> val m = Map(1 -> 2)
    m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)

    After importing only the mutable collections that we wanted, we can use the Queue and ArrayBuffer mutable collections while still accessing the Map immutable collection. In
    veral classes from the same package they can visibly reduce the size of an ìimportî section.
    Thereís actually a way to add both the immutable and the mutable Map collections to the current namespace without having a conflict. To do this, use an import alias that renames one of the types inside the local namespace. Whatís renamed is the local namespace reference to the class, not the class itself, so there is no actual change to classes outside your namespace (typically the file you are editing).

    IMPORT ALIIAS
    -------------------------------------
    Syntax: Using an Import Alias
    import <package>.{<original name>=><alias>}
    Letís use an import alias to bring the collection.mutable.Map collection into our namespace, but in a way that wonít conflict with our standard immutable Map:

    scala> import collection.mutable.{Map=>MutMap}
    import collection.mutable.{Map=>MutMap}
    scala> val m1 = Map(1 -> 2)
    m1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
    scala> val m2 = MutMap(2 -> 3)
    m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
    scala> m2.remove(2); println(m2)
    Map()
    With our aliased collection ìMutMapî (short for ìmutable,î not ìmuttî!) we can create both mutable and immutable maps by name, without specifying their packages


    PACKAGE WRITING
    ------------------------------
    Syntax: Packaging Classes
    package <identifier> { <class definitions> }
    Letís rewrite our ìConfigî example (from ìPackagingî on page 151) using packaging syntax. Because we arenít relying on a file to delimit the end of the package, we can write the entire package in the REPL:

    The Scala REPL Requires ìRawî Paste Mode for Packages .
    Packages are traditionally used to mark files, and thus are unsuppor- ted in the standard editing mode in the REPL. The workaround is to enter the ìrawî paste mode with :paste -raw and then paste the contents of a Scala file, which will be fully compiled but available from the REPL.

    scala> :paste -raw // Entering paste mode (ctrl-D to finish)
    package com {
    package oreilly {
       class Config(val baseUrl: String = "http://localhost")
    }
    }
    // Exiting paste mode, now interpreting.
    scala> val url = new com.oreilly.Config().baseUrl url: String = http://localhost

     Our new class is now available at com.oreilly.Config and clearly packaged.


    Privacy Controls
    -------------------------
    A corollary to packaging code is using privacy controls to manage its access. While you are organizing your code into separate packages (or subpackages), youíll probably find certain functionality in one package that ought to be hidden from other packages. For example, low-level persistence code could be hidden from your user interface-level code to force your layers to use a middle layer for communication. Or you may want to limit who can extend a subclass so that your parent class can keep track of its implementers.

    One privacy control is marking fields and methods as protected, which will limit their access to code from the same class or its subclasses. No other code except that class or subclasses will have access. Use the protected keyword before a val, var, or def key- word to mark that entity as being protected

    Hereís an example of protecting a field from access by outside classes. The field is still accessible by a subclass, however:
    scala> class User { protected val passwd = util.Random.nextString(10) } defined class User
    scala> class ValidUser extends User { def isValid = ! passwd.isEmpty } defined class ValidUser
    scala> val isValid = new ValidUser().isValid isValid: Boolean = true To verify that the ìpasswdî field is only accessible to ìUserî and its subclasses, try cre- ating a new instance of ìUserî and access its protected field directly. You should see an error from the compiler alerting you that the ìpasswdî field is not accessible from out- side the class (or subclasses).

    When you need a more stringent level of protection, mark fields and methods as pri- vate to limit their access to only the class in which they are defined. No other code outside the class, and not even subclasses, will have access to that field.

     Finally, weíll add a validation system, again without exposing our private (literally!) password to external reads or writes:
    scala> class User(private var password: String) {     |   def update(p: String) {     |     println("Modifying the password!")     |     password = p     |   }     |   def validate(p: String) = p == password     | } defined class User
    scala> val u = new User("1234") u: User = User@94f6bfb
    scala> val isValid = u.validate("4567") isValid: Boolean = false
    scala> u.update("4567") Modifying the password!
    scala> val isValid = u.validate("4567") isValid: Boolean = true

    Privacy Access Modifiers
    ---------------------------------
    The private and protected keywords provide class-hierarchy restrictions, but there are times when you want more fine-grained access control for your classís members. For example, a class in a persistence package may only want to reveal some of its database-level methods to other classes in the same package to reduce bugs and ensure a single point of access.

    You can add this level of control by specifying access modifiers in addition to your private or protected designation. An access modifier specifies that such a designation is only active up to a given point, such as a package, class, or instance, and then is inactive within that point


    Letís try this out with an example of specifying both package-level and instance-level protections. Weíll use the packaging syntax to denote the classís package, and thus the ìrawî paste mode in the REPL to support the packaging:

    scala> :paste -raw // Entering paste mode (ctrl-D to finish)
         package com.oreilly {
      private[oreilly] class Config { (1)
                                    val url = "http://localhost"
    }
     class Authentication {
      private[this] val password = "jason" // TODO change me  (2)             def validate = password.size > 0
     }
     class Test {    println(s"url = ${new Config().url}")
    }

    }
    // Exiting paste mode, now interpreting. (3)
    scala> val valid = new com.oreilly.Authentication().validate    
    valid: Boolean = true
    scala> new com.oreilly.Test url = http://localhost                                           (4)
    res0: com.oreilly.Test = com.oreilly.Test@4c309d4d

    scala> new com.oreilly.Test url = http://localhost (5)
                   res0: com.oreilly.Test = com.oreilly.Test@4c309d4d

    scala> new com.oreilly.Config
     <console>:8: error: class Config Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here. Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class. Here we are verifying ìpasswordî access from the same instance. The ìTestî class was able to successfully instantiate a ìConfigî classÖ Ö but we were not able to do the same from outside the package.
    in package oreilly cannot be   accessed in package com.oreilly   New com.oreilly.Config

    1)Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here.
    2)Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class.
    3)Here we are verifying ìpasswordî access from the same instance.
    4)The ìTestî class was able to successfully instantiate a ìConfigî classÖ
    5) Ö but we were not able to do the same from outside the package.


    *******************************************************************************

    Objects, Case Classes, and Traits
    *********************************************

    -------------
    Objects
    -------------

    An object is a type of class that can have no more than one instance, known in object- oriented design as a singleton. Instead of creating an instance with a new keyword, just access the object directly by name. An object gets automatically instantiated the first time it is accessed in a running JVM, which also means that until it is accessed the first time it wonít get instantiated

    Use the object keyword, in place of class, to define an object. Objects do not take any parameters (they are automatically instantiated), but you can define the same fields, methods, and internal classes as you can with regular classes.

    Syntax: Defining an Object
    object <identifier> [extends <identifier>] [{ fields, methods, and classes }]

    Letís design an object that will demonstrate how objects are automatically instantiated:

    scala> object Hello { println("in Hello"); def hi = "hi" }
    defined object Hello

    scala> println(Hello.hi)
    in Hello hi
    scala> println(Hello.hi)
    hi
    The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the objectís ìhiî method reused the same global instance so there was no additional initialization


    +As an example, weíll create an object that provides pure functions as utilities, one of my favorite uses for objects:
    scala> object HtmlUtils {     |   def removeMarkup(input: String) = {     |     input
     |       .replaceAll("""</?\w[^>]*>""","")     |       .replaceAll("<.*>","")     |   }     | }
    defined object HtmlUtils
    scala> val html = "<html><body><h1>Introduction</h1></body></html>" html: String = <html><body><h1>Introduction</h1></body></html>
    scala> val text = HtmlUtils.removeMarkup(html)
    text: String = Introduction

    Apply Methods and Companion Objects
    ----------------------------------------------------

    We have covered the apply method for classes (see ìApply Methodsî on page 150), which makes it possible to invoke an instance. The same feature works for objects, making it possible to invoke an object by name. By defining one or more of these methods, your object can be invoked by name, much like List(1, 2, 3).

     The Future object uses apply() to take your function parameter and invoke it in a background thread. This is known as the Objects factory pattern in object-oriented programming, and is a popular use of the apply() method in objects. Specifically, the factory pattern is a popular way to generate new instances of a class from its companion object. A companion object is an object that shares the same name as a class and is defined together in the same file as the class. Having a companion object for a class is a common pattern in Scala, but there is also a feature from which they can benefit. Companion objects and classes are considered a single unit in terms of access controls, so they can access each otherís private and protected fields and methods.

    Letís try out the apply() factory pattern and the companion object pattern in the same example. We will use the REPLís :paste mode to simulate a class and object defined together in the same file, because otherwise the REPL would assume they are separate:

    However, we havenít yet seen the benefit of a companion object, namely the special access controls that it shares with a companion class. Letís try this out in a new example where the class accesses private members of its companion object:

    scala> :paste // Entering paste mode (ctrl-D to finish)
    object DBConnection {
    private val db_url = "jdbc://localhost"
    private val db_user = "franken"
    private val db_pass = "berry"
      def apply() = new DBConnection
    }
    class DBConnection {
    private val props = Map(
    "url" -> DBConnection.db_url,  
    "user" -> DBConnection.db_user,
      "pass" -> DBConnection.db_pass  )
    println(s"Created new connection for " + props("url"))
    }

    // Exiting paste mode, now interpreting.
    defined object DBConnection
    defined class DBConnection

    scala> val conn = DBConnection()
    Created new connection for jdbc://localhost
    conn: DBConnection = DBConnection@4d27d9d

    Our new DBConnection object stores the database connection data in private constants, while the class of the same name can read them when creating a connection. The con- stants are global, because the settings are constant across the application, and safe from being read by any other part of the system.

    This approach is suitable for testing, but doesnít make it possible to reuse your code. The scala command will execute the contents of your file as if they were entered in a REPL, but you donít end up with compiled classes. In order to write reusable, compiled code, youíll need to compile your classes and objects with the scalac command and then execute them from your own application. In the next section weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects


    Command-Line Applications with Objects
    -----------------------------------------------------

    weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects

    Here is a new example that emulates the Unix command cat, which prints the contents of a file to the console. It takes one or more filenames (or paths) and prints each one to the console:
    $ cat > Cat.scala object Cat {  def main(args: Array[String]) {    for (arg <- args) {    
    println( io.Source.fromFile(arg).mkString )    }  } }

    $ scalac Cat.scala

    $ scala Cat Date.scala

    object Date {  def main(args: Array[String]) {    println(new java.util.Date)  } }
    This time weíre making use of the input arguments. The fromFile method in the Scala libraryís io.Source object (we can call it by its correct name now) is used to read each file, and the collection method mkString is used to convert the lines back into a single String for printing

    USE;
    ------
    In a way, the best command-line applications are like pure functions: they read input, process it, and write output. Like the operations in Scalaís collections they are only good for a single task, but when chained together they create a bounty of new opportunities and possibilities. Command-line applications written in Scala may not replace native tools and shell scripts, because their slower startup time (a known problem in the JVM) and greater memory requirements may make them less desirable for all environments. They do make writing command-line tools more fun, however, and are a great way to learn the language. I recommend taking the time to rewrite some of your favorite (and shorter) shell scripts in Scala. Itís a great way to continue learning and practicing with the language, and you may find your Scala applications to be shorter and more stable than those written in other languages.


    ------------------------------------
    Case Classes
    -----------------------------------

    A case class is an instantiable class that includes several automatically generated meth- ods. It also includes an automatically generated companion object with its own auto- matically generated methods. All of these methods in the class and in the companion object are based on the classís parameter list, with the parameters being used to formulate methods like an equals implemention that iteratively compares every field and a to String method that cleanly prints out the class name and all of its field values

     And extending a case class with a regular class could lead to invalid results from the generated methods, which canít take into account fields added by sub-classes. However, if you want a class with a definitive set of fields, and these automatically generated methods are useful, then a case class may be right for you

    To create a case class, just add the keyword case before your class definition.

    Syntax: Defining a Case Class
    ----------------------------------------
    case class <identifier> ([var] <identifier>: <type>[, ... ])
    [extends <identifier>(<input parameters>)] [{ fields and methods }]

    -->The val Keyword Is Assumed for Case Class Parameters :
    ---------------------------------------------------------------------
    By default, case classes convert parameters to value fields so it isnít necessary to prefix them with the val keyword. You can still use the var keyword if you need a variable field.

     --->displays the class and object methods that get automatically generated for case classes.

     Generated case class methods
    --------------------------------------------------------------------------------------------------------------------
    Name Location Description
    --------------------------------------------------------------------------------------------------------------------
    apply Object A factory method for instantiating the case class.

    copy Class Returns a copy of the instance with any requested changes. The parameters are the classís fields with the default values set to the current field values.

    equals Class Returns true if every field in another instance match every field in this instance. Also invocable by the operator ==.

    hashCode Class Returns a hash code of the instanceís fields, useful for hash-based collections.

    toString Class Renders the classís name and fields to a String.

    unapply Object Extracts the instance into a tuple of its fields, making it possible to use case class instances or pattern  matching.
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    The methods generated by the Scala compiler for case classes arenít special in any way, other than that they are automatically generated for you. You could skip using case classes and add the methods and companion object yourself. The benefit that case classes bring is convenience, because writing all of these methods correctly for every data-based class would require a lot of work and maintenance. They also add a certain level of consistency, because all case classes carry the same features.


    scala> case class Character(name: String, isThief: Boolean) (1)
    defined class Character
    scala> val h = Character("Hadrian", true)                   (2)
    h: Character = Character(Hadrian,true)                    
    scala> val r = h.copy(name = "Royce")       // r,h is instances (3)
            r: Character = Character(Royce,true)
    scala> h == r                                                 (4)
    res0: Boolean = false
    scala> h match {  
    case Character(x, true) => s"$x is a thief"             (5)
      case Character(x, false) => s"$x is not a thief"  
    | }
        res1: String = Hadrian is a thief

    1)Hereís our companion objectís factory method, Character.apply().
    2) The generated toString method, printed here by the REPL, is a clean and simple representation of the fields in our instance.
    3)Our second instance shares the same value for the second field, so we only need to specify a new value for the first field in the copy method.
    4)If both instances are nonnull, the == operator triggers an instanceís equals method, acting as a useful shortcut to the field comparisonñbased method generated for us.
    5) The companion objectís unapply method allows us to decompose the instance into its parts, binding the first field (see ìMatching with Wildcard Patternsî on page 34) and using a literal value to match the second field.

    All of the generated methods we used in the example depended on the case class having two fields, name and isThief, based on the case class parameters. If our case class had extended another class with its own fields, but we hadnít added the fields as case class parameters, the generated methods wouldnít have been able to make use of them. This is an important caveat to know about before using case classes.

    ------------------------------------------------------------
    Traits
    ------------------------------------------------------------
    A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated

    To define a trait, use the trait keyword in place of where you would normally use the class keyword.

    Syntax: Defining a Trait
    -------------------------------
    trait <identifier> [extends <identifier>] [{ fields, methods, and classes }]

    scala> trait HtmlUtils {     |   def removeMarkup(input: String) = {     |     input     |       .replaceAll("""</?\w[^>]*>""","")     |       .replaceAll("<.*>","")     |   }     | }
    defined trait HtmlUtils

    scala> class Page(val s: String) extends HtmlUtils {     |   def asPlainText = removeMarkup(s)     | }
    defined class Page

    scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
    res2: String = Introduction Our Page class can now use the removeMarkup method directly without specifying an object name.

    This works pretty well, but a class version of HtmlUtils could have done the same job. Letís make it more interesting by adding a second trait. This time weíll use a new key- word, with, which is required for extending the second and later traits:

    scala> trait SafeStringUtils {     // Returns a trimmed version of the string wrapped in an Option,     |   // or None if the trimmed string is empty.     |
    def trimToNone(s:String): Option[String] = {     |     Option(s) map(_.trim) filterNot(_.isEmpty)     |   }     | }
    defined trait SafeStringUtils
    scala> class Page(val s: String) extends SafeStringUtils with HtmlUtils {     |   def asPlainText: String = {     |     trimToNone(s) map removeMarkup getOrElse "n/a"     |   }     | } defined class Page
    scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
    res3: String = Introduction
    scala> new Page("  ").asPlainText
    res4: String = n/a
    scala> new Page(null).asPlainText
    res5: String = n/a

    Our new, more robust Page class now extends two traits and can handle null or empty strings by returning the message n/a.

    This process of taking a horizontal list of a class and traits being extended, and reforming them into a vertical chain of one class extending another, is known as linearization. It is a kind of coping mechanism for supporting multiple inheritance in an execution environment that only supports single inheritance. The fact that the JVM only supports single inheritance ensures that all class hierarchies are nondeterministic and prevents the possibility of confusing two traits that have competing members


    The most important point to understand about linearization is in what order the Scala compiler arranges the traits and optional class to extend one another. The multiple inheritance ordering, from the lowest subclass up to the highest base class, is right to left.

    Thus, a class defined as class D extends A with B with C, where A is a class and B and C are traits, would be reimplemented by the compiler as class D extends C ex tends B extends A. The rightmost trait is the immediate parent of the class being defined, and either the class or the first trait becomes the last parent class. This is a lot to remember, so letís write a quick test to verify this ordering:

    scala> trait Base { override def toString = "Base" }
    defined trait Base
    scala> class A extends Base { override def toString = "A->" + super.toString }
    defined class A
    scala> trait B extends Base { override def toString = "B->" + super.toString }
    defined trait B
    scala> trait C extends Base { override def toString = "C->" + super.toString }
    defined trait C
    scala> class D extends A with B with C { override def toString = "D->" +  super.toString } defined class D
    scala> new D()
    res50: D = D->C->B->A->Base
    The toString method overridden in D prints the class name and then appends the output of its parent classís implementation. Fortunately all of its parent classes also override this method, so we can see the exact ordering of methods called. First the toString in D was invoked, followed by the one in trait C, trait B, class A, and finally the common base class Base.

    The process of linearization may seem odd, but itís a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesnít. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime

    Another benefit of linearization is that you can write traits to override the behavior of a shared parent class. Hereís an example of a solid base class plus traits that add extra functionality when combined with a subclass. The example is rather lengthy so weíll cover it in two parts. First, hereís the parent class and two traits that extend it:

    scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
    defined class RGBColor
    scala> val green = new RGBColor(255 << 8).hex
    green: String = 00FF00
    scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
    defined trait Opaque
    scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
    defined trait Sheer
    The two traits, Opaque and Sheer, extend the RGBColor class and add an opacity level to the red-green-blue color of its parent. The extra byte is often known as an alpha channel in computer graphics, so the traits are convering an RGB color value to an RGBA (a for alpha) color value, in hexadecimal format.

    ------------------------------
    Self Types
    ------------------------------

    A self type is a trait annotation that asserts that the trait must be mixed in with a specific type, or its subtype, when it is added to a class. A trait with a self type cannot be added to a class that does not extend the specified type. In a way, it is a guarantee that the trait will always be extending that type, while not actually directly extending it.

    A popular use of self types is to add functionality with traits to classes that require input parameters. A trait cannot easily extend a class that takes input parameters, because the trait itself cannot take input parameters. However, it can declare itself to be a subtype of that parent class with a self type and then add its functionality.

    A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type

    Syntax: Defining a Self Type
    trait ..... { <identifier>: <type> => .... }

    The standard identifier used in self types is ìself,î although any other identifier may be used. That is, except for a keyword like this. The benefit of using the common identifier ìselfî is that it can help to make your code more readable to other Scala developers.

    Here is an example of a trait using a self type to ensure that it will always be a subtype of the specified type when mixed into a class:

    scala> class A { def hi = "hi" }
    defined class A
    scala> trait B { self: A =>      override def toString = "B: " + hi     } (1)
    defined trait B
    scala> class C extends B (2)
    <console>:9: error: illegal inheritance;
     self-type C does not conform to B's selftype B with A
          class C extends B                       ^
    scala> class C extends A with B (3)
                                             defined class C
    scala> new C() (4)
    res1: C = B: hi    
                                             
    1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. Ö
    2) but just to prove it, letís try defining a class with trait B but without the requested class. No luck.
    3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
    4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.

    -------------------------------------
    Instantiation WITH Traits
    -------------------------------------

    In this chapter we have used traits by having classes extend them, using the extends or with keyword in the class definition. The class that extends the trait will pick up the fields and methods of that trait, whether they are implemented by the trait or inherited from its own subtypes


    You can add one or more traits to a class using the WITH keyword. The EXTENDS keyword cannot be used here, which is appropriate; your class is not actually extending the traits but instead being extended by them.
     Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:
    scala> class A
    defined class A
    scala> trait B { self: A => }
    defined trait B
    scala> val a = new A with B
    a: A with B = $anon$1@26a7b76d

     we created an instance where trait B extended trait A.

    Letís experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:

    scala> class User(val name: String) {   def suffix = ""   override def toString = s"$name$suffix"     | }
    defined class User
    scala> trait Attorney { self: User => override def suffix = ", esq." }
    defined trait Attorney
    scala> trait Wizard { self: User => override def suffix = ", Wizard" }
    defined trait Wizard
    scala> trait Reverser { override def toString = super.toString.reverse }
    defined trait Reverser
    scala> val h = new User("Harry P") with Wizard
    h: User with Wizard = Harry P, Wizard
    scala> val g = new User("Ginny W") with Attorney
    g: User with Attorney = Ginny W, esq.
    scala> val l = new User("Luna L") with Wizard with Reverser
    l: User with Wizard with Reverser = draziW ,L anuL

    ------------------------------------------
    Importing Instance Members
    ------------------------------------------

    The import keyword can also be used to import members of classes and objects into the current namespace. This makes it possible to access them directly without specifying their enclosing instance (for classes) or name (for objects).

    The syntax for importing class and object members is the same as importing packaged classes. You can import a single member of a class instance by name, or the entire set of fields and methods with the underscore character. Importing fields and methods does not override privacy controls, so only those that would be normally accessible can be imported.

    Here is an example of a case classís members being imported for better accessibility:
    scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
    defined class Receipt
    scala> {  
      val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")  
      import latteReceipt._
     println(s"Sold a $title for $amount to $who")  
               }

    Sold a Medium Latte for 4.12 to fred By importing the fields from a value with a lengthy name, latteReceipt, we could access them directly in our println statement with a much simpler line of code.

    As an example of object imports, letís add all of the methods from the util.Random object. This object extends the util.Random class, providing a single global instance thatís useful to use when you donít need to set a new seed for random number generation:

    scala> import util.Random._

    scala> val letters = alphanumeric.take(20).toList.mkString
    letters: String = MwDR3EyHa1cr0JqsP9Tf
    scala> val numbers = shuffle(1 to 20)
    numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 18, 1,  16, 8, 20, 14, 19, 11, 17, 3, 15, 7, 4, 9, 6, 12, 13, 2)

     The alphanumeric(): Stream and shuffle(Traversable) methods, members of the util.Random object (and parent class), are here made accessible without their objectís prefix.

    USAGE:
    -----------
    Importing instance members is a great way to streamline your code. Care must be taken to avoid naming conflicts, however, as well as any reductions in code readability. If readers of your code will get confused by the source of the imported members you are using, consider locating your import statement closer to the affected code.


    --------------------------------------------------------------------------------
    VARIABLE DECLARATION AND EXAMPLES
    ---------------------------------------------------------------------------------
    VALUES
    *******
    Values are immutable, typed storage units, and by convention are the default method for storing data.
    You can define a new value using the val keyword.

    The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.


    INTEGER TYPE:
    ---------------
    scala> val m=5;
    m: Int = 5

    scala> m
    res1: Int = 5


    FLOAT TYPE:
    -----------

    scala> val mm =5f  //here f suffix the value belongs to float without f it      belongs to double.
    mm: Float = 5.0

    scala> mm
    res9: Float = 5.0

    scala> val nn = 15.04
    nn: Double = 15.04

    scala> nn
    res2: Double = 15.04


    CHAR TYPE:
    -----------

    scala> val cha = 'a'
    cha: Char = a

    scala> cha
    res3: Char = a


    STRING TYPE:
    ------------
    scala> van name:string = "7hills"
    <console>:1: error: ';' expected but '=' found.
    van name:string = "7hills"
                    ^

    scala> name
    <console>:12: error: not found: value name
           name
           ^

    scala> val name:string = "7hills"
    <console>:11: error: not found: type string
           val name:string = "7hills"
                    ^

    scala> val name:String = "7hills"
    name: String = 7hills

    scala> val mm = println("what are you doing");
    what are you doing
    mm: Unit = ()   //here mm is unit() type

    scala> mm

    scala> mm

    NOTE:when using println command it can not store into variable it just print the data only but we can declare as a string it would be print and retrive anyware

    scala> val mm ="what are you doing"
    mm: String = what are you doing

    scala> mm
    res8: String = what are you doing

    VARIBLE:
    ---------

    The var keyword is used to define a variable with a given name, type, and assignment.
    Syntax: Defining a Variable
    var <identifier>[: <type>] = <data>


    scala> p
    res10: Double = 7.5

    scala> var naam = "7hills"
    naam: String = 7hills

    scala> var naam = "8hills"
    naam: String = 8hills

    scala> val nn = 16.2
    nn: Double = 16.2

    scala> nn = 15.2   //because nn is immutable it intiate by val
    <console>:12: error: reassignment to val
           nn = 15.2
              ^

    scala> naam = "jfkdj"
    naam: String = jfkdj





    *******************************************************************************
    IF...ELSE EXPRESSIONS
    --------------------------------------------------------------------------------

    NOTE:   1) if expressions without an else should always use curly braces, ******   because they tend to be statements that create side effects.

        2) in scala doesn't have the NESTED-IF OR LEADER-IF.

            3) where the punctuation characters ? and : act as a one-line if and else expression.

    scala> val x = 2
    x: Int = 2

    scala> x
    res0: Int = 2

    scala> val nn ={if (x%2 == 0)}
    <console>:1: error: illegal start of simple expression
    val nn ={if (x%2 == 0)}
                          ^

    scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
    x is divisible by 2
    nn: Unit = ()

    scala> val x = 5
    x: Int = 5

    scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
    nn: Unit = ()

    scala> val nn ={if (x%2 == 0) println("x is divisible by 2") else println("x is not divisible by 2")}
    x is not divisible by 2
    nn: Unit = ()


    ------------------------------------------------------------------------------
    Match Expressions
    -------------------------------------------------------------------------------

    scala> val day="sat"
    day: String = sat

    scala> val week = day match
         | { case "mon"|"tue"|"wed"|"thurs"|"fri" => "regularday"
         | case "sat"|"sun" => "weekday"
         | }
    week: String = weekday

    scala> val student = sno match{
         | case 102 => println("\n student name is 7hills \n student number is $sno")
         | case 103 => println("\n student name is 8hills \n student number is $sno")
         | case 104 => println("\n student name is 10hills \n studen number is $sno")
         | }

     student name is 7hills
     student number is $sno
    student: Unit = ()

    printl the single value
    -----------------------
    scala> val sno= 103
    sno: Int = 103

    scala> val stu = sno match{
         | case 102 => println("student name is 7hills sno")
         | case 103 => s"${sno}"
         | }
    stu: Any = 103

    scala> val stu = sno match{
         | case 102 => println("student name is 7hills sno")
         | case 103 => "${sno}"
         | }
    stu: Any = ${sno}


    TO PRINT THE NAME AND VALUE IN SINGLE LINE BY USING CASE STATEMENT
    ------------------------------------------------------------------

    scala> val sno= 105
    sno: Int = 105

    scala> val student = sno match{
         | case 102 => "stuent name is 7hills" + "number is "+ sno
         | case 103 => "stuent name is pavan" + "number is "+ sno
         | case 105 => "student name is sarvan" + "number is" + sno
         | }
    student: String = student name is sarvannumber is105

    scala> val sno =102
    sno: Int = 102

    scala> val stu = sno match{
         | case 102 => "stuent name is 7hills" + "number is "+ sno
         | }
    stu: String = stuent name is 7hillsnumber is 102

    NOTE:Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores

    scala> val sno= 107
    sno: Int = 107

    scala> val student = sno match{
         | case 102 => "stuent name is 7hills" + "number is "+ sno
         | case 103 => "stuent name is pavan" + "number is "+ sno
         | case 105 => "student name is sarvan" + "number is" + sno
         | case _ => println("the given number is invalid")
         | }
    the given number is invalid
    student: Any = ()

    ERROR:
    -----
    val stu = sno match{
         | case 102 => "stuent name is 7hills" + "number is "
         | }
    scala.MatchError: 103 (of class java.lang.Integer)
      ... 34 elided


    SOLUTION: sno must be match with any case value
                      here sno is 103 that way it shows but we write 102


    MATCHING WITH PATTERN GURDS  OR CASE STATEMENT WITH IF()EXPRESSIONS
    -------------------------------------------------------------------

    scala> val a=5
    a: Int = 5

    scala> val b=10
    b: Int = 10

    scala> val option=2
    option: Int = 2


    scala> val arthi = option match{
         | case 1=> "the addition of a and b is" + a+b
         | case 2 => "the subtraction of a and b is" + (a-b)
         | case 3=> "the multiplication of a and b is" + a*b
         | case 4 => "the subtraction of a and b is" + a/b
         | case 5 => "the modulo of a and b is" + a%b
         | }

    arthi: String = the subtraction of a and b is-5

    scala> val arthi = option match{
         | case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
         | case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + (a-b)
         | }
    arthi: String = the subtraction of a and b is-5

    ERROR
    ------
    NOTE: SUBTRACTION OPERTION MUST BE IN () PARANTHISIS.

    scala> val arthi = option match{
         | case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
         | case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
         | }
    <console>:16: error: value - is not a member of String
           case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
                                                                        ^

    NESTED FOR LOOP
    ---------------

    // there is no need to create a relation (fo)
    scala> val fo=for(x<- 1 to 2){
         | for(y <- 3 to 4){
         | println(x,y)
         | }
         | }
    (1,3)
    (1,4)
    (2,3)
    (2,4)
    fo: Unit = ()

    scala> fo
    //not display any result
    scala>

    ********************************************************************************

    -------------------------------------------------------------------------------
    FUNCTIONS
    -------------------------------------------------------------------------------


    UNNUMOUS FUNCTIONS
    -------------------

    scala> val x=9
    x: Int = 9

    scala> val y = 10
    y: Int = 10

    scala> def mul:Int = x*y
    mul: Int

    scala> mul
    res0: Int = 90


    scala> def fun ="hi"
    fun: String

    scala> fun
    res1: String = hi

    --------------------
    NAMED FUNCTIONS
    --------------------

    scala> def MULTIPLICATION(mul:Int):Int = x*y
    MULTIPLICATION: (mul: Int)Int

    scala> MULTIPLICATION
    <console>:15: error: missing argument list for method MULTIPLICATION
    Unapplied methods are only converted to functions when a function type is expected.
    You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.
           MULTIPLICATION
           ^

    scala> MULTIPLICATION
    <console>:15: error: missing argument list for method MULTIPLICATION
    Unapplied methods are only converted to functions when a function type is expected.
    You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.

           MULTIPLICATION
           ^

    scala> MULTIPLICATION(5,5)
    <console>:15: error: too many arguments for method MULTIPLICATION: (mul: Int)Int
           MULTIPLICATION(5,5)
                         ^

    scala> MULTIPLICATION(5)
    res5: Int = 90

    scala> MULTIPLICATION()
    <console>:15: error: not enough arguments for method MULTIPLICATION: (mul: Int)Int.
    Unspecified value parameter mul.
           MULTIPLICATION()
                         ^

    scala> def MULTIPLICATION(mul:Int):Int = mul*y
    MULTIPLICATION: (mul: Int)Int

    scala> MULTIPLICATION(5)
    res7: Int = 50


    scala> def factorial1(n : Int) : Int = {
         | var res = 1
         | for(x <- 1 to n) res = res * x
    //here don't have the return res so it shows error
         | }
    <console>:14: error: type mismatch;
     found   : Unit
     required: Int
           for(x <- 1 to n) res = res * x
                 ^

    scala> def factorial1(n : Int) : Int = {
         | var res = 1
         | for(x <- 1 to n) res = res * x
         | res
         | }
    factorial1: (n: Int)Int

    scala> factorial1(4)
    res10: Int = 24


    scala> def factrec(n : Int) : Int = {
         | if(n==1)
         | n
         | else
         | n*factrec(n-1)  //here give the return statement it show st.consol error
         | }
    factrec: (n: Int)Int

    scala> factrec(3)
    res13: Int = 6




    /* TO FIND THE FIBNOIC SERIES OF GIVEN NO */

    scala> def fib(n:Int):Int={
         | var c=0;var a=0;var b=1
         | for(x <- 1 to (n+1)){
         | c=a+b
         | println("a :" + a +"b: " + b +"c:" + c)
         | a=b;b=c;
         | }
         | c
         | }
    fib: (n: Int)Int

    scala> fib(3)
    a :0b: 1c:1
    a :1b: 1c:2
    a :1b: 2c:3
    a :2b: 3c:5
    res23: Int = 5


    scala> def fib(n:Int):Int={
         | var c=0;var a=0;var b=1
         | for(x <- 1 to n){
         | c=a+b
         | println("a :" + a +"b: " + b +"c:" + c)
         |  a=b;b=c;
         | }
         | c
         | }
    fib: (n: Int)Int

    scala> fib(3)
    a :0b: 1c:1
    a :1b: 1c:2
    a :1b: 2c:3
    res25: Int = 3

    scala> fib(5)
    a :0b: 1c:1
    a :1b: 1c:2
    a :1b: 2c:3
    a :2b: 3c:5
    a :3b: 5c:8
    res26: Int = 8

    -------------------
    nested functions
    -------------------

    scala> def max(x: Int)(y: Int) = if (x > y) x else y
    max: (x: Int)(y: Int)Int

    scala> val larger = max(20)(39)
    larger: Int = 39

    -----------------
    varge parameters (*)
    ----------------
    scala> def sum(items: Int*): Int = {
         | var total = 0
         | for (i <- items){ total += i
         | println("i : " + i +" item : " + items)
         | }
         | total
         | }
    sum: (items: Int*)Int

    scala> sum(10,20,90)
    i : 10 item : WrappedArray(10, 20, 90)
    i : 20 item : WrappedArray(10, 20, 90)
    i : 90 item : WrappedArray(10, 20, 90)
    res29: Int = 120


    ----------------------------------------
    Calling Functions with Named Parameters
    ----------------------------------------

    scala> val res = sum(20,30,40)
    i : 20 item : WrappedArray(20, 30, 40)
    i : 30 item : WrappedArray(20, 30, 40)
    i : 40 item : WrappedArray(20, 30, 40)
    res: Int = 90

    scala> res
    res0: Int = 90


    ----------------------------------------------
    Assigning a Function with the Wildcard Operator
    -----------------------------------------------

    scala> def double(x: Int): Int = x * 2
    double: (x: Int)Int

    scala> val myDouble = double _
    myDouble: Int => Int = <function1>

    scala> val amount = myDouble(20)
    amount: Int = 40

    scala> def double(x: Int,y:Int): Int = x * y * 2
    double: (x: Int, y: Int)Int

    scala> val myDouble = double _,_
    <console>:1: error: ';' expected but ',' found.
    val myDouble = double _,_
                           ^

    scala> val myDouble = double _ _
    <console>:1: error: ';' expected but '_' found.
    val myDouble = double _ _
                            ^

    scala> val myDouble = double _ _


    scala> def mydouble= double _+_
    mydouble: String => String

    scala> val rr = mydouble(20,30)
    <console>:14: error: too many arguments for method apply: (v1: String)String in trait Function1
           val rr = mydouble(20,30)

                            ^
    Anonymous Functions

    Scala represents anonymous functions with a elegant syntax. The _ acts as a placeholder for parameters in the anonymous function. The _ should be used only once, But we can use two or more underscores to refer different parameters.
    scala> List(1,2,3,4,5).foreach(print(_))
    12345
    scala> List(1,2,3,4,5).foreach( a => print(a))
    12345
    scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
    sum: Int = 15

    scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
    sum: Int = 15

    Scala is a functional language. So we can treat function as a normal variable. If you try to assign a function to a new variable, the function will be invoked and the result will be assigned to the variable. This confusion occurs due to the optional braces for method invocation. We should use _ after the function name to assign it to another variable.

    class Test {
      def fun = {
        // some code
      }
      val funLike = fun _
    }

    --------------------------------------------
    HIGH ORDER FUNCTION
    --------------------------------------------
    1)
    scala> def logStart() = "-" * 50 + "\nStarting NOW\n" + "*" * 50
    logStart: ()String

    scala> val start: () => String = logStart
    start: () => String = <function0>

    scala> println( start() )

    --------------------------------------------------
    Starting NOW
    **************************************************



    2)
    scala> def safeStringOp(s: String, f: String => String) = {
         |  if (s != null) f(s) else s
         | }
    safeStringOp: (s: String, f: String => String)String //f:String is func

    scala> def reverser(s: String) = s.reverse
    reverser: (s: String)String

    scala> safeStringOp(null, reverser)
    res2: String = null

    scala> safeStringOp("7HILLS", reverser)
    res3: String = SLLIH7

    ------------------------------------
    String literals
    ------------------------------------

    scala> val greater=(name:String)=>s"hello$name"
    greater: String => String = <function1>

    scala> val great= greater(7hills)
    <console>:1: error: Invalid literal number
    val great= greater(7hills)
                       ^

    scala> val great= greater("7hills")
    great: String = hello7hills


    scala> val great= greater("7hills")
    great: String = hello7hills

    scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
    tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int

    scala> tripleOp(23, 92, 14, _ * _ + _)
    res4: Int = 2130

    scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
    tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B

    scala> ripleOp[Int,Int](23, 92, 14, _ * _ + _)
    <console>:12: error: not found: value ripleOp
           ripleOp[Int,Int](23, 92, 14, _ * _ + _)
           ^

    scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
    res6: Int = 2130

    scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
    res7: Int = 2130

    scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
    res8: Double = 0.017857142857142856

    ********************************************************************************

    more collections
    ***********************************

    ------------
    List
    -----------

    scala>  val numbers = List(32, 95, 24, 21, 17)
    numbers: List[Int] = List(32, 95, 24, 21, 17)

    scala> numbers[0]
    <console>:1: error: identifier expected but integer literal found.
    numbers[0]
            ^

    scala> numbers(0)
    res9: Int = 32

    scala> numbers(n)
    <console>:13: error: not found: value n
           numbers(n)
                   ^

    scala> numbers(4)
    res11: Int = 17


    scala> val colors=list("red","green","yeelow")
    <console>:12: error: too many arguments for method apply: (n: Int)Int in trait LinearSeqOptimized
           val colors=list("red","green","yeelow")
                          ^

    scala> val colors=List("red","green","yeelow")
    colors: List[String] = List(red, green, yeelow)


    scala> colors.size
    res2: Int = 3


    scala> colors.head
    res5: String = red

    scala> colors.tail
    res6: List[String] = List(green, yellow)


    scala> colors.<press TAB>    //now it will display predefined functions with

    scala> . <press TAB>
    ++              flatMap              min                 sortBy        
    ++:             flatten              minBy               sortWith      
    +:              fold                 mkString            sorted        
    /:              foldLeft             nonEmpty            span          
    :+              foldRight            orElse              splitAt      
    ::              forall               padTo               startsWith    
    :::             foreach              par                 stringPrefix  
    :\              genericBuilder       partition           sum          
    WithFilter      groupBy              patch               tail          
    addString       grouped              permutations        tails        
    aggregate       hasDefiniteSize      prefixLength        take          
    andThen         hashCode             product             takeRight    
    apply           head                 productArity        takeWhile    
    applyOrElse     headOption           productElement      to            
    canEqual        indexOf              productIterator     toArray      
    collect         indexOfSlice         productPrefix       toBuffer      
    collectFirst    indexWhere           reduce              toIndexedSeq  
    combinations    indices              reduceLeft          toIterable    
    companion       init                 reduceLeftOption    toIterator    
    compose         inits                reduceOption        toList        
    contains        intersect            reduceRight         toMap        
    containsSlice   isDefinedAt          reduceRightOption   toSeq        
    copyToArray     isEmpty              repr                toSet        
    copyToBuffer    isTraversableAgain   reverse             toStream      
    corresponds     iterator             reverseIterator     toString      
    count           last                 reverseMap          toTraversable
    diff            lastIndexOf          reverse_:::         toVector      
    distinct        lastIndexOfSlice     runWith             transpose    
    drop            lastIndexWhere       sameElements        union        
    dropRight       lastOption           scan                unzip        
    dropWhile       length               scanLeft            unzip3        
    endsWith        lengthCompare        scanRight           updated      
    equals          lift                 segmentLength       view          
    exists          map                  seq                 withFilter    
    filter          mapConserve          size                zip          
    filterNot       max                  slice               zipAll        
    find            maxBy                sliding             zipWithIndex



    ----------------------------------------------
    TO SEE THE FULL SYNTAX OF FUNCTION
    -------------------------------------

    scala> values.sortBy <press TAB>
       def sortBy[B](f: Int => B)(implicit ord: scala.math.Ordering[B]): List[Int]

    scala> values.sum <press Enter>
       def sum[B >: Int](implicit num: Numeric[B]): B

    scala> def sum[B >: Int](implicit num: Numeric[B]): B
         | = 5 + 10
    sum: [B >: Int](implicit num: Numeric[B])B

    scala> sum
    res14: Int = 15

    scala> list.map<press TAB>
    map   mapConserve

    scala> list.map<press TAB>

    final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
    def map[B](f: Int => B): scala.collection.TraversableOnce[B]



    ----------------------------------------------------------
    CREATING A LIST BY USING CONS(::) OPERATOR
    ------------------------------------------------------------
    scala> val list2=5::2::6::
    <console>:11: error: value :: is not a member of Int
           val list2=5::2::6::
                         ^

    scala> val list2=5::2::6::nill
    <console>:11: error: not found: value nill
           val list2=5::2::6::nill
                              ^

    scala> val list2=5::2::6::Nill
    <console>:11: error: not found: value Nill
           val list2=5::2::6::Nill
                              ^

    scala> val list2= 5 :: 2 :: 6 :: Nill
    <console>:11: error: not found: value Nill
           val list2= 5 :: 2 :: 6 :: Nill
                                     ^

    scala> val list2= 5 :: 2 :: 6 :: Nil
    list2: List[Int] = List(5, 2, 6)

    scala> val list3=5::2::6::Nil
    list3: List[Int] = List(5, 2, 6)


    scala> list3
    res18: List[Int] = List(5, 2, 6)

    scala> list3::10
    <console>:13: error: value :: is not a member of Int
           list3::10
                ^

    scala> list2
    res20: List[Int] = List(5, 2, 6)


    ----------------------------------
    append the data to list
    -----------------------------------
    scala> list2
    res103: List[Int] = List(1, 7, 3, 4, 2)

    NOTE :list can shows updated but not applay the original list

    scala> list2::2
    <console>:13: error: value :: is not a member of Int
           list2::2
                ^

    scala> list2:+2
    res105: List[Int] = List(1, 7, 3, 4, 2, 2)

    scala> 10+:list2:+2
    res106: List[Int] = List(10, 1, 7, 3, 4, 2, 2)

    scala> list2.sorted
    res107: List[Int] = List(1, 2, 3, 4, 7)

    NOTE: IF YOU WANT UPDATE BASE WE SHOULD FOLLOW THE BELOW


    scala> list2.sorted
    res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)

    scala> val list2=List(1, 7, 3, 4, 2, 2):+6
    list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)

    scala> list2.sorted
    res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)

    ERRORS:
    -------
    scala> val list2= list2:+2
    <console>:12: error: recursive value list2 needs type
           val list2= list2:+2
                      ^

    scala> val list2= val list2:+2
    <console>:1: error: illegal start of simple expression
    val list2= val list2:+2
               ^

    scala> val list2=list2:+2
    <console>:12: error: recursive value list2 needs type
           val list2=list2:+2







    ---------------------------------
    append one list to another list  (:::)
    ----------------------------------
    scala> list2:::list3
    res21: List[Int] = List(5, 2, 6, 5, 2, 6)



    -----------------------------------------
    comparision of two lists  (if correct return true)
    -----------------------------------------

    scala> list2 == list3
    res35: Boolean = true


    scala> val list= List(5, 2, 6, 5, 2, 6)
    list: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list == list3
    res37: Boolean = false

    scala> list.distinct
    res39: List[Int] = List(5, 2, 6)



    ---------------------------------------------------
    cut the data from list using DROP COMMAND
    ---------------------------------------------------
    scala> val later=List('a','v','s','e')
    later: List[Char] = List(a, v, s, e)

    scala> leter.drop 2
    <console>:1: error: ';' expected but integer literal found.
    leter.drop 2
               ^

    scala> leter drop 2
    <console>:12: error: not found: value leter
           leter drop 2
           ^

    scala> leter.drop 2
    <console>:1: error: ';' expected but integer literal found.
    leter.drop 2
               ^

    scala> leter.
         | drop
    <console>:12: error: not found: value leter
           leter.
           ^

    scala> later drop 2
    res43: List[Char] = List(s, e)

    scala> later drop 1
    res45: List[Char] = List(v, s, e)

    scala> later drop 0
    res47: List[Char] = List(a, v, s, e)


    ----------------------------------------
    filter the data by using filter command
    -----------------------------------------

    scala> later drop 0
    res47: List[Char] = List(a, v, s, e)

    scala> later filter(_>5)
    res48: List[Char] = List(a, v, s, e)

    scala> list3 filter(_>5)
    res49: List[Int] = List(6)


    -------------------------------------------
    flatten operator
    ------------------------------------------
    scala> List(list2,list3).flatten
    res50: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> List(list2,list).flatten
    res51: List[Int] = List(5, 2, 6, 5, 2, 6, 5, 2, 6)

    scala> List(list2,later).flatten
    res52: List[AnyVal] = List(5, 2, 6, a, v, s, e)


    --------------------------------------------------
    PARTITION THE GIVEN LIST
    --------------------------------------------------
    scala> list
    res57: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list partition(_<2)
    res58: (List[Int], List[Int]) = (List(),List(5, 2, 6, 5, 2, 6))

    scala> list partition(_>2)
    res59: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))

    scala> list partition(_>3)
    res60: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))

    scala> list partition(_>4)
    res61: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))



    ------------------------------------------------------
    TO REVERSE THE GIVEN LIST
    -------------------------------------------------------
    scala> list.reverse
    res62: List[Int] = List(6, 2, 5, 6, 2, 5)

    scala> list2.reverse
    res63: List[Int] = List(6, 2, 5)

    scala> list1.reverse
    <console>:12: error: not found: value list1
           list1.reverse
           ^

    scala> list3.reverse
    res65: List[Int] = List(6, 2, 5)


    -------------------------------------------------
    SLICE(CUT) THE  GIVEN LIST BY USING SLICE COMMAND
    --------------------------------------------------
    scala> list
    res67: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list slice(1,3)
    res66: List[Int] = List(2, 6)


    scala> val sli=list slice(1,3)
    sli: List[Int] = List(2, 6)

    scala> sli
    res68: List[Int] = List(2, 6)

    note: it first position is called one not zero.


    ----------------------------------------------------
    sorting the list by using sort  and sortBy
    -------------------------------------------------------

    sort the integer list by usind <.sorted> command (by the list of natural values)
    ------------------------------------------------
    scala> list.sorted
    res74: List[Int] = List(2, 2, 5, 5, 6, 6)

    sort the char list by size using < sortedBy> command don't give the .<dot>
    -----------------------------------------------------------------------

    scala> List("apple", "to") sortBy(_.size)
    res71: List[String] = List(to, apple)





    -----------------------------------------------------------------------
    split the given list by using splitAt <position>/<int>
    ----------------------------------------------------------------------


    scala> list
    res83: List[Int] = List(5, 2, 6, 5, 2, 6)


    scala> list splitAt2
    <console>:13: error: value splitAt2 is not a member of List[Int]
           list splitAt2
                ^

    scala> list splitAt 2
    res78: (List[Int], List[Int]) = (List(5, 2),List(6, 5, 2, 6))

    scala> list splitAt 3
    res79: (List[Int], List[Int]) = (List(5, 2, 6),List(5, 2, 6))

    scala> list splitAt 4
    res80: (List[Int], List[Int]) = (List(5, 2, 6, 5),List(2, 6))

    scala> list splitAt 5
    res81: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2),List(6))

    scala> list splitAt 6
    res82: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2, 6),List())




    --------------------------------------------------------------------
    Extracts the n elmenents by using command:take <int/value> command
    --------------------------------------------------------------------

    syntax: list take <int/value>
    ------
    scala> list
    res83: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list taken 3
    <console>:13: error: value taken is not a member of List[Int]
           list taken 3
                ^

    scala> list take 3
    res85: List[Int] = List(5, 2, 6)

    scala> list take 6
    res86: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list take 4
    res87: List[Int] = List(5, 2, 6, 5)


    ----------------------------------------------------------------------
    combines two list in tuple point of view by using zip command
    ----------------------------------------------------------------------

    syntax: <list1> zip <list2>
    -------
    scala> list2
    res91: List[Int] = List(5, 2, 6)

    scala> list2 zip list3
    res90: List[(Int, Int)] = List((5,5), (2,2), (6,6))

    scala> list3
    res92: List[Int] = List(5, 2, 6)

    scala> List(1, 2) zip List("a", "b")
    res88: List[(Int, String)] = List((1,a), (2,b))

    scala> val list4=List(1, 2) zip List("a", "b")
    list4: List[(Int, String)] = List((1,a), (2,b))

    scala> list4
    res89: List[(Int, String)] = List((1,a), (2,b))

    scala> li
    res2: List[Int] = List(1, 2, 3)

    scala> val lis=List(5,6)
    lis: List[Int] = List(5, 6)

    scala> li zip lis
    res4: List[(Int, Int)] = List((1,5), (2,6))

    note:in zip function work as one to one mapping only if it has any other variables are there it's leav that.



    -----------------------------------------------------------------------
    Drop some values in our list by using
    ------------------------------------------------------------------------

    scala> list2
    res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)



    scala> suffix
    res113: List[Int] = List(4, 6, 7)

    scala> val drop = list2 dropRight 2
    drop: List[Int] = List(1, 2, 2, 3, 4)

    scala> list2
    res114: List[Int] = List(1, 2, 2, 3, 4, 6, 7)

    scala> drop
    res115: List[Int] = List(1, 2, 2, 3, 4)

    Errors:
    -------
    scala> val drop = list2 dropmid 2
    <console>:12: error: value dropmid is not a member of List[Int]
           val drop = list2 dropmid 2
                            ^

    scala> val drop = list2 dropleft 2
    <console>:12: error: value dropleft is not a member of List[Int]
           val drop = list2 dropleft 2
                            ^

    scala> val drop = list2 dropstart 2
    <console>:12: error: value dropstart is not a member of List[Int]
           val drop = list2 dropstart 2



    ---------------------------------------------------------------------
    takeRight command for taking the right side values in our list
    ------------------------------------------------------------------------

    synatax:  <list> <takeRight> <value/int>
    -----------
    NOTE: In takeRight command take the values in our list based on our list
    -----  so you can check what is what data you have in list
           perhaps you are sorted with out update the original list.it will perform
           based on original list.

    scala> list2
    res108: List[Int] = List(1, 7, 3, 4, 2, 2, 5)

    scala> list2.sorted
    res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)

    scala> val list2=List(1, 7, 3, 4, 2, 2):+6
    list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)


    scala> list2.sorted
    res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)

    scala> val suffix = list2 takeRight 3   *** because original list not modified**
    suffix: List[Int] = List(2, 2, 6)

    scala> list2
    res111: List[Int] = List(1, 7, 3, 4, 2, 2, 6)  **base/original list ****

    scala> list2
    res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)

    scala> val suffix = list2 takeRight 3
    suffix: List[Int] = List(4, 6, 7)


    scala> suffix
    res113: List[Int] = List(4, 6, 7)


    ----------------------------------------------------------
    MAP
    -------------------------------------------------------------

    note:zip is

    scala> val list2= List(1, 7, 3, 4, 2, 2, 6)
    list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)

    scala> val list4=List(1, 2) zip List("a", "b")
    list4: List[(Int, String)] = List((1,a), (2,b))

    scala> val list= List(5, 2, 6, 5, 2, 6)
    list: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> val list3= List(1, 7, 3)
    list3: List[Int] = List(1, 7, 3)

    scala> val list4= List(1, 7, 3,9,9,10)
    list4: List[Int] = List(1, 7, 3, 9, 9, 10)

    scala> val listchar = List("7hills","hi","how","are","you")
    listchar: List[String] = List(7hills, hi, how, are, you)

    scala> val list6=List(8,10,5)
    list6: List[Int] = List(8, 10, 5)



    MAPPING OPERATION
    ------------------

    COLLECT:
    --------

    Description
    ------------
    Transforms each element using a partial function, retaining applicable elements


    scala> val list6=List(8,10,5)
    list6: List[Int] = List(8, 10, 5)

    scala> val mapc= List(0, 1, 0) collect {case 1 => "ok"}
    mapc: List[String] = List(ok)

    scala> mapc
    res0: List[String] = List(ok)

    scala> val mapc= List4 collect {case 1 => "ok"  //Does not work list4
         | case 2=> "this value is 7"  you should give complete
         | case 4=>"this value is 9"} list of values
    <console>:11: error: not found: value List4
           val mapc= List4 collect {case 1 => "ok"
                     ^

    scala> val mapc= List4.collect {case 1 => "ok"
         | }
    <console>:11: error: not found: value List4
           val mapc= List4.collect {case 1 => "ok"
                     ^

    scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => "this is index 1"
         | case 2=> "this value is 7" //here return string type
         | case 4=>"this value is 9"} List[String] = List(this is index 1)
    mapc: List[String] = List(this is index 1)

    scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => println("this is index 1")
         | case 2=> println("this value is 7")  //here println convert unit() type
         | case 4=> println("this value is 9")} List[Unit] = List(())
    this is index 1
    mapc: List[Unit] = List(())


    ------------
    flatmap
    ---------
    scala> list.flatMap

    final override def flatMap[B, That](f: Int =>scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
    def flatMap[B](f: Int => scala.collection.GenTraversableOnce[B]): scala.collection.TraversableOnce[B]

    scala> List("milk,tea") flatMap (_.split('|')
         | )
    res5: List[String] = List(milk,tea)

    scala> val mapfla=List("milk,tea") flatMap (_.split('|'))
    mapfla: List[String] = List(milk,tea)

    scala> mapfla
    res6: List[String] = List(milk,tea)

    the flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list.
    Fortunately(adrustam), Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level,
    a list of string arrays can be safely flattened into a list of strings.

    scala> def flatten(ls: List[Any]): List[Any] = ls flatMap {
         |   case i: List[_] => flatten(i)
         |   case e => List(e)
         | }
    flatten: (ls: List[Any])List[Any]

    scala> val k = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)
    k: List[Any] = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)

    scala> flatten(k)
    res26: List[Any] = List(1, 2, 3, 4, 5, 6, 7, 8)

    ------------
    map
    -------------
    Desription: Transforms each element using the given function
    -----------
    scala> list.map<press TAB>
    map   mapConserve

    scala> list.map<press TAB>

    final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
    def map[B](f: Int => B): scala.collection.TraversableOnce[B]

    scala> List("milk","tea") map (_.toUpperCase)
    res7: List[String] = List(MILK, TEA)

    ----------------
    reduce operation
    -----------------
    SCALA REDUCE OPERATIONS  ARE FOLLWS

    1) MATH OPERATIONS

    2)BOOLEAN OPERATIONS

    3) HIGHOREDER OPERATIONS

    -----------------
    MATH OPERATIONS
    ----------------
    max,min,sum,product

    scala> list
    res13: List[Int] = List(5, 2, 6, 5, 2, 6)

    scala> list.max
    res15: Int = 6

    scala> list.min
    res16: Int = 2

    scala> list.sum
    res17: Int = 26

    scala> list.product
    res18: Int = 3600

    ---------------------
    BOOLEAN OPERATOR
    --------------------
    IF CONDATION/CHECKING VALUE IS THERE IT DISPLAYS TRUE

    <list> contains <value> ,endswith,exists,forall,startwith

    contains:Checks if the list contains this element

    scala> list4 contains 6
    res11: Boolean = false


    endsWith:Checks if the list ends with a given list

    scala> list4 endsWith list4(10)
    <console>:13: error: type mismatch;
     found   : Int
     required: scala.collection.GenSeq[?]
           list4 endsWith list4(10)
                               ^

    scala> list4 endsWith List(9,10)
    res10: Boolean = true

    exists:Checks if a predicate holds true for at least one element in the list.

    scala> list4 exists (_<0)
    res14: Boolean = false


    scala> list4 exists (_<10)
    res12: Boolean = true

    forall:Checks if a predicate holds true for every element in the list.
    ------
    scala> list4
    res21: List[Int] = List(1, 7, 3, 9, 9, 10)

    scala> list4 forall (_<11)
    res22: Boolean = true

    scala> list4 forall (_<21)
    res23: Boolean = true

    scala> list4 forall (_<2)
    res24: Boolean = false


    scala> list4 forall (_<9)
    res15: Boolean = false

    scala> list4 forall (_<25)
    res16: Boolean = true

    scala> list4 forall (_<2,5)
    <console>:13: error: too many arguments for method forall: (p: Int => Boolean)Boolean
           list4 forall (_<2,5)
                 ^

    scala> list4 forall (_<2 5)
    <console>:1: error: ')' expected but integer literal found.
    list4 forall (_<2 5)
                      ^

    scala> list4 forall (_<10)
    res18: Boolean = false

    scala> list4 forall (_<15)
    res19: Boolean = true

    scala> list4 forall (_<78)
    res20: Boolean = true


    StartWith:Tests whether the list starts with a given list.
    ----------

    scala> list4 startsWith List(1)
    res26: Boolean = true

    scala> list4 startsWith List(8)
    res27: Boolean = false

    scala> list4 startsWith list4   //internally calls first element to other list 1st element
    res29: Boolean = true


    Error: here Genseq[] meens you should type List
    -----
    scala> list4 startsWith list(8)
    <console>:14: error: type mismatch;
     found   : Int
     required: scala.collection.GenSeq[?]
           list4 startsWith list(8)

    -----------------------------------------------------
    converting collections
    -----------------------------------------------------

    mkString; Renders a collection to a Set using the given delimiters

    scala> listchar
    res30: List[String] = List(7hills, hi, how, are, you)


    scala> val listchar2=listchar.mkString("|")
    listchar2: String = 7hills|hi|how|are|you

    toBuffer; Converts an immutable collection to a mutable one.


    scala> List('f', 't').toBuffer
    res37: scala.collection.mutable.Buffer[Char] = ArrayBuffer(f, t)

    scala> listchar.toBuffer
    res38: scala.collection.mutable.Buffer[String] = ArrayBuffer(7hills, hi, how, are, you)


    toList:Converts a collection to a List.
    ------
    scala> val map=Map(1->"hi",2->"hello")
    map: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)

    scala> map.toList
    res45: List[(Int, String)] = List((1,hi), (2,hello))

    toMap;converts a collection to a Map
    -----
    scala> val maplist=List(1->"hi",2->"hello")
    maplist: List[(Int, String)] = List((1,hi), (2,hello))

    scala> maplist.toMap
    res43: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)

    scala> maplist
    res44: List[(Int, String)] = List((1,hi), (2,hello))

    scala> val cmaplist=maplist.toMap
    cmaplist: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)


    toSet:Converts a collection to a Set.
    -----

    scala> map.toSet
    res46: scala.collection.immutable.Set[(Int, String)] = Set((1,hi), (2,hello))


    scala> list4.toSet
    res47: scala.collection.immutable.Set[Int] = Set(10, 1, 9, 7, 3)


    toString: Renders a collection to a String, including the collection’s type.
    --------

    scala> list4
    res50: List[Int] = List(1, 7, 3, 9, 9, 10)

    scala> list4.toString
    res51: String = List(1, 7, 3, 9, 9, 10)

    scala> map
    res49: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)

    scala> map.toString
    res48: String = Map(1 -> hi, 2 -> hello)


    -----------------------------------------------------------
    pattern matching with collection
    -------------------------------------------------------------

    scala> val colpatern=list4.head match{
         | case x if(x == 1)=> "head value is 1"
         | case x if(x == 9)=> " middle value"
         | }
    colpatern: String = head value is 1

    scala>  val colpatern=list4 match{
         | case List(9,9) => "not "
         | case List(1, 7, 3, 9, 9, 10) => "not found & error"
         | }
    colpatern: String = not found & error




    DOUBT: HOW TO CHECK EACH VALUE THROUGH MATCHING PATTERN BY THE SINGLE VALUE.



    *******************************************************************************

    MORE COLLECTIONS
    *******************************

    We’ll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then we’ll move on to arrays, streams, and other collections


    ---------------------------------------
    Mutable Collections
    ---------------------------------------

    Mutable collection types
    --------------------------

    collection.mutable.Buffer:
    --------------------------


    scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
    m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)

    scala>  val b = m.toBuffer
    b: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((AAPL,597), (MSFT,40))

    scala>  b trimStart 1

    scala> b
    res55: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((MSFT,40))

    scala> m
    res56: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)

    scala> b += ("GOOG" -> 521)
    res57: b.type = ArrayBuffer((MSFT,40), (GOOG,521))

    scala> val n = b.toMap
    n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)



    --------------------
    Arrays
    --------------------


    scala> val colors = Array("red", "green", "blue")
    colors: Array[String] = Array(red, green, blue)

    scala> colors(0) = "purple"

    scala> colors
    res59: Array[String] = Array(purple, green, blue)

    scala> colors :+ "red"
    res60: Array[String] = Array(purple, green, blue, red)

    scala> colmut trimStart 1

    scala> colmut
    res74: scala.collection.mutable.Buffer[String] = ArrayBuffer(green, blue)


    ----------------------------------
    Seq and Sequences
    ----------------------------------

    scala> val inks = Seq('C','M','Y','K')
    inks: Seq[Char] = List(C, M, Y, K)

    scala> inks(0)
    res75: Char = C

    scala> inks(1)
    res76: Char = M

    scala> inks(4)
    java.lang.IndexOutOfBoundsException: 4
      at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
      at scala.collection.immutable.List.apply(List.scala:84)
      ... 32 elided

    scala> inks(3)
    res78: Char = K


    -------------------------
    Using Collection Builder
    -----------------------------
    scala> val b = Set.newBuilder[Char]
    b: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2

    scala> b += 'h'
    res0: b.type = scala.collection.mutable.SetBuilder@6debcae2

    scala> b ++= List('e', 'l', 'l', 'o')
    res1: b.type = scala.collection.mutable.SetBuilder@6debcae2

    scala> b
    res2: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2

    scala> val helloSet = b.result
    helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o)

    scala> b.result
    res3: scala.collection.immutable.Set[Char] = Set(h, e, l, o)

    ------------------------------
    ARRAY
    ------------------------------
    scala>  def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1))
    inc: (i: Int)Stream[Int]

    scala> val s = inc(1)
    s: Stream[Int] = Stream(1, ?)


    *****************************************************************************

    Object-Oriented Scala



    --------------------------------------------------------------------------------------------------------------------
    Classes
    ---------------------------------------------------------------------------------------------------------------------


    Design the user class in 2 ways
    --------------------------

    1)scala> class hills
    defined class hills

    scala> val u= new hills
    u: hills = hills@77681ce4

    2)scala> class username{
    val name="7hills"
    val pass="777"
    def greet:String=s"hello $name"
    override def toString=s"username($name)"
    }
    defined class username

    scala> val u = new username()
    u: username = username(7hills)


    scala> val u = new username
    u: username = username(7hills)

    scala> println(u.greet)
    hello 7hills

    scala> class hills{
         | val nn=10
         | def greet:String = s"this no is $nn"
         | override def toString=s"hills($nn)"
         | }
    defined class hills


    scala> val u = new hills()
    u: hills = hills(10)

    scala> u.greet
    res1: String = this no is 10

    scala> println(u.greet)
    this no is 10

    ------------------------------------------
    CREATING A CLASS FOR USER AUTHENTICATION
    ------------------------------------------

    (1) METHOD -1
    ==============
    class username(pass:String) {
    val name="7hills"
    def greet:String=s"your name $name"
    println("enter your password")
    if(pass=="7777"){
    println("hi" + name  + "your")
    println("successfully login")
    }
    else
    println("you entered wrong password")
    }

    output:
    -------
    CREATE OBJECT ON CLASS
    -----------------------
    scala> val u= new username("7777")
    enter your password
    hi7hillsyour
    successfully login
    u: username = username@1b96f15e


    output:
    -------
    scala> val u= new username("8888")
    enter your password

    you entered wrong password
    u: username = username@1f22d7e4


    class username {
    val name="7hills"
    def greet:String=s"your name $name"
    def passvalidat(pass:String) = {
    println("enter your password")
    if(pass=="7777"){
    println("hi" + name  + "your")
    println("successfully login")
    }
    else
    println("you entered wrong password")
    }
    }


    output:
    -------
    scala> u.passvalidat("7777")
    hi7hillsyour
    successfully login

    scala> u.passvalidat("777")
    you entered wrong password


    scala> val u=new username()
    u: username = username@addefe5

    scala>

    scala> u.passvalidat("7777")
    enter your password
    hi7hillsyour
    successfully login

    --------------------

    class username {
    val name="7hills"
    def greet:String=s"your name $name"
    def passvalidat(pass:String) = {
    println("enter your password")
    if(pass=="7777"){
    println("hi" + name  + "your")
    println("successfully login")
    }
    else
    println("you entered wrong password")
    }
    }


    (2) METHOD - 2
    ================

    class usr(name:String) {
          val para = name
           para + "the val is"
         def passvali = {
         if(para == "555")
          para + "your are sucessfully login"
        else
         println(" you entered wrong password")
        }
        }

    scala> val u= new usr("896")   //create the object on class
    u: usr = usr@3956ae6e

    scala> u.passvali //call the defined function
     you entered wrong password
    res29: Any = ()

    NOTE: YOU SHOULD CALL FIRST IN PARA VARIABLE
    -----
    scala> u.para // call the name parameter
    res30: String = 896

    scala> val u= new usr("555") //
    u: usr = usr@406b6988

    scala> u.para
    res31: String = 555

    scala> u.passvali
    res32: Any = 555your are sucessfully login

    (3) METHOD - 2
    ================

    class usr(name:String) {
          val para = name
           para + "the val is"
         def passvali = {
         if(para == "555")
          para + "your are sucessfully login"
        else
         println(" you entered wrong password")
        }
       }

    class calling extends usr{override def passvali="your name" + super.para + super.passvali}

    -------------------------------------------------------------

    INNER CLASS CALLING  (or) call one class to another class
    -----------------------------------------------------------------
    BASE CLASS:
    ------------
    scala> class usr(name:String) {
         |       val para = name
         |        para + "the val is"
         |      def passvali = {
         |      if(para == "555")
         |       para + "your are sucessfully login"
         |     else
         |      println(" you entered wrong password")
         |     }
         |    }

    defined class usr

    ----------
    CHILD CLASS
    -----------

    scala> class calling extends usr("7777"){override def passvali="your name" + super.passvali}

    defined class calling

    scala> val cals = new calling().passvali
     you entered wrong password
    cals: String = your name()


    scala> class calling extends usr("555"){override def passvali="your name" + super.passvali}
    defined class calling

    scala> val cals = new calling().passvali
    cals: String = your name555your are sucessfully login


    ERRORS:
    -------
    scala> class calling extends use{override def passvali="your name" + super.para + super.passvali}
    <console>:11: error: not found: type use
           class calling extends use{override def passvali="your name" + super.para + super.passvali}
                                 ^
    <console>:11: error: value para is not a member of AnyRef
           class calling extends use{override def passvali="your name" + super.para + super.passvali}
                                                                               ^
    <console>:11: error: value passvali is not a member of AnyRef
           class calling extends use{override def passvali="your name" + super.para + super.passvali}
                                                                                            ^

    scala> class calling extends usr{override def passvali="your name" + super.para + super.passvali}
    <console>:13: error: not enough arguments for constructor usr: (name: String)usr.
    Unspecified value parameter name.
           class calling extends usr{override def passvali="your name" + super.para + super.passvali}
                                 ^

    scala> class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
    <console>:13: error: super may not be used on value para
           class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
                                                                                       ^

    ------------
    SOLUTION
    -----------
    1) YOU SHOULD PASS THE PARAMETERS IN BASE WHEN CALLING THROUG CHILD CLASS ALSO

    2)YOU SHOULD NOT CALL VARIABLES/VALUES THROUGH SUPER KEYWORD.WE KNOW SUPER KEYWORD USING ONLY CALLS THE PARENT/BASE CLASS METHODS/FUNCTIONS.


    ANOTHER EXAMPLE
    ----------------
    scala> class A { def hi = "Hello from A"  
         | override def toString = getClass.getName }
    defined class A

    scala> class B extends A
    defined class B

    scala> class C extend B{ override def hi = "hi C -> " + super.hi }


    scala> val hiA= new A().hi
    hiA: String = Hello from A

                 OR
    scala> hiA
    res35: String = Hello from A


    scala> val hiB= new B().hi
    hiB: String = Hello from A

    scala> val hiC= new C().hi
    hiC: String = hi C -> Hello from A


    Let’s try creating a class with both value and variable fields as parameters:
    ----------------------------------------------------------------------------

    scala> class Car(val make: String, var reserved: Boolean) {
         |  def reserve(r: Boolean): Unit = { reserved = r } }
    defined class Car

    scala> val t = new Car("Toyota", false)
    t: Car = Car@e283976

    scala>  t.reserve(true)

    scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
    My Toyota is now reserved? true


    scala>  t.reserve(false)

    scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
    My Toyota is now reserved? false


    NOTE:WE CAN NOT CHANGE THE make value because it is val.


    Class with Input Parameters and Default Values
    -----------------------------------------------










    ---------------------------------------------------------
    ABSTRACT CLASS
    ---------------------------------------------------------
    scala> abstract class car{
         | val year:Int
         | val automatic:Boolean = true
         | def Color:String
         | }
    defined class car

    scala> new car
    <console>:13: error: class car is abstract; cannot be instantiated
           new car
           ^

    scala> class red(val year:Int) extends car
    <console>:12: error: class red needs to be abstract, since method Color in class car of type => String is not defined
           class red(val year:Int) extends car
                 ^

    scala> class red(val year:Int) extends car{
         | val Color = "red"
         | }
    defined class red

    scala> val m=new red(2017)
    m: red = red@3ada9e37

    scala> m.Color
    res1: String = red

    scala> m.year
    res2: Int = 2017

    scala> m.automatic
    res3: Boolean = true

    -------------------------------------------------------------------------
    Anonymous Classes (OR) write the function operation when call the method
    -----------------------------------------------------------------------
    We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation.


    scala> abstract class Listener { def trigger }
    defined class Listener

    scala> class Listening {  var listener: Listener = null
         | def register(l: Listener) {listener = l }
         | def sendNotification() { listener.trigger }
         | }
    defined class Listening

    scala>  val notification = new Listening()
    notification: Listening = Listening@4671e53b

    scala> notification.register(new Listener {
         | def trigger { println(s"Trigger at ${new java.util.Date}") }
         | })

    scala> notification.sendNotification
    Trigger at Sat Sep 02 18:44:10 IST 2017



    Error:
    ---------
    <console>:14: error: reassignment to val
           listenerv=l}
                    ^

    note:you can not assign the value to val type variable because it is immutable



    -------------------------------------------------------
    Overloaded Methods
    ------------------------------

    Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:

    (1)

    scala> class Printer(msg: String) {
         | def print(s:String):Unit = println(s"$msg: $s")
         | def print(l:Seq[String]):Unit = print(l.mkString(","))
         | }
    defined class Printer

    scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
    hi this: this,is,fizzy
    p: Unit = ()

    (2)

    scala> class Printer(msg: String) {
         | def print(s:String):Unit = println(s"$msg: $s")
         | def print(l:Seq[String]):Unit = println(l.mkString(","))
         | }
    defined class Printer


    scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
    this,is,fizzy
    p: Unit = ()


    note; 1st  method use println only but we use two methods in println statement 2nd method only use print statement

    Errors
    ------

    scala> val p = new Printer("hi this").print("this"::"is"::"fizzy")
    <console>:12: error: value :: is not a member of String
           val p = new Printer("hi this").print("this"::"is"::"fizzy")
                                                            ^
    sol:end with Nil

    ---------------------
    Apply Methods
    ----------------------

    scala> class Multiplier(factor: Int) {
         | def applay(f:Int) = factor * f}
    defined class Multiplier

    scala> val mul=new Multiplier(5)
    mul: Multiplier = Multiplier@79d7035

    scala> mul
    res11: Multiplier = Multiplier@79d7035

    scala> mul.applay(5)
    res12: Int = 25

    scala> val mul=new Multiplier(6)
    mul: Multiplier = Multiplier@25791d40


    scala> val mu=new Multiplier(6).applay(5)
    mu: Int = 30

    ------------------------------------------
    Accessing Packaged Classes
    --------------------------------------

    scala> import collection.immutable._
    import collection.immutable._

    scala> import collection.mutable._
    import collection.mutable._


    scala> val b = new ArrayBuffer[String]
    b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()

    scala> b += "hello"
    res15: b.type = ArrayBuffer(hello)

    scala> val q = new Queue[Int]
    q: scala.collection.mutable.Queue[Int] = Queue()

    scala> q += 10
    res16: q.type = Queue(10)



    scala> val l = new [Int]
    <console>:18: error: class List is abstract; cannot be instantiated
           val l = new List[Int]
                   ^

    scala> val l = List[Int]
    <console>:18: error: missing argument list for method apply in object List
    Unapplied methods are only converted to functions when a function type is expected.
    You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
           val l = List[Int]
                       ^
    scala> val q = new Queue[Int]
    q: scala.collection.mutable.Queue[Int] = Queue()

    scala> q.enqueue(3, 4, 5)

    scala> q.dequeue(3)
    <console>:20: error: too many arguments for method dequeue: ()Int
           q.dequeue(3)
                    ^

    scala> q.dequeue
    res19: Int = 3

    scala> q
    res20: scala.collection.mutable.Queue[Int] = Queue(4, 5)


    ---------------------------------------
    IMPORT ALIIAS
    -------------------------------------

    syntax:

    import <package>.{<original name>=><alias>}

    scala> import collection.mutable.{Map=>MutMap}
    import collection.mutable.{Map=>MutMap}

    scala> val m1 = Map(1 -> 2)
    m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)

    scala> val m2 = MutMap(2 -> 3)
    m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)

    scala> m2.remove(1)
    res21: Option[Int] = None

    scala> m2.remove(2)
    res22: Option[Int] = Some(3)

    scala> val m1 = Map(1 -> 2)
    m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)

    scala> val m2 = MutMap(2 -> 3)
    m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)

    scala> m2
    res24: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)

    scala> m1
    res25: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)

    scala> m1.remove(1)
    res26: Option[Int] = Some(2)

    scala> m1
    res27: scala.collection.mutable.Map[Int,Int] = Map()

    With our aliased collection “MutMap” (short for “mutable,” not “mutt”!) we can create both mutable and immutable maps by name, without specifying their packages


    -------------
    Objects
    -------------

    Syntax: Defining an Object
    object <identifier>/objectname [extends <identifier>] [{ fields, methods, and classes }]


    scala> object Hello { println("in Hello"); def hi = "hi" }
    defined object Hello

    scala> Hello
    in Hello
    res34: Hello.type = Hello$@557665b2

    scala> Hello.hi
    res35: String = hi

    scala> hi
    <console>:20: error: not found: value hi
           hi
           ^

    scala> Hello
    res37: Hello.type = Hello$@557665b2

    The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the object’s “hi” method reused the same global instance so there was no additional initialization


    ------------------------------------
    Case Classes
    -----------------------------------

    scala> case class Character(name:String,isthief:Boolean)
    defined class Character

    scala> val h=new Character("7hills",true)
    h: Character = Character(7hills,true)

    scala> val u=new Character("hills",false)
    u: Character = Character(hills,false)

    scala> h match{
         | case Character("x",true) => s"$x is theif"
         | case Character("x",true) => s"$x is nottheif"
         | }
    <console>:16: error: not found: value x
           case Character("x",true) => s"$x is theif"
                                          ^
    <console>:17: error: not found: value x
           case Character("x",true) => s"$x is nottheif"
                                          ^

    scala> h match{
         | case Character(x,true) => s"$x is nottheif"
         | case Character(x,false) => s"$x is nottheif"
         | }
    res1: String = 7hills is nottheif

    scala> h
    res2: Character = Character(7hills,true)

    scala> val r = h.Copy(name = "sarvan")
    <console>:14: error: value Copy is not a member of Character
           val r = h.Copy(name = "sarvan")
                     ^
    <console>:14: error: not found: value name
           val r = h.Copy(name = "sarvan")
                          ^

    scala> val r = h.copy(name = "sarvan")
    r: Character = Character(sarvan,true)

    scala> r == h
    res3: Boolean = false

    scala> h match{
         | case Character("x",true) => s"$x is theif"
         | case Character(x,true) => s"$x is not theif"
         | }
    <console>:16: error: not found: value x
           case Character("x",true) => s"$x is theif"
                                          ^

    scala> h match{
         | case Character(x,true) => s"$x is theif"
         | case Character(x,false) => s"$x is not theif"
         | }
    res5: String = 7hills is theif

    scala> r match{
         | case Character(x,true) => s"$x is theif"
         | case Character(x,false) => s"$x is not theif"
         | }
    res6: String = sarvan is theif

    scala> h
    res7: Character = Character(7hills,true)

    scala> r
    res8: Character = Character(sarvan,true)

    scala> val u=new Character("7hills",true)
    u: Character = Character(7hills,true)

    scala> h == u
    res9: Boolean = true

    scala> u match{
         | case Character(x,true) => s"$x is theif"
         | case Character(x,false) => s"$x is not theif"
         | }
    res10: String = 7hills is theif

    scala> val u=new Character("7hills",false)
    u: Character = Character(7hills,false)

    scala> u == h
    res11: Boolean = false

    scala> u match{
         | case Character(x,true) => s"$x is theif"
         | case Character(x,false) => s"$x is not theif"
         | }
    res12: String = 7hills is not theif


    ------------------------------------------------------------
    Traits
    ------------------------------------------------------------
    A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated


    The process of linearization may seem odd, but it’s a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesn’t. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime

    Another benefit of linearization is that you can write traits to override the behavior of a shared parent class


    scala> trait Base { override def toString = "Base" }
    defined trait Base

    scala> trait B extends Base { override def toString = "B->" + super.toString }
    defined trait B

    scala> trait C extends Base { override def toString = "B->" + super.toString }
    defined trait C


    scala> class D extends A with B with C { override def toString = "B->" + super.toString }
    defined class D

    scala> new D()
    res14: D = B->B->B->A->Base


    ERRoRS
    ------

    scala> class A extends Base { override def toString = "A->" + super.toString }
    defined class A

    scala> class B extends Base { override def toString = "B->" + super.toString }
    defined class B

    scala> class C extends Base { override def toString = "C->" + super.toString }
    defined class C

    scala> class D extends A with B with C{ override def toString = "D>" + super.toString }
    <console>:15: error: class B needs to be a trait to be mixed in
           class D extends A with B with C{ override def toString = "D>" + super.toString }
                                  ^
    <console>:15: error: class C needs to be a trait to be mixed in
           class D extends A with B with C{ override def toString = "D>" + super.toString }


    ---------
    solution: we only extennds the trait not the class
    ---------


    scala> trait D extends A with B with C { override def toString = "B->" + super.toString }
    defined trait D

    scala> new D()
    <console>:17: error: trait D is abstract; cannot be instantiated
           new D()
           ^


    ---------
    solution: we only instance/intiate(new) on class only but not on traits.so in this situvation
    --------- last D extends with trait and it also creat as a class not trait(you sould use class keyword instead on trait


    2nd example
    ----------
    scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
    defined class RGBColor

    scala> val green = new RGBColor(255 << 8).hex
    green: String = 00FF00

    scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
    defined trait Opaque

    scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
    defined trait Sheer



    ------------------------------
    Self Types
    ------------------------------

    A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type

    Syntax: Defining a Self Type
    trait ..... { <identifier>: <type> => .... }


    scala> class A { def hi = "hi" }
    defined class A

    scala> trait B { self: A => override def toString = "B: " + hi} //SHOULD BE MENTION self keyword and =>
    defined trait B

    scala> class C extends B
    <console>:13: error: illegal inheritance;
     self-type C does not conform to B's selftype B with A
           class C extends B
                           ^

    scala> class C extends A with B
    defined class C

    scala> new C()
    res15: C = B: hi


    1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. …
    2) but just to prove it, let’s try defining a class with trait B but without the requested class. No luck.
    3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
    4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.


    Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:

    scala> class A
    defined class A

    scala> trait B { self: A => }
    defined trait B

    scala>  val a = new A with B
    a: A with B = $anon$1@331ff3ac


    we created an instance where trait B extended trait A


    Let’s experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:

    scala> class User(val name: String){
         |  def suffix =""
         | override def toString = s"$name$suffix"
         | }
    defined class User

    scala> trait Attorney { self: User => override def suffix = ", esq." }
    defined trait Attorney

    scala> trait Wizard { self: User => override def suffix = ", Wizard" }
    defined trait Wizard

    scala>  trait Reverser { override def toString = super.toString.reverse }
    defined trait Reverser

    scala> val h = new User("Harry P") with Wizard
    h: User with Wizard = Harry P, Wizard

    scala> val g = new User("Ginny W") with Attorney
    g: User with Attorney = Ginny W, esq.

    ------------------------------------------
    Importing Instance Members
    ------------------------------------------

    scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
    defined class Receipt

    scala> {
         | val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")
         |  import latteReceipt._
         | println(s"Sold a $title for $amount to $who")
         | }
    Sold a Medium Latte for 4.12 to fred

    scala> import util.Random._ import util.Random._
    <console>:1: error: ';' expected but 'import' found.
    import util.Random._ import util.Random._
                         ^

    scala> import util.Random._
    import util.Random._

    scala> val letters = alphanumeric.take(20).toList.mkString
    letters: String = WoVkuFqZOdP0Snh00FeF

    scala> val numbers = shuffle(1 to 20)
    numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 8, 10, 18, 14, 11, 9, 16, 12, 2, 4, 7, 19, 17, 15, 13, 1, 3, 20, 6)



    ********************************************************************

    --------------------------------------------------------------
    SCALA distinct predefined method
    ---------------------------------------------------------------
    List[Int] = List(10, 20, 3, 6, 35)


    distinct
    -----------

    scala> list.distinct
       def distinct: List[Int]

    distinct explanation: def -----------> keyword for function
    --------------------  distinct -------> that is function name
     note: it should return list after function operation is completed that function open with { and colose with }


    scala> def distinct = {
         |    list
         | }
    distinct: List[Int]

    scala> distinct
    res13: List[Int] = List(10, 20, 3, 6, 35)


    -----------
    diff
    -----------
    scala> list.diff
       def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]

    scala> def apply(n:Int) = { val list2=List(1, 7, 3, 4, 2, 2):+6
         | list
         | }
    apply: (n: Int)List[Int]



    scala> def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]
         | =
         | {
         |  apply(B)
         | }
    diff: [B >: Int](that: scala.collection.GenSeq[B])List[Int]




    ***************************************************************************
    TO PRINT THE LIST VALUES BY USING  NAMEDFUNCTION
    ****************************************************************************
    scala> listbig
    res145: List[Int] = List(20, 30, 65, 88, 99, 101, 450, 101, 256, 12, 6, 8, 99, 45, 101)

    scala> listbig.tail.size
    res143: Int = 14 // it consider only within value position

    scala> listbig.tails.size
    res144: Int = 15 //it consider Nil position also


    scala> def eachret() = {
         | for(z <- 1 to listbig.tail.size) <--tail---->
         | println("\n z is:"+z + ",listval:"+listbig(z))
         | }
    eachret: ()Unit

    scala> eachret

     z is:1,listval:30

     z is:2,listval:65

     z is:3,listval:88

     z is:4,listval:99

     z is:5,listval:101

     z is:6,listval:450

     z is:7,listval:101

     z is:8,listval:256

     z is:9,listval:12

     z is:10,listval:6

     z is:11,listval:8

     z is:12,listval:99

     z is:13,listval:45

     z is:14,listval:101


    ********
    ERROR
    *******

    scala> def eachret() = {
         | for(z <- 0 to listbig.tails.size) <---------tails------>
         | println("\n z is:"+z + ",listval:"+listbig(z))
         | }
    eachret: ()Unit

    scala> eachret

     z is:0,listval:20

     z is:1,listval:30

     z is:2,listval:65

     z is:3,listval:88

     z is:4,listval:99

     z is:5,listval:101

     z is:6,listval:450

     z is:7,listval:101

     z is:8,listval:256

     z is:9,listval:12

     z is:10,listval:6

     z is:11,listval:8

     z is:12,listval:99

     z is:13,listval:45

     z is:14,listval:101
    java.lang.IndexOutOfBoundsException: 15
      at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
      at scala.collection.immutable.List.apply(List.scala:84)
      at $anonfun$eachret$1.apply$mcVI$sp(<console>:16)
      at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:160)
      at .eachret(<console>:15)
      ... 32 elided

    solution
    --------
    Array Index out of bounds of exception comes when extract value without having that.



    ************************************************************************
     TAKE THE LIST AS A PARAMETER AND DISPLAY TOTAL VALUES WITH POSTIONS BY USING SWITCH STATEMENTS
    **************************************************************************

    scala> def f(k:Any) = k match {
    case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
    println("\n z is:"+z + ",listval:"+listbig(z))
    case k if(k == list) => list
    case _=> println("there is no values")
    }
    f: (k: Any)Any

    scala> f(listbig)

     z is:1,listval:30

     z is:2,listval:65

     z is:3,listval:88

     z is:4,listval:99

     z is:5,listval:101

     z is:6,listval:450

     z is:7,listval:101

     z is:8,listval:256

     z is:9,listval:12

     z is:10,listval:6

     z is:11,listval:8

     z is:12,listval:99

     z is:13,listval:45

     z is:14,listval:101
    res150: Any = ()

    scala> f(list)
    res151: Any = List(25, 33, 45, 66, 88)

    scala> f(list1)
    there is no values
    res152: Any = ()

    ERROR
    ------
    1)
    scala> def f(k:Any) = k Match {
         | case if(k == listbig) => for(z <- 1 to listbig.tail.size)
    <console>:2: error: illegal start of simple pattern
    case if(k == listbig) => for(z <- 1 to listbig.tail.size)
         ^

    SOL) case k if(k == listbig)=> for(z <- 1 to listbig.tail.size)
     ^

    2)

    scala> def f(k:Any) = k Match {
         | case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
         | println("\n z is:"+z + ",listval:"+listbig(z))
         | case k if(k == list) => list
         | case _=> println("there is no values")
         | }
    <console>:15: error: value Match is not a member of Any
           def f(k:Any) = k Match {
                            ^
    sol)match not Match

    ***************************************************************************
    TO SEND THE DIFFERENT LIST PARAMETERS AND A VALUE AFTER SEARCHING IT WILL BE DISPLAY WHICH LIST CONTAINS THE VALUE
    *****************************************************************************
    scala> def f(y:Any,k:Any*) = k match {
         | case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
         | if(listbig(z) == y)
         | println("value found in the :"+listbig)
         | case k if(k == list) => for(z <- 1 to list.tail.size)
         | if(list(z) == y)
         | println("value found in the :"+list)
         | case k if(k == list1) => for(z <- 1 to list1.tail.size)
         | if(list1(z) == 'y')
         | println("value found in the :"+list1)
         | case _=> println("there is no values")
         | }

    f: (y: Any, k: Any*)Unit

    ***NOTE: HERE IT IS NOT WORKING BECAUSE K:ANY* IS TAKE THE VALUES AS A ARRAY TYPE SO IT IS NOT POSSIBLE TO COMPARE INDIVIDUALLY IN LISTS

    scala> def f(k:Any*,y:Any) = k match {
         | case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
         | if(listbig(z) == y)
         | println("value found in the :"+listbig)
         | case k if(k == list) => for(z <- 1 to list.tail.size)
         | if(list(z) == y)
         | println("value found in the :"+list)
         | case k if(k == list1) => for(z <- 1 to list1.tail.size)
         | if(list1(z) == 'y')
         | println("value found in the :"+list1)
         | case _=> println("there is no values")
         | }
    <console>:16: error: *-parameter must come last
           def f(k:Any*,y:Any) = k match {
                 ^


    ============================================================================
     CONNNECT THE MYSQL DATABASE USING SCALA AND RETRIVE THE DATA
    ============================================================================
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    import java.sql.DriverManager
    import java.sql.Connection
    object DBConnection {
    val driver="com.mysql.jdbc.Driver"
    val url = "jdbc:mysql://localhost/7hills"
    val username = "root"
    val password = "hadoop"
    def apply() = new DBConnection
    }
    class DBConnection{
    private val props = Map(
    "url" -> DBConnection.url,
    "user" -> DBConnection.username,
    "pass" -> DBConnection.password )
    println(s"Created new connection for " + props("url"))
    val connection = DriverManager.getConnection(DBConnection.url, DBConnection.username, DBConnection.password)
    DriverManager.registerDriver(new com.mysql.jdbc.Driver ());
    val statement = connection.createStatement()
    val resultSet = statement.executeQuery("SELECT movieId,title FROM movies")
    while(resultSet.next() ) {
    val movieId = resultSet.getString("movieId")
    val title = resultSet.getString("title")
    println("movieId,title="+movieId+","+title)
    }
    }

    ctrl+D
    // Exiting paste mode, now interpreting.

    import java.sql.DriverManager
    import java.sql.Connection
    defined object DBConnection
    defined class DBConnection

    scala> val conn = new DBConnection()
    Created new connection for jdbc:mysql://localhost/7hills
    movieId,title=1,the hero
    conn: DBConnection = DBConnection@42bc14c1


    *AFTER ADDING SOME ROWS IN MOVIES TABLE ***

    scala> val conn = new DBConnection()
    Created new connection for jdbc:mysql://localhost/7hills
    movieId,title=1,the hero
    movieId,title=2,theLEGEND
    movieId,title=3,thetarminator
    movieId,title=3,brundavanam
    movieId,title=4,kithakithalu
    conn: DBConnection = DBConnection@5fc930f0


    ****println("movieId ="+movieId+":TITLE:"+title)
    scala> val conn = new DBConnection()
    Created new connection for jdbc:mysql://localhost/7hills
    movieId =1:TITLE:the hero
    movieId =2:TITLE:theLEGEND
    movieId =3:TITLE:thetarminator
    movieId =3:TITLE:brundavanam
    movieId =4:TITLE:kithakithalu
    conn: DBConnection = DBConnection@4bc33720

    ===============================================================================