Documents

Scala 시작하기

Scala 설치

$ brew install scala

Scala

  • 확장 가능한 언어(SCAlable LAnguage)의 약자

  • 2003년 마틴 오더스키(Martin Odersky) 교수와 EPFL(로잔 공과 대학)의 연구진이 JVM 플랫폼에서 FP와 OOP를 동시에 지원하는, 성능이 우수한 환경을 제공하기 위해 이 언어를 만듬

  • JVM 언어로 자바 런타임을 사용

  • REPL(Real-Eval-Print-Loop) 셸 제공

  • 정적 타입 지원

    정적 타입 언어

    컴파일 시간에 변수의 타입을 알게 되면 정적 타입 언어라고 함

타입

scala type

  • Any: 모든 타입들의 슈퍼타입, 직접적으로 두 개의 서브타입을 가지고 있음(AnyVal, AnyRef)

  • AnyVal: 모든 값 타입의 루트, 9개의 값 타입이 있음(Double, Float, Long, Int, Short, Byte, Char, Unit, Boolean)

  • AnyRef: 모든 참조 타입의 루트, java.lang.Object 에 해당

  • Noting: 모든 타입의 서브타입(바텀타입)

  • Null: 모든 참조 타입의 서브타입

  • Char: 숫자형 데이터 타입에도 등장할 수 있는 유일한 타입. String 타입의 기본이 되는 타입으로 단일 문자. Char 리터럴은 single quoter로 작성

  • Boolean: true 혹은 boolean. 다른 언어들과 달리 불이 아닌 다른 값을 변환하지 않음(숫자 0은 false와 다름)

  • Unit: 데이터 타입을 나타내는 대신 데이터가 없음을 나타냄. Unit 리터럴은 빈 괄호.

    val nada = ()

Tuple

  • 튜플은 둘 이상의 값을 가지는 순서가 있는 컨테이너

    구문: 튜플 생성
    // ( <값 1>, <값 2[, <값 3>...] )
    val info = (5, "Korben", true)
  • 튜플의 각 항목은 1부터 시작하는 인덱스를 이용하여 접근 가능

    val name = info._2
  • 화살표 연산자()를 사용하는 방식도 존재. 튜플에서 키-값 쌍을 표현하는 가장 보편적인 방법

    scala> val red = "red" -> "0xff0000"
    red: (String, String) = (red, 0xff0000)
    
    scala> val reversed = red._2 -> red._1
    reserved: (String, String) = (0xff0000,red)

String

  • multiline literal, interpolation(보간) 기능 추가

  • 자바와 달리 등호 연산자(==)는 객체 참조가 같은지 검사하지 않고 실제 값이 같은지 검사함

  • 여러 줄의 String인 double quoter 세 개를 이용하여 생성

    // 특수 문자의 시작을 나타내는 역슬래시를 인지하지 못함
    scala> val str = """She suggested reformatting the file
        | by replacing tabs(\t) with newlines (\n);
        | "Why do that?", he asked. """
    str: String =
    "She suggested reformatting the file
    by replacing tabs(\t) with newlines (\n);
    "Why do that?", he asked. "
  • 문자열 보간(string interpolation)은 첫 큰 따옴표 전에 접두사 s 를 추가하여 표기.

    scala> println(s"Pi, using 355/113, is about $approx.")
    Pi, using 355/113, is about 3.141593.
    
    scala> val item = "apple"
    item: String = apple
    
    scala> s"How do you like them ${item}s?"
    res0: String = How do you like them apples?
    
    scala> s"Fish n chips n vinegar, ${"pepper " * 3}salt"
    res1: String = Fish n chips n vinegar, pepper pepper pepper salt
  • 문자열 보간의 또 다른 포맷은 printf 표기법을 사용하는 것(접두사 f 사용)

    scala> val item = "apple"
    item: String = apple
    
    scala> f"I wrote a new $item%.3s today" (1)
    res0: String = I wrote a new app today
    
    scala> f"Enjoying this $item ${355/113.0}%.5f times today" (1)
    res1: String = Enjoying this apple 3.14159 times today
    1 printf 표기법이 읽기는 다소 어렵지만, 출력을 근본적으로 제어할 수 있는 장점

Rich Wrapper classes

  • 스칼라에는 원시(primitive) 데이터 타입 개념 없음

  • Java의 primitive 타입을 표현하는데 사용

문법

선언

  • 값과 변수는 관례상 소문자로 시작 → camelCas

  • 문자, 숫자, 연산자 기호 사용 가능

연산자(operator)

\u0020에서 \u007F 사이의 문자와 유니코드 Sm[Symbol/Math] 카테고리에서 대괄호와 마침표를 제외한 모든 문자

  • 스칼라에서는 연산자 오버로딩을 지원하지 않음

  • 연산자는 단순히 메서드

  • 메서드 이름이 : 으로 끝나면 오른쪽 결합이 된다.

    scala> val my_list = List(1, 2, 3, 4)
    my_list: List[Int] = List(1, 2, 3, 4)
    
    scala> val my_result1 = my_list :+ 5
    my_result1: List[Int] = List(1, 2, 3, 4, 5)
    
    scala> val my_result2 = 0 +: my_list
    my_result2: List[Int] = List(0, 1, 2, 3, 4)

Variable

  • mutable한 값

  • 변수에 재할당은 가능하지만 지정된 타입을 바꿀 수는 없음

Syntax: define var
var <name>: <type> = <literal>
Example
scala> var x: Int = 5
x: Int = 5

scala> x = 3
x: Int = 3

Value

  • immutable한 값

  • 데이터를 통해 타입 추론(type inference)이 가능하여 type을 생략할 수 있음

Syntax: define val
val <name>[: <type>] = <literal>
Example
scala> val x: Int = 5
x: Int = 5

scala> x = 3
         ^
       error: reassignment to val

Lazy Value

  • Regular Expression

    • 자바 클래스 java.util.regex.Pattern 에 기반함

    • String 타입은 정규 표현식을 지원하는 built-in 연산을 제공

      1. matches

        scala> "Froggy went a' courting" matches ".*courting"
        res0: Boolean = true
      2. replaceAll

        scala> "milk, tea, muck" replaceAll ("m[^ ]+k", "coffie")
        res0: String = coffie, tea, coffie
      3. replaceFirst

        scala> "milk, tea, muck" replaceFirst ("m[^ ]+k", "coffie")
        res0: String = coffie, tea, muck
    • 정규표현식으로 값 캡쳐하기

      • r 연산자를 호출하여 문자열을 정규 표현식 타입으로 전환

      • Regex 인스턴스를 반환

      • Capture Group은 정규 표현식 패턴을 기반으로 주어진 문자열에서 항목을 선택하고 이를 로컬 값으로 전환할 수 있게 해줌

      • 패턴은 최소 하나의 괄호로 정의된 캡처 그룹을 포함해야 함

      • 역슬래시를 인식하기 위해 multiline string을 사용

        scala> val input = "Enjoying this apple 3.14159 times today"
        input: String = Enjoying this apple 3.14159 times today
        
        scala> val pattern = """.* apple ([\d.]+) times .*""".r (1)
        pattern: scala.util.matching.Regex = .* apple ([\d.]+) times .* (2)
        
        scala> val pattern(amountText) = input
        amountText: String = 3.14159 (3)
        
        scala> val amount = amountText.toDouble
        amount: Double = 3.14159 (4)
        1 .r 을 붙혀 Regex 인스턴스를 반환 받음
        2 정규표현식 타입은 scala.util.matching.Regex 또는 간단히 util.matching.Regex
        3 val <regex value>(<name>) = <input string>
        4 Convert String type to Double type

조건문, 반복문

표현식

표현식(expression)은 값을 반환하는 코드의 단위

