20장: 스칼라 언어 살펴보기

자바의 함수형 프로그래밍에 영향을 준 스칼라 언어에 대해 알아본다.

컬렉션

val authorsToAge = Map("Raoul" -> 23, "Mario" -> 40, "Alan" -> 53)
  • 리스트

val authors = List("Raoul", "Mario", "Alan")
  • 집합

val numbers = Set(1, 1, 2, 3, 5, 8)
  • 튜플

    • 임의 크기의 튜플을 제공한다.

val raoul = ("Raoul", "+ 44 88000000")
val book = (2018, "hello", "world")
val numbers = (42, 1337, 0, 3, 14)

println(numbers._4)

불변과 가변

  • 컬렉션을 만들면 기본적으로 불변이기 때문에 변경이 불가능하다.

  • 불변 컬렉션을 갱신하려면 새로운 컬렉션에 기존 컬렉션과 새로운 원소를 넣어야 한다.

val numbers = Set(2, 5, 3)
val newNumbers = numbers + 8
  • 자바의 경우 Collections 클래스에서 Unmodifiable Collection을 만들 수 있는 메서드를 제공한다. 다만 이는 값을 고칠 수 있는 컬렉션을 감싸는 역할을 할 뿐이다.

Set<Integer> numbers = new HashSet<>();
Set<Integer> newNumbers = Collections.unmodifiableSet(numbers);

옵션

  • Java의 Optional과 유사하게 Scala에서는 Option을 사용할 수 있다.

  • Scala에도 null이 존재하기 때문에 Option을 사용해 null 예외를 방지할 수 있다.

def getCarInsuranceName(person: Option[Person], minAge: Int) =
    person.filter(_.getAge() >= minAge)
          .flatMap(_.getCar)
          .flatMap(_.getInsurance)
          .map(_.getName)
          .getOrElse("Unknown")

일급 함수

  • 스칼라의 함수는 인수로 전달하거나 결과로 반환하거나 변수에 저장 가능한 일급 함수이다.

  • Boolean을 반환하는 Predicate 타입의 함수를 통해 조건 기반 필터링 함수를 만들고 filter 메서드를 이용해 필터링할 수 있다.

def isShortTweet(tweet: String) : Boolean = tweet.length() < 20

val tweets = List("lol", "hello")
tweets.filter(isShortTweet).forEach(println)

익명 함수

  • 아래와 같이 Function1 타입을 사용하기 위해 익명 함수를 사용할 수 있다.

// Function1 객체
val isLongTweet : String => Boolean =
    new Function1[String, Boolean] {
        def apply(tweet: String): Boolean = tweet.length() > 60
    }

// 익명 함수
val isLongTweet : String => Boolean =
    (tweet: String) => tweet.length() > 60

// 사용
isLongTweet.apply("It's short tweet")
isLongTweet("It's short tweet") // f(a) 를 컴파일러가 f.apply(a) 로 자동 변환해주기도 함

클로저

  • 함수의 비 지역변수를 자유롭게 참조할 수 있는 함수의 인스턴스

  • 자바의 람다 표현식에서는 람다가 정의된 메서드의 지역 변수가 암시적으로 final 취급되어 변경할 수 없다.

public static void main(String[] args) {
    int count = 0;
    Runnable inc = () -> count+=1; // count가 변경 불가능해야 람다식에서 쓸 수 있다.
    inc.run();
    System.out.println(count);
}
  • 스칼라의 익명 함수는 변수를 캡쳐할 수 있다.

def main(args: Array[String]) {
    var count = 0
    val inc = () => count+=1 // count가 변경될 수 있어도 익명 함수에서 사용 가능하다.
    inc()
    println(count)
}
  • 프로그램을 쉽게 유지보수하고 병렬화하기 위해서는 변화를 피해야 하므로, 꼭 필요할 때에만 클로저 기능을 사용하는 것이 좋다.

커링

  • x,y를 입력받는 f 함수를 하나의 인자만 받는 g 함수, 나머지 인수를 받는 함수의 형태로 바꾸는 것

  • 즉, 여러 인수를 가진 함수를 인수의 일부만 받는 여러 개의 함수로 분할하는 것

// 일반 함수
def multiply(x: Int, y: Int) = x * y
val r = multiply(2, 10)

// 커링된 함수
def multiply(x: Int)(y: Int) = x * y
val r = multiply(2)(10)

val multiplyByTwo: Int => Int = multiply(2)
val r = multiplyByTwo(10)

클래스

  • 스칼라는 완전한 객체지향 언어이므로 자바와 유사하게 클래스를 만들고 객체로 인스턴스화 할 수 있다.

class Hello {
}

val hello = new Hello()

트레이트

  • 자바의 인터페이스를 대체하는 개념이다.

  • 추상 메서드와 기본 구현을 가진 메서드를 모두 정의할 수 있다.

  • 다중 상속이 가능하다.

trait Sized {
    var size : Int = 0
    def isEmpty() = size == 0
}

class Empty extends Sized

class Box
val b1 = new Box with Sized

println(new Empty().isEmpty()) // true
println(b1.isEmpty()) // true

Last updated