In this unit, we are going to learn a little bit more how classes are organized into larger programs. Classes and objects are conceptually members of packages. You can think of a package as similar to a directory in a file system. In fact, packages often correspond one to two directories. To place a class or object inside a package, you use a package clause at the top of your source file. You write Package progfun.examples and then object Hello, and that would give you an object in the package progfun.examples. Usually, you organize your sources so that you would have a directory progfun, and that directory would contain the file Hello.scala. That's the normal way you organize source files, but it's not enforced by the system. It's just a convention. Once you have an object in this package, you can refer to it by its fully qualified name. To refer to Hello, you would write progfun.examples.Hello. For instance, to run that Hello program, you would write scala progfun.examples.Hello. To avoid having to write long package names over again, you have imports. Let's say we have a class rational in package week3, we can use the class using its fully qualified name, like this; week3.Rational. Alternatively, you can also use an import. You can say import week3.Rational and that makes rational known under its simple name here. You don't need to week3.prefix. Imports in scala are quite flexible. They come in several forms. You can either have an import that imports a one specific thing or several things. Import week3. Rational, Hello embraces would import both rational and Hello. Or you can have a wildcard import which we write with an underscore so that third import would import everything in package week3. The first two are called named imports, and the last is a wildcard import. You can import from either a package or an object. In fact, conceptually, packages in scala are just essentially very large objects that have members that are each in individual source files. Some entities are automatically imported in any scala program. These are all members of the packaged scala, all the members of the package java.lang, and all members of the singleton object scala.Predef. Some of the names you've seen before, actually live in these packages and objects and are imported automatically. The Int type is really scala.Int, so it's defined in the scala package and it's made available because of this first automatic import. The same happens for the Boolean type and object which you get from java.Lang object. They require and assert methods that we've used in the rational example are actually defined in the scala.Predef object as methods and you get them by the automatic import from this scala.Predef objects. They are really scala.Predef.require, and scala.Predef.assert. You can explore the standard Scala library using the Scaladoc webpages. You can start at this web address. Here we see the root page of the web pages and we can look for the definition of a type, let's say Int. That would then give us a scala Int type and all the methods that are defined on that int type, so you'll see. The usual suspects, all the operators that you can have on Int and several others more. Another important concept in Scala are traits. Traits are similar to classes. The difference is that in Scala as well as Java, a class can only have one superclass. It can extend only one class. But it often happens that our class has several natural supertypes to which it should conform and from which it sometimes wants to inherit code. In that situation, you could use traits. A trait is declared like an abstract class just with trait in front instead of abstract class. Here we have a trait Planar that has methods height and width, which are kept abstract and surface which is defined as height times width. Class is objects and traits can inherit from it most one class, but arbitrarily many traits. For instance, you could have a class square that extends shape, which might be a class, and then it also extends straits Planar and movable. Traits resemble interfaces in Java, but they're more powerful because they can have parameters and they can contain fields and concrete methods. Here's an outline of the class hierarchy as seen from every Scala program. The Any classes, two subtypes; AnyVal and AnyRef. AnyVal is the common superclass of all primitive types. We have nine primitive types in Java as well. Scala; they're Double, Float, Long, Int, Short, Byte. Those are the numeric types. Then Character, Boolean and Unit. On the other side are most user-defined types. They are all subclasses of Scala. AnyRef which is just another name, an alias for Java.lang object, the Java's root class. Typical subclasses of object would be string or Scala classes like scala.List, scala.Seq, scala.Iterable. Other Scala classes defined by the system or defined by you or other java classes, they're all inherit from AnyRef occur object. Then duly to the top type, Any scala also has a bottom type, Nothing. The Nothing type is a subtype of all the other types and it doesn't have a value. Nothing essentially represents a computation that never returns a value. That could be a computation that loops forever, never terminates, or a computation that finishes with an exception, but not with a normal value return. The arrows here mean subtyping. That means everything at the bottom of an arrow conforms to the thing at the top of an arrow. There's also the dashed arrows here. They are conversions. You have implicit conversions along these edges. For instance, you can convert a Byte to a Short, can convert that to an Int, can convert that to a Long. You can convert a Long to a Float or a Float to a Double, and you can also convert a Character to an Int. They're similar to subtypes, but there's also difference. When you have a subtype relationship, the two classes on either end have typically the same representation. Whereas for conversion, the representation changes internally, an integer is not represented the same way as a Long. Long has twice the bits of an integer. Again, Long is not represented the same way as a Float into Double. Let's take a closer look at the top types of this hierarchy. At the very top we have Any, which is the base type of all other types. It has methods for comparisons; equals not equals or an alphanumeric equals method that's inherited from Java. You can take the 'hashCode' of every object and you can turn every object into a 'string' using the 'toString' method. The two top classes are AnyRef and AnyVal. AnyRef is an alias for 'java.lang.Object' and AnyVal is the base type of all primitive types. We've seen also nothing at the bottom of Scala type hierarchy, which is a subtype of every other type. There's no value of type nothing but nothing is nevertheless very useful as a type to signal abnormal termination, and also as an element type of empty collections. We'll see that in the next week. Another issue we should briefly touch on are exceptions. Scala's exception handling is quite similar to Java's. The expression throw Exc with a type of an exception aborts the evaluation with the exception Exc. The type of this throw expression is Nothing because there's no value that's returned from the expression. Let's finish with an exercise. What's the type of this expression? If true, then 1 else is false. Is it Int or Boolean or AnyVal or Object or Any? To answer this question, let's look at the class hierarchy. We had a two branches of the if and Int and a Boolean, 1 and false. Their common supertype is AnyVal and indeed, that's also the type of the if-then-else. AnyVal is the correct answer here. You might have said, "Well, why isn't the compiler is smart enough to say, well, the condition is always true", so the if statement will evaluate to the first side, which is an Int. In which case, the type maybe should be Int, which is a subtype of AnyVal and subtypes are considered as better types of the supertypes. The compiler is intentionally not doing that because it would lead to instability of programs. If you change this condition to something that was less obviously true, some condition p, let's say, then suddenly the type of the F would become less good. It would go from into AnyVal and that might break your program. That's one of the things where a programmer would know more than what the type system of a compiler actually gives you.