{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
\n", "\n", "# Basics\n", "\n", "In this page, we will cover basics of Scala.\n", "\n", "## Trying Scala interactively in a Jupyter Notebook\n", "\n", "If you want to execute the code examples in this tour interactively, the easiest way to get started is to [run the tour as Jupyter notebooks on Binder](https://mybinder.org/v2/gh/sbrunk/almond-examples/master?urlpath=lab%2Ftree%2Fscala-tour%2Fbasics.ipynb). That way, you can play with the examples and try new things in your browser without having to install anything locally.\n", "\n", "To execute a cell of code, just click on it and press control + enter or use the menu.\n", "\n", "For more information how this works or how to run it locally see the [README](https://github.com/sbrunk/almond-examples/blob/master/README.md) of this project or the [Almond documentation](http://almond-sh.github.io/almond/stable/docs/intro).\n", "\n", "All content is derived from https://docs.scala-lang.org/tour/tour-of-scala.html\n", "\n", "## Expressions\n", "\n", "Expressions are computable statements." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres0\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can output results of expressions using `println`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "Hello!\n", "Hello, world!\n" ] } ], "source": [ "println(1) // 1\n", "println(1 + 1) // 2\n", "println(\"Hello!\") // Hello!\n", "println(\"Hello,\" + \" world!\") // Hello, world!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Values\n", "\n", "You can name results of expressions with the `val` keyword." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] }, { "data": { "text/plain": [ "\u001b[36mx\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val x = 1 + 1\n", "println(x) // 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Named results, such as `x` here, are called values. Referencing\n", "a value does not re-compute it.\n", "\n", "Values cannot be re-assigned." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd3.sc:1: reassignment to val\n", "val res3 = x = 3 // This does not compile.\n", " ^Compilation Failed" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "x = 3 // This does not compile." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Types of values can be inferred, but you can also explicitly state the type, like this:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mx\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val x: Int = 1 + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how the type declaration `Int` comes after the identifier `x`. You also need a `:`. \n", "\n", "### Variables\n", "\n", "Variables are like values, except you can re-assign them. You can define a variable with the `var` keyword." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9\n" ] }, { "data": { "text/plain": [ "\u001b[36mx\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var x = 1 + 1\n", "x = 3 // This compiles because \"x\" is declared with the \"var\" keyword.\n", "println(x * x) // 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As with values, you can explicitly state the type if you want:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mx\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "var x: Int = 1 + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Blocks\n", "\n", "You can combine expressions by surrounding them with `{}`. We call this a block.\n", "\n", "The result of the last expression in the block is the result of the overall block, too." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] } ], "source": [ "println({\n", " val x = 1 + 1\n", " x + 1\n", "}) // 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions\n", "\n", "Functions are expressions that take parameters.\n", "\n", "You can define an anonymous function (i.e. no name) that returns a given integer plus one:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres7\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = ammonite.$sess.cmd7$Helper$$Lambda$2461/875549557@23cd7091" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x: Int) => x + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On the left of `=>` is a list of parameters. On the right is an expression involving the parameters.\n", "\n", "You can also name functions." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] }, { "data": { "text/plain": [ "\u001b[36maddOne\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = ammonite.$sess.cmd8$Helper$$Lambda$2475/452780641@232887cf" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val addOne = (x: Int) => x + 1\n", "println(addOne(1)) // 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions may take multiple parameters." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] }, { "data": { "text/plain": [ "\u001b[36madd\u001b[39m: (\u001b[32mInt\u001b[39m, \u001b[32mInt\u001b[39m) => \u001b[32mInt\u001b[39m = ammonite.$sess.cmd9$Helper$$Lambda$2482/741665633@3d3644c1" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val add = (x: Int, y: Int) => x + y\n", "println(add(1, 2)) // 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or it can take no parameters." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "42\n" ] }, { "data": { "text/plain": [ "\u001b[36mgetTheAnswer\u001b[39m: () => \u001b[32mInt\u001b[39m = ammonite.$sess.cmd10$Helper$$Lambda$2490/1374863475@f01186c" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val getTheAnswer = () => 42\n", "println(getTheAnswer()) // 42" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Methods\n", "\n", "Methods look and behave very similar to functions, but there are a few key differences between them.\n", "\n", "Methods are defined with the `def` keyword. `def` is followed by a name, parameter lists, a return type, and a body." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36madd\u001b[39m" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add(x: Int, y: Int): Int = x + y\n", "println(add(1, 2)) // 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how the return type is declared _after_ the parameter list and a colon `: Int`.\n", "\n", "Methods can take multiple parameter lists." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "9\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36maddThenMultiply\u001b[39m" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier\n", "println(addThenMultiply(1, 2)(3)) // 9" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or no parameter lists at all." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, brunksn!\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mname\u001b[39m" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def name: String = System.getProperty(\"user.name\")\n", "println(\"Hello, \" + name + \"!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are some other differences, but for now, you can think of them as something similar to functions.\n", "\n", "Methods can have multi-line expressions as well." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mgetSquareString\u001b[39m" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def getSquareString(input: Double): String = {\n", " val square = input * input\n", " square.toString\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last expression in the body is the method's return value. (Scala does have a `return` keyword, but it's rarely used.)\n", "\n", "## Classes\n", "\n", "You can define classes with the `class` keyword followed by its name and constructor parameters." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mGreeter\u001b[39m" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class Greeter(prefix: String, suffix: String) {\n", " def greet(name: String): Unit =\n", " println(prefix + name + suffix)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The return type of the method `greet` is `Unit`, which says there's nothing meaningful to return. It's used similarly to `void` in Java and C. (A difference is that because every Scala expression must have some value, there is actually a singleton value of type Unit, written (). It carries no information.)\n", "\n", "You can make an instance of a class with the `new` keyword." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, Scala developer!\n" ] }, { "data": { "text/plain": [ "\u001b[36mgreeter\u001b[39m: \u001b[32mGreeter\u001b[39m = ammonite.$sess.cmd15$Helper$Greeter@61c125d6" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val greeter = new Greeter(\"Hello, \", \"!\")\n", "greeter.greet(\"Scala developer\") // Hello, Scala developer!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will cover classes in depth [later](classes.ipynb).\n", "\n", "## Case Classes\n", "\n", "Scala has a special type of class called a \"case\" class. By default, case classes are immutable and compared by value. You can define case classes with the `case class` keywords." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPoint\u001b[39m" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "case class Point(x: Int, y: Int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can instantiate case classes without `new` keyword." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mpoint\u001b[39m: \u001b[32mPoint\u001b[39m = \u001b[33mPoint\u001b[39m(\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m)\n", "\u001b[36manotherPoint\u001b[39m: \u001b[32mPoint\u001b[39m = \u001b[33mPoint\u001b[39m(\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m)\n", "\u001b[36myetAnotherPoint\u001b[39m: \u001b[32mPoint\u001b[39m = \u001b[33mPoint\u001b[39m(\u001b[32m2\u001b[39m, \u001b[32m2\u001b[39m)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val point = Point(1, 2)\n", "val anotherPoint = Point(1, 2)\n", "val yetAnotherPoint = Point(2, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And they are compared by value." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Point(1,2) and Point(1,2) are the same.\n", "Point(1,2) and Point(2,2) are different.\n" ] } ], "source": [ "if (point == anotherPoint) {\n", " println(point + \" and \" + anotherPoint + \" are the same.\")\n", "} else {\n", " println(point + \" and \" + anotherPoint + \" are different.\")\n", "} // Point(1,2) and Point(1,2) are the same.\n", "\n", "if (point == yetAnotherPoint) {\n", " println(point + \" and \" + yetAnotherPoint + \" are the same.\")\n", "} else {\n", " println(point + \" and \" + yetAnotherPoint + \" are different.\")\n", "} // Point(1,2) and Point(2,2) are different." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a lot more to case classes that we'd like to introduce, and we are convinced you will fall in love with them! We will cover them in depth [later](case-classes.ipynb).\n", "\n", "## Objects\n", "\n", "Objects are single instances of their own definitions. You can think of them as singletons of their own classes.\n", "\n", "You can define objects with the `object` keyword." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject\u001b[39m \u001b[36mIdFactory\u001b[39m" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "object IdFactory {\n", " private var counter = 0\n", " def create(): Int = {\n", " counter += 1\n", " counter\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access an object by referring to its name." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n" ] }, { "data": { "text/plain": [ "\u001b[36mnewId\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m1\u001b[39m\n", "\u001b[36mnewerId\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val newId: Int = IdFactory.create()\n", "println(newId) // 1\n", "val newerId: Int = IdFactory.create()\n", "println(newerId) // 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will cover objects in depth [later](singleton-objects.ipynb).\n", "\n", "## Traits\n", "\n", "Traits are types containing certain fields and methods. Multiple traits can be combined.\n", "\n", "You can define traits with `trait` keyword." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mGreeter\u001b[39m" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait Greeter {\n", " def greet(name: String): Unit\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Traits can also have default implementations." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mGreeter\u001b[39m" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trait Greeter {\n", " def greet(name: String): Unit =\n", " println(\"Hello, \" + name + \"!\")\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can extend traits with the `extends` keyword and override an implementation with the `override` keyword." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hello, Scala developer!\n", "How are you, Scala developer?\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mDefaultGreeter\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mCustomizableGreeter\u001b[39m\n", "\u001b[36mgreeter\u001b[39m: \u001b[32mDefaultGreeter\u001b[39m = ammonite.$sess.cmd24$Helper$DefaultGreeter@56c4afe7\n", "\u001b[36mcustomGreeter\u001b[39m: \u001b[32mCustomizableGreeter\u001b[39m = ammonite.$sess.cmd24$Helper$CustomizableGreeter@5b523d34" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class DefaultGreeter extends Greeter\n", "\n", "class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {\n", " override def greet(name: String): Unit = {\n", " println(prefix + name + postfix)\n", " }\n", "}\n", "\n", "val greeter = new DefaultGreeter()\n", "greeter.greet(\"Scala developer\") // Hello, Scala developer!\n", "\n", "val customGreeter = new CustomizableGreeter(\"How are you, \", \"?\")\n", "customGreeter.greet(\"Scala developer\") // How are you, Scala developer?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, `DefaultGreeter` extends only a single trait, but it could extend multiple traits.\n", "\n", "We will cover traits in depth [later](traits.ipynb).\n", "\n", "## Main Method\n", "\n", "The main method is an entry point of a program. The Java Virtual\n", "Machine requires a main method to be named `main` and take one\n", "argument, an array of strings.\n", "\n", "Using an object, you can define a main method as follows:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "attributes": { "classes": [ "tut" ], "id": "" } }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject\u001b[39m \u001b[36mMain\u001b[39m" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "object Main {\n", " def main(args: Array[String]): Unit =\n", " println(\"Hello, Scala developer!\")\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Previous

\n", "

Next

\n", "

Tour of Scala

\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Scala (2.13)", "language": "scala", "name": "scala213" }, "language_info": { "codemirror_mode": "text/x-scala", "file_extension": ".scala", "mimetype": "text/x-scala", "name": "scala", "nbconvert_exporter": "script", "version": "2.13.1" } }, "nbformat": 4, "nbformat_minor": 4 }