Scala 2 for Beginners¶
참고: https://rockthejvm.com/courses/1462300
Scala At Light Speed - 복습¶
The Absolute Basic¶
Expressions¶
- 개요
- Everything in scala is an Expression
- IF는 Expression
- Code Block도 Expression
- Everything in scala is an Expression
- Expressions vs Instructions
- Instruction: 실행되는 것 (Java) - do sth
- Expressions: 평가되는 것 (Scala) - give me the value of sth
- Side Effect
- 함수의 리턴값이 아닌 값에 대한 상태를 변화시키는 것을 의미함
- 변수 조작, 파일/DB 읽기, 콘솔에 출력하기 등등을 의미함 (주로 Unit 반환)
- 함수형 프로그래밍에서 프로그램이 어떻게 돌아가는지 추론을 어렵게 만들기에 권장되지 않음
- 함수형 프로그램에서는 함수의 행위가 오로지 input에 대해서만 영향을 받아야함
- 사이드 이펙트가 발생해야하는 경우가 있음 ex) 데이터베이스에 읽기/쓰기
- 굉장히 작은, 잘 정제된 코드 부분에서 접근하도록 해야하며, 프로그램 전체에 영향을 미치지 않도록 해야함
- monad를 사용하여 많은 라이브러리들이 이를 캡슐화하도록 지원하고 있음
- purely functional 하도록 유도
- 함수의 리턴값이 아닌 값에 대한 상태를 변화시키는 것을 의미함
Functions¶
- 함수 실행은 Expression 평가와 같다
- 파라미터가 없다면 그냥 함수 이름만 써서 실행시킬 수 있음
- 반복문이 필요하다면, 재귀함수를 쓰세요!
Type Inference¶
- 컴파일러는 우리가 빠뜨린 자료형의 타입을 알아서 써줘
- 컴파일러는 우리가 빠뜨린 함수의 반환 타입을 알아서 써줘
- 대신 재귀함수를 쓸 때는 함수의 반환 타입을 알려주세요 => 모를 수 있거든요
Recursion¶
- 반복문이 필요하다면 꼬리 재귀(tailrec)을 사용하세요!!
- tailrec
- 재귀를 써도 스택 오버플로우가 안 터지는 방법
- 꼬리 재귀를 쓰면 새로운 스택 위에 쌓지 않아
- don't need to save intermediate result
- evaluated stack frame => current stack frame (No need for extra stack frame)
- 꼬리 재귀가 되려면 재귀 함수를 마지막 expression으로 사용하세요!!!!
Call By Name & Call By Value¶
- Call By Value
- 파라미터로 넘어가는 "값" 그 자체!
- 그 값을 아무리 호출해도 넘겨받은 값을 사용하게 됨
- 파라미터로 넘길 때 값을 평가해서 "값"을 줌
- Call By Name
- 파라미터로 넘어가는 "Expression"
- 해당 "Expression"을 호출할 때마다 평가된다!
- 파라미터로 넘길 때 호출할 "Expression"을 넘겨줌
- 따라서 호출이 되어야 평가를 하고 값을 반환함
def calledByValue(x: Long): Unit = {
println("by value: " + x)
println("by value: " + x)
}
def calledByName(x: => Long): Unit = {
println("by name: " + x)
println("by name: " + x)
}
calledByValue(System.nanoTime())
calledByName(System.nanoTime())
/*
* by value: 12345
* by value: 12345
* by name: 12378
* by name: 12401
* */
Default and Named Arguments¶
- 파라미터에 이름 붙이고, default value를 줄 수 있음
- 함수 호출시
파리미터 이름 = 값형식으로 쓰자!
String Operations and Interpolations¶
- S-Interpolations
- F-Interpolations
- Raw-Interpolations
OOP in Scala¶
OO Basics¶
- 개요
- constructor의
val을 붙여야 멤버변수로써 활용 가능 - 그냥 클래스 내부에서
val을 붙여도 멤버변수로 활용 가능 - multiple constructor 활용 가능 => 너무 많다면 default parameter가 대안이 될 수 있음
- constructor의
- TIPS
- 불변성을 고려하여 만들자! (상태 패턴이랑 비슷해보이기도 함!)
Method Notations¶
- 개요
- 다양한 기호들이 메서드의 이름으로 사용될 수 있음 => 가독성 향상 가능
- prefix, infix, postfix 방식으로 메서드가 여기저기에 낄 수 있도록 syntatic sugar 제공
- 파라미터가 0개/1개라면 보다 유연하게 호출할 수 있음
- 예시
class Person(val name: String, favoriteMovie: String) { def likes(movie: String): Boolean = (movie == favoriteMovie) def +(person: Person): String = this.name + " +++++ " + person.name def unary_! : String = "supports prefix!" // unary_* 하되, 띄어쓰기 하고 : 붙이기 def isAlive: Boolean = true def apply(): String = "My name is " + this.name } val joel = new Person("Joel", "Shall we dance?") // 메서드가 호출될 때, 어떻게 정의되어 있는가에 따라 prefix, infix, postfix가 될 수 있음 println(!joel) // prefix println(joel likes "Shall we dance?") // infix println(joel isAlive) // postfix
Scala Objects¶
- 개요
- 스칼라는 class-level functionality, 즉 자바의 static 변수가 없음 => object를 통해서 대신할 수 있음
- Scala Object는 싱글턴으로 기능함
- Companions
- class와 object의 이름이 같으면, Companions라고 칭한다
- Companions 왜 필요한데?
- Code Organizations: 오브젝트와 클래스에 필요한 코드를 그룹핑 시켜 이해하기 좋은 코드를 만들자
- Convenience: 클래스를 생성하는 팩토리 메서드를 오브젝트에 넣자.
apply()syntatic sugar 쓰면 더 좋고 - Static Methods: 정적 메서드 사용하듯 사용할 수 있음. 간결하고 읽기 쉽게!
- Implicit Conversions: 다른 이름과 대표값이 있을때 변환을 쓱 하는데 지원
- Sharing state: 클래스가 공통적으로 공유하는 상태를 오브젝트에 만들자!
Abstract Class and Inheritance¶
- 오버라이딩 방지
- final members
- final classes
- sealed classes
- trait
- constructor에 파라미터 가지면 안댐
- 여러개의 trait 받아서 클래스 생성 가능
Anonymous Classes¶
abstract class Animal {
def eat: Unit
}
val funnyAnimal: Animal = new Animal {
override def eat: Unit = println("~~~")
}
Generics¶
- 개요
- 자바의 제네릭과 비슷한 느낌이 많이 든다
- 다만 Covariance, Invariance, Contravariance 등의 컨셉이 자바와 다름
- 제네릭의 한정자 super와 extends 는 <: 등으로 상한/하한 표현 가능
Case Classes¶
- 개요
- CC에서는 파라미터가 필드다
- CC는 toString을 오버라이딩 해둔다
- equals & hashCode가 필드가 같으면 같다
- CC는 copy()를 보다 쉽게 지원 (새로운 인스턴스 생성)
- CC는 companion object를 가진다. (apply() 가 호출되어 constructor 대체)
- CC는 serializable
- CC는 패턴 매칭에서도 자주 쓰임
case class Person(name: String, age: Int) // 1. class parameters are fields val jim = new Person("Jim", 34) println(jim.name) // Jim // 2. sensible toString println(jim) // Person(Jim,34) // 3. equals and hashCode val jim2 = new Person("Jim", 34) println(jim == jim2) // true // 4. handy copy method val jim3 = him.copy(age = 45) println(jim3) // Person(Jim,45) println(jim == jim3) // false // 5. Case Class have companion objects val thePerson = Person val mary = Person("Mary", 23) // 6. CC are serializable // 7. CC can be used in pattern matching
Handling Exceptions¶
- Exception 던지기
val aWeirdValue: String = throw new NullPointerException
- Exception 잡기
- Exception 정의하기
- 자바와 비슷!
Packaging and Imports¶
- 자바와 비슷
FP in Scala¶
What's a Function?¶
- 개요
- JVM에서 함수를 일급 시민으로 쓸 수는 없을까?
- JVM은 OOP 기반으로 설계 되어있음 => 함수형을 JVM 위에서 구동시키기 위해서는 함수형을 OOP 위에 구현시키려는 노력이 필요했음
- Function_x
- trait
Function_1~Function22까지 (1~22는 파라미터 갯수) 인스턴스화 시켜서 이를 사용토록 - 이를 람다식으로 표현할 수 있도록 Syntatic Sugar 제공
- 모든 스칼라의 Function은 JVM 위에서 동작한다는 것을 명심!
- trait
Anonymous Functions¶
- 개요
- 익명 클래스 처럼, 익명 함수를 만들어보자
- 그냥 trait, abstract class를 변수에서 new 인스턴스화로 박아버리듯, 함수도 그렇게 람다식으로 박아버리자
- Syntatic Sugar
_를 활용해 함수에서 넘겨받은 매개변수를 표현할 수 있다
HOF and Curries¶
- Higher Order Function(HOF)
- 함수를 파라미터로, 리턴값으로 쓰는 경우, 이를 HOF라고 부른다
- 함수 합성, 중첩 등이 가능해진다.
- Curried Functions
- 함수의 재사용성을 높이기 위해 함수 자체를 반환하는 함수
- 참고: https://itprogramming119.tistory.com/entry/Javascript-%EC%BB%A4%EB%A7%81Currying-%ED%95%A8%EC%88%98%EB%9E%80
Collections Overview¶
Sequences¶
- Seq
- Ranges
- List
- Singly-Linked List
- O(1): head, tail, isEmpty
- o(n): 그 외 다수의 random access
- 앞 순서 변경에 강함
- Vector
- 트리 기반의 데이터 구조
- 트리의 노드는 32개의 element까지 수용할 수 있다
- Vector는 효율적인 random access, tail operation 보장 O(logN)
- head insertion/deletion 느림
- 뒷 순서 변경에 강함
- Array
Array.ofDim[T](n1, n2, ...)- multidimensional array of type T 생성
Array.ofDim[Int](3)는 Int 타입의 3칸짜리 1차원 배열(0, 0, 0)Array.ofDim[String](3)는 String 타입의 3칸짜리 1차원 배열(null, null, null)- 배열은 안의 요소 변경할 수 있음
Tuples & Maps¶
- Tuples
- Maps
map, filter, flatMap¶
- 개요
- list, array, set을 조작하는 HOF
- 스칼라 FP, 간결하고, 효율적인 코드 작성 도와줌
- map
- 주어진 함수를 각 요소별로 수행시켜서, 기존 컬렉션을 새로운 같은 사이즈의 컬렉션으로 반환
- 여기서 새로운 컬렉션은 기존 컬렉션의 요소별 함수 수행 값
- filter
- 기존 컬렉션에서 해당 predicate를 만족하는 새로운 컬렉션을 반환
- flatMap
- map과 비슷하나!
- 컬렉션을 반환하는 함수를 적용시키고, 이를 flattening 시켜 하나의 컬렉션으로 만들어버림
Options¶
- 개요
- 값을 가질수도, 가지지 않을수도 있는 컨테이너
- Null 값을 핸들링하기 위함이며, type-safe way한 방식으로 다루기 위함
- FP에서 성공할수도, 실패할수도 있는 계산을 수행할 때 많이 사용
- 실패 == null 이라는 공식에서 벗어나, Option 타입을 돌려주자
- Option
- 실패 == None
- 성공 == Some(value)
object Option { // ~~ } sealed abstract class Option[+A] extends IterableOnce[A] with Product with Serializable { def get: A } final case class Some[+A](value: A) extends Option[A] { def get: A = value } case object None extends Option[Nothing] { def get: Nothing = throw new NoSuchElementException("None.get") }
- 예시
- Option의 메서드
- [orElse]:
if (isEmpty) alternative else this - [flatMap]:
if (isEmpty) None else f(this.get)
- [orElse]:
- 예시
val config: Map[String, String] = Map( "host" -> "182.12.34.3", "port" -> 80 ) class Connection { def connect = "Connected" } object Connection { val random = new Random(System.nanoTime()) def apply(host: String, port: String): Option[Connection] = if (random.nextBoolean()) Some(new Connection) else None } val host = config.get("host") val port = config.get("port") val connection = host.flatMap(h => port.flatMap(p => Connection.apply(h, p)))
Try¶
- 개요
- 성공/실패에 대해 값을 가질 수 있도록 Try

