[Tour of Scala] Basics

2022. 10. 28. 00:35Dev/Scala

Tour of Scala 시리즈의 글은 다중패러다임(함수형 + OOP) 언어인 Scala 의 공식 튜토리얼 'Tour of Scala 문서를 톺아보고 학습해보는 글이다. 내용에 따라서 추가된 내용과 생략된 부분이 있다. (중요) 또한 학습은 Scala 3 을 기준으로 작성되었다. 몇몇 부분에서 2버전과는 다른 내용으로 진행하였다.
- 공식 web site : https://www.scala-lang.org
- Tour of Scala : https://docs.scala-lang.org/tour/tour-of-scala.html

 

Expressions

values / variables

다른 언어에도 동일한 개념이 있는 '상수(value)' 와 '변수(variable)'. 이 둘을 구분 짓는 가장 기본적인 개념은 re-assign 의 가능여부다. 

re-assign 이 가능하면 변수, 불가능하면 상수.

val x = 1		// assign to 'value' x
x = 2			// re-assign 'value' : compile error

var y = 200		// assign to 'variable' y
y = 0			// re-assign : OK
println(y)		// 0

val 과 var 는 assign 되는 값의 타입을 바탕으로 유추하여 결정되고 한번 정해진 타입은 다른 타입으로 변경될 수 없다. 또한 명시적으로 지정할 수도 있다.

var x = 0		// 변수 x 에 int 0 이 assign 되면서 x 의 type 은 Int 가 되었다
x = "ok"		// compile error! : 한번 assign 된 type 에 다른 type 을 assign 할 수 없다.

var y: Int = 0		// 명시적 type 지정

 

 

Blocks

여러 라인의 수식을 묶어둘때 `{ }` 을 이용하는데 가장 마지막 줄이 이 block의 result 가 된다.

println({
  val x = 1
  val y = 2
  x + y					// result of block : 3
})

// block 을 value 로 assign 해서 사용할 수도 있다.
val blockResult = {
  val x = 1
  val y = 2
  x + y
}

println(blockResult)	// result of value 'blockResult' : 3

또한 기본적으로 다른언어에서도 공통적으로 가지고 있는 value/variable 의 scope 를 제한하는 역할도 한다.

 

 

Functions

Functions are expressions that have parameters, and take arguments.

우리는 먼저 'parameter' 와 'argument' 에 대해서 먼저 간단하게 구분해야 function 에 대한 개념이 설 것 같다. 그렇지 않으면 위의 예제에서 단순히 blocks 로 감싼 blockResult value 와 혼동될 수도 있으니.

  • parameter : function signature에서 입력받은 값을 가르키기 위해 정의한 변수
  • argument : 미리 정의된 function 을 실행할때 넘겨주는 실제 값
val addOne = (x: Int) => x + 1			// x is parameter
val result = addOne(99)				// 99 is argument

함수란, 정의된 parameters 로 전달된 arguments 를 취하는 수식이다. 그 모양은 아래와 같다

val functionName = (parameter name: type) => expressions body
(x: Int) => x + 1				// 익명함수

val addOne = (x: Int) => x + 1			// 함수 이름 지정 가능

val addTwo = (x: Int) => {			// block 사용 가능
  println("x is : " + x)
  x + 2						// 마지막 줄이 이 함수의 result!
}

val addThree = (x: Int) =>			// block 없이 들여쓰기 만으로도 표현이 가능(like python)
  println("x is : " + x)
  x + 3
  
val minus = (x: Int, y: Int) => x - y		// 여러개의 파라미터 정의

val hundred = () => 100				// 파라미터 없이도 가능

 

 

Methods

일반적으로 함수(function) 의 하위 개념으로 method 가 존재한다. method 는 클래스에 종속된 함수다. 개념적으로는 이렇게 구분되지만 코드를 작성함에 있어서는 동일하게 작성 하게 되지만, scala 에서는 method 를 작성하는법이 별도로 존재한다.

