Created
September 28, 2015 00:35
-
-
Save shajra-cs/ab0a00dae047a843d4af to your computer and use it in GitHub Desktop.
Here's an example of a tutorial that spun out of a Friday discussion.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package c12e.learn | |
// Note, expressions in Scala need to be inside things like objects. We'll | |
// discuss objects (and packages) later. These are just a complications we | |
// introduce now to have a well-formed program. | |
object ValuesAndTypes { | |
// Binding Values | |
// -------------- | |
// Let's bind some values to names using Scala's *val* syntax. This is a | |
// basis of writing programs. Note that "values" and "expressions" are | |
// synonyms, and both terms are commonly used interchangeably. | |
val i: Int = 13: Int | |
val s: String = "Hello": String | |
val b: Boolean = true: Boolean | |
// Scala gives us some *literals* to write down some basic values. Every | |
// value has a type. Integers are represented in Scala by the Int type, with | |
// values are written as you might expect. Text is represented by the String | |
// type with value written as the text enclosed by double quotes. There is | |
// also a type with two values -- true and false -- called Boolean, which is | |
// important for branching. | |
// | |
// We use a colon to explicitly *ascribe* a value with its type. | |
// | |
// Above, we've bound the value 13 of type Int to the name "i" that is | |
// restricted to bindings of only Ints. Similarly, we've bound the value | |
// "Hello" of type String to the name "s" that's limited to bindings of only | |
// Strings. | |
// Tuple Literals | |
// -------------- | |
// Tuples can group values of different types together. Scala also gives us | |
// a tuple literal notation that uses parentheses, a lot like coordinates: | |
val pair: (Int, String) = | |
(1: Int, "a": String): (Int, String) | |
val triple: (Int, String, Boolean) = | |
(2: Int, "b": String, false: Boolean): (Int, String, Boolean) | |
// The *arity* of a tuple indicates how many types it groups together. | |
// 2-tuples are tuples of arity 2, 3-tuples of arity 3, and so forth. | |
// 2-tuples are often called *pairs*. Another term for tuple is *product*. | |
// Type Inference | |
// -------------- | |
// There is a *type theoretic* view of values that's founded on the idea that | |
// values can only have one type. If this is the case, then we know that 13 | |
// is an Int without saying so explicitly. Similarly we know that "Hello" is | |
// a String. | |
// | |
// We don't have to ascribe our values with their respective types: | |
val i2: Int = 13 | |
val s2: String = "Hello" | |
val b2: Boolean = true | |
val pair2: (Int, String) = (1, "a") | |
val triple2: (Int, String, Boolean) = (2, "b", false) | |
// Note, that we used new names (i2, s2, pair2, and triple2) because old | |
// names can not be rebound. This is very much like how in mathematics, π | |
// can not be rebound to anything other than the number 3.1415... | |
// Furthermore, we also don't have to ascribe the bindings: | |
val i3 = 13 | |
val s3 = "Hello" | |
val b3 = true | |
val pair3 = (1, "a") | |
val triple3 = (2, "b", false) | |
// In Scala, we try to avoid ascribing values whereever we can, but we often | |
// ascribe bindings to document our intent to others. The type is like a | |
// contract to the outside world, so they don't have to read the | |
// implementation of the value (which can be expressions far more complex | |
// than just 13 or "Hello"). | |
// Complexity with Inference | |
// ------------------------- | |
// Type inference would be easy if Scala were purely type theoretic where | |
// values indeed only have one type. Scala, though, takes a more *set | |
// theoretic* view that values can have multiple types. Think of a Venn | |
// diagram of sets, where values are like elements that lie in the | |
// intersections of different sets. | |
// The common justification for this decision is to have compatibility with | |
// Scala's sister programming language Java (Scala's popularity is in large | |
// part due to it's interoperability with Java). | |
// In Scala, types have a hierarchy. At the top of the hierarchy, we have | |
// one type, Any, that is so generic that every value is of this type from | |
// numbers like 13 to text like "Hello". This is why in Scala, we can do | |
// this: | |
val i4: Any = 13 | |
val s4: Any = "Hello" | |
val b4: Any = true | |
val pair4: Any = (1, "a") | |
val triple4: Any = (2, "b", false) | |
// We say that all types are a *subtype* of Any, because we can use instances | |
// of any type as an Any. We can also say that Any is the *supertype* of all | |
// types. | |
// There is also an interesting "bottom" type of the hierarchy called Nothing | |
// that has no values, and is the subtype of all types. Because Nothing has | |
// no values, we don't use it often. | |
// Because Scala is more set theoretic than type theoretic, we can sometimes | |
// have problems with inference. Most of the time, Scala infers the type | |
// we'd intend from a type theoretic viewpoint. But sometimes it infers an | |
// Any or Nothing, which is generally not our intent. This is another reason | |
// we ascribe bindings in Scala. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment