Dynamic (Ruby) | Static (Java) |
---|---|
|
|
Dynamic-Like | Static |
---|---|
|
|
val sum = 1 + 2 + 3 // Int
val lst = List(1, 2, 3) // List[Int]
val map = Map("abc" -> List(1,2,3)) // Map[String, List[Int]]
val sum: Int = 1 + 2 + 3
val lst: List[Int] = List(1, 2, 3)
val map: Map[String, List[Int]] = Map("abc" -> List(1,2,3))
// Check if string has uppercase character
boolean hasUpperCase = false;
for(int i = 0; i < name.length(); i++) {
if(Character.isUpperCase(name.charAt(i))) {
hasUpperCase = true;
break;
}
}
// Check if string has uppercase character
val hasUpperCase = name.exists(_.isUpperCase)
public class Person {
private String name;
private int age;
public Person(String name, Int age) { // constructor
this.name = name;
this.age = age;
}
public String getName() { // name getter
return name;
}
public int getAge() { // age getter
return age;
}
public void setName(String name) { // name setter
this.name = name;
}
public void setAge(int age) { // age setter
this.age = age;
}
}
class Person(var name: String, var age: Int)
Scala
Override Getter and/or Setter as-needed
class Person(var name: String, private var _age: Int) {
def age = _age // Getter for age
def age_=(newAge:Int) { // Setter for age
println("Changing age to: "+newAge)
_age = newAge
}
}
var - can reassign val - only assign once
// variable
var foo = "foo"
// this is fine since foo is a var
foo = "bar"
// value
val bar = "bar" // Java equivalent: final String bar = "bar"
// does not compile since bar is a val
bar = "foo"
// Every value is an object
1.toString
// Every operation is a method call
1 + 2 + 3 <=> (1).+(2).+(3)
// Can omit . and ( )
"abc" charAt 1 <=> "abc".charAt(1)
// Classes (and abstract classes) like Java
abstract class Language(val name: String) {
override def toString = name
}
// Example implementations
class Scala extends Language("Scala")
// Anonymous class
val scala = new Language("Scala") { /* add stuff here */ }
// Like interfaces in Java
trait Language {
val name: String
// But allow implementation
override def toString = name
}
trait JVM {
override def toString = super.toString+" runs on JVM" }
trait Static {
override def toString = super.toString+" and is Static" }
// Traits are stackable
class Scala extends Language with JVM with Static {
val name = "Scala"
}
println(new Scala) // "Scala runs on JVM and is Static"
// Replaces static methods from Java
// Can extend/implement classes & traits
object Hello {
def world = println("Hello World"}
}
Hello.world // Prints: Hello World
// Create anonymous function
val plusOne = (x: Int) => x + 1
// Calling the anonymous function
plusOne(5)
var foo = 1
// plusFoo can reference any values/variables in scope
val plusFoo = (x: Int) => x + foo
plusFoo(5) // 6
// Changing foo changes the return value of plusFoo
foo = 5
plusFoo(5) // 10
val plusOne = (x: Int) => x + 1
val nums = List(1,2,3)
// map takes a function: Int => T
nums.map(plusOne) // List(2,3,4)
// Inline Anonymous
nums.map(x => x + 1) // List(2,3,4)
// Short form using a placeholder
nums.map(_ + 1) // List(2,3,4)
val nums = List(1,2,3,4)
// A few more examples for List class
nums.exists(_ == 2) // true
nums.find(_ == 2) // Some(2)
nums.indexWhere(_ == 2) // 1
nums.reduceLeft(_ + _) // 10
nums.foldLeft(100)(_ + _) // 110
// Many more in collections library
// functions as parameters
def call(f: Int => Int) = f(1)
call(plusOne) // 2
call(x => x + 1) // 2
call(_ + 1) // 2
// functions as parameters
def each(xs: List[Int], fun: Int => Unit): Unit = {
if (!xs.isEmpty) {
fun(xs.head)
each(xs.tail, fun) // tail recursion is optimized!
}
}
each(List(1,2,3), println)
// 1
// 2
// 3
def each[T](xs: List[T], fun: T => Unit): Unit = xs match {
case Nil =>
case head :: tail => fun(head); each(tail, fun)
}
each(List(1,2), println)
// 1
// 2
each(List("foo", "bar"), println)
// foo
// bar
def what(any: Any) = any match {
case i: Int => "It's an Int"
case s: String => "It's a String"
case _ => "I don't know what it is"
}
what(123) // "It's an Int"
what("hello") // "It's a String"
what(false) // "I don't know what it is"
val nums = List(1,2,3)
// Pattern matching to create 3 vals
val List(a,b,c) = nums
a // 1
b // 2
c // 3
// Immutable types by default
var nums = Set(1,2,3)
nums += 4 // nums = nums.+(4) -- creates new set
// Mutable types available
import scala.collection.mutable._
val nums = Set(1,2,3)
nums += 4 // nums.+=(4) -- updates existing set
(*Okay not really, but it has lots of features typically only found in Dynamic languages)
bash$ cat hello.sc
println("Hello, World!")
bash$ scala-cli hello.sc
Hello, World!
bash$ scala
Welcome to Scala 3.3.1 (17, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> class Foo { def bar = "baz" }
// defined class Foo
scala> val f = new Foo
val f: Foo = Foo@213835b6
scala> f.bar
val res0: String = baz
import reflect.Selectable.reflectiveSelectable
def doTalk(any: {def talk: String}) = {
println(any.talk)
}
class Duck { def talk = "Quack" }
class Dog { def talk = "Bark" }
doTalk(new Duck) // "Quack"
doTalk(new Dog) // "Bark"
// Goal: Add isBlank method to String class
implicit class RichString(s: String) {
def isBlank = null == s || "" == s.trim
}
// Our isBlank method is now available on Strings!
" ".isBlank // true
"foo".isBlank // false
// 1. Does not type check
"abc".isBlank // ERROR
// 2. Search in-scope implicits classes and methods that take
// a String and return a type with an isBlank method
implicit class RichString(s: String) { def isBlank = ... }
// 3. Scala converts the code to:
new RichString("abc").isBlank
import scala.language.dynamics
class Foo extends Dynamic {
def applyDynamic(name: String)(args: Any*) = {
println("called: "+name+"("+args.mkString(",")+")")
}
}
val f = new Foo
f.helloWorld() // called: helloWorld()
f.hello("world") // called: hello(world)
f.bar(1,2,3) // called: bar(1,2,3)
def hello(foo: Int = 0, bar: Int = 0) = {
println("foo: "+foo+" bar: "+bar)
}
hello() // foo: 0 bar: 0
hello(1) // foo: 1 bar: 0
hello(1,2) // foo: 1 bar: 2
def hello(foo: Int = 0, bar: Int = 0) = {
println("foo: "+foo+" bar: "+bar)
}
hello(bar = 6) // foo: 0 bar: 6
hello(foo = 7) // foo: 7 bar: 0
hello(foo = 8, bar = 9) // foo: 8 bar: 9
val a = if (true) "yes" else "no"
val b = try {
"foo"
} catch {
case _ => "error"
}
val c = {
println("hello")
"foo"
}
// initialized on first access
lazy val foo = {
println("init")
"bar"
}
foo // init
foo //
foo //
def outer() = {
var msg = "foo"
def one() = {
def two() = {
println(msg)
}
two()
}
one()
}
def log(doLog: Boolean, msg: => String) {
if (doLog) {
msg // evaluates msg
msg // evaluates msg again!
}
}
def foo: String = { println("in foo"); "Foo" }
log(true, foo+" Bar") // foo called twice
log(false, foo+" Bar") // foo never called
@foo def hello = "world" // annotations
case class Foo(bar: String) // case classes
def foo(a: Int)(b: Boolean) // currying
fun(123, _) // partially applied functions
(1, "foo", false) // tuples
for (i <- 1 to 5 if i % 2 == 0) yield i // for comprehensions