def method이름(파리미터1, 파라미터2)(또다른 파라미터): 반환타입 =
  // ...body...
  // ...body...
  // 마지막 줄은 이 method 의 결과 반환값
  1. 시작할때 keyword 'def' 로 시작한다.
  2. parameter 는 다수의 () 를 허용한다. (예제로 확인)
  3. body 는 = 로 구분된다.
  4. 반환형(return type) 을 명시할 수 있다.
def addOne(x: Int): Int = x + 1

def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier

 

 

Classes

scala 에도 class가 있고 이를 작성하는 법은 조금 독특하다. (이는 kotlin 의 매개변수 생성방법과 유사하다)

이때 정의된 파라미터는 val(value) 이므로 class instance가 생성되면 변경할 수 없다.

class 클래스명(파라미터1, 파라미터2 ...):
class Greeter(prefix: String, suffix: String):
  def greet(name: String): Unit =
    println(prefix + name + suffix)

 

 

Case Classes

case class 는 scala 의 매우 독측한 타입으로 기본적으로 immutable 이며 클래스의 비교시에 value 를 비교하게 된다 (case 가 붙지 않은 보통의 class 는 단순 비교시 instance 의 참조값을 비교한다). 여담이지만 case class 를 보면서 data를 담을 훌륭한 타입이라고 생각한다.

작성법은 기본 class 작성법과 유사하고 앞에 keyword 'case' 가 붙고, body 가 없기 때문에 마지막에 ':'(콜론)을 붙히지 않는다.

case class 클래스명(파라미터1, 파라미터2...)
case class Pointer(x: Int, y: Int)


val temp1 = Pointer(1, 2)
val temp2 = Pointer(1, 2)
val temp3 = Pointer(100, 2)

println(temp1 == temp2)			// true
println(temp1 == temp3)			// false

 

 

Objects

단 하나의 instance 만을 가지는 singleton class 이다. 

object Util:
  private var x = 0

  def setX(tempX: Int): Unit =
    this.x = tempX

  def getX: Int = this.x

val util = Util					// 실제 reference 가 같다
val anotherUtil = Util				// 실제 reference 가 같다

println(util.getX)				// 0
util.setX(100)

println(anotherUtil.getX)			// 100

// util 과 anotherUtil 의 reference 가 같기 때문에 한쪽에서 x 의 값을 변경하면 다른 쪽에서도 영향을 받게 된다

 

 

Traits

'특성' 이라는뜻이 trait 은 scala 의 추상타입이다. 이는 Java 의 interface 와 매우 유사하다.

  • 기본적으로 구현해야 할 명세(method)만을 작성한다.
  • default 로 method 의 body를 가질 수 있다.
  • class 는 단 하나만 extend 할 수 있지만 trait 은 다중으로 extend 할 수 있다.
  • 재정의(override) 할 method 는 keyword 'overriede' 가 붙는다. (Java 에서는 명시적으로 keyword 를 붙히지 않는다. annotation(@Override) 으로 대체할 수 있으나 필수는 아니다.)

(이거 완전 Java interface 인데?!)

trait Greeter:
  def greet(name: String): Unit =
    println("Default : " + name)

class DefaultGreeter extends Greeter

class CustomGreeter extends Greeter:
  override def greet(name: String): Unit =
    println("Custom : " + name)

val greeter = DefaultGreeter()
greeter.greet("Jay 1")

val customGreeter = CustomGreeter()
customGreeter.greet("Jay 2")

 

 

Program Entry Point

작성한 프로그램이 최초 실행시의 진입점. C의 'int main()', Java 의 'public static void main()' 과 같지만 함수의 특정 네이밍이 강제되는 것은 아니다. 대신 annotation '@main' 이 붙은 함수가 그 진입점이 된다.

@main def hello() = println("Hello, Scala developer!")

 

'Dev > Scala' 카테고리의 다른 글

[Tour of Scala] Unified Types  (0) 2022.11.15