기본 구문
val <name>[: <type> = <expression>
var <name>[: <type> = <expression>

표현식 블록(expression block)

scala> val x = 5 * 20; val amount = x + 10
x: Int = 100
amount: Int = 110
expression block
scala> val amount = {val x = 5 * 20; x + 10} (1)
amount: Int = 110
1 블록의 마지막 표현식은 블록의 반환값을 결정한다.
expression block
scala> val amount = {
    val x = 5 * 20
    x + 10
}
amount: Int = 110

문장

  • 문장(statement)은 값을 반환하지 않는 표현식

  • 문장의 반환 타입은 값이 없음을 나타내는 Unit

  • 아래 코드의 값 정의는 어떤 것도 반환하지 않기 때문에 문장에 해당함

    scala> val x = 1
    x: Int = 1

If .. Else expression block

  • 스칼라는 하나의 'if’와 선택적인 'else' 블록만을 지원

  • 'else if' 블록을 단일 구성체로 인식하지 않음

  • 스칼라에서 if와 else 블록은 한 줄에 간결하게 맞아떨어지기 때문에 삼항 표현식이 실제로 필요하지 않음

// if (<부울식>) <표현식>
// if (<부울식>) <표현식> else <표현식>

scala> if (47 % 3 > 0 ) println("Not a multiple of 3")
Not a multiple of 3

scala> val result = if ( false ) "what does this return?"
result: Any = ()

scala> val x = 10; val y = 20
x: Int = 10
y: Int = 20

scala> val max = if (x > y) x else y
max: Int = 20

Match Expression

  • 자바의 'switch’문과 유사

  • 표현식은 단 하나의 패턴만 매칭할 수 있으므로 여러 개의 패턴을 한 번에 순서대로 매칭하는 형태의 제어 이동(fall-through)이 없음

    • 중간에 그 제거 이동에서 빠져나오는 'break' 문도 없음

  • 실제로 대부분 스칼라 개발자는 'if..else' 블록보다 매치 표현식을 더 선호. 표현력이 좋고 구문이 간결하기 때문

  • 여러 패턴을 하나로 결합하여 그 패턴 중 하나라도 일치하면 case 블록을 동작시키려면 패턴 대안(pattern alternative)

  • 매칭되는 것이 없는 에러를 예방하려면 wildcard match-all 패턴 사용

  • 패턴 가드(pattern guard)는 값 바인딩 패턴에 if 표현식을 추가하여 match 표현식에 조건부 로직을 섞어 쓸 수 있음

구문
<expression> match {
  case <pattern match> => <expression>
  [case...]
}
scala> val x = 10; val y = 20;
x: Int = 10
y: Int = 20

scala> val max = x > y match {
        case true => x
        case false => y
}
max: Int = 20
Match Expression
scala> val status = 500
status: Int = 500

scala> val message = status match {
        case 200 =>
            "ok"
        case 400 => {
            println("ERROR - we called the service incorrectly")
            "error"
        }
        case 500 => {
            println("ERROR - the service encountered an error")
            "error"
        }
}
ERROR - the service encountered an error
message: String = error
Match Expression with pattern alternative(패턴 대안)
scala> val day = "MON"
day: String = MON

scala> val kind = day match {
    case "MON" | "TUE" | "WED" | "THU" | "FRI" =>
        "weekday"
    case "SAT" | "SUN" =>
        "weekend"
}
kind: String = weekday
Match Expression with value binding
scala> val message = "OK"
message: String = OK

scala> val status = message match {
    case "OK" => 200
    case other => { (1)
        println(s"Couldn't parse $other")
        -1
    }
}
status: Int = 200
1 other 는 case 블록이 유지되는 동안 정의
Match Expression with wildcard
scala> val message = "Unauthorized"
message: String = Unauthorized

scala> val status = message match {
    case "Ok" => 200
    case _ => {
        println(s"Couldn't parse $message")
        -1
    }
}
Couldn't parse Unauthorized
status: Int = -1

함수

  • return 키워드는 선택적이기 때문에 스칼라 컴파일러는 return 키워드가 없으면 마지막 할당 값을 리턴하게 설계됨

중첩 함수

nested function

for comprehensions

Implicit

monad

Collection

Import문

import math._ (1)
import math.{sin, cos} (2)
import scala.collection.mutable.{Map => MutableMap} (3)
import math.{cos => _, _} (4)
1 _(underscore) 는 Java의 *(asterisk)와 비슷하게 와일드카드로, math 패키지의 모든 것을 import 한다.
2 { } 를 통해 math 패키지에 일부만 import 한다.
3 이름을 변경해서 import 할 수 있다.
4 math 패키지를 모두 import하지만 cos는 안보이게 한다.

컬렉션

Currying

객체지향

생성자

보조 생성자

오브젝트

  • 정적메서드 대신 오브젝트

  • 클래스의 컴패니언

트레이트

trait

  • 트레이트는 타입 파라미터만 가질 수 있다

추상클래스

타입 어스크립션 type ascription

final 키워드

제네릭