자바스크립트 개발자를 위한 Scala

자바스크립트 개발자를 위한 Scala (Scala for JavaScript Developers)

이 페이지는 JavaScript와 Scala 프로그래밍 언어 사이의 비교를 제공해요. JavaScript를 알고 Scala를 배우고 싶은 프로그래머를 대상으로 하며, 특히 JavaScript 언어 기능이 Scala에서는 어떻게 보이는지 예시를 보여주는 데 집중해요.

출처: Scala 3 Book — Scala for JavaScript Developers

개요 (Overview)

이 섹션은 뒤따르는 섹션들에 대한 비교적 간단한 소개와 요약을 제공해요. JavaScript와 Scala의 유사점과 차이점을 높은 수준에서 보여주고, 코드를 쓸 때 매일 겪게 될 차이점도 소개할게요.

높은 수준의 유사점 (High-level similarities)

높은 수준에서 Scala는 JavaScript와 이런 유사점을 공유해요.

  • 둘 다 고수준 프로그래밍 언어로 간주되어, 포인터나 수동 메모리 관리 같은 저수준 개념에 신경 쓸 필요가 없어요
  • 둘 다 비교적 단순하고 간결한 문법을 가져요
  • 둘 다 C/C++/Java 스타일의 중괄호 문법으로 메서드와 다른 코드 블록을 작성하는 걸 지원해요
  • 둘 다 객체지향 프로그래밍(OOP)을 위한 (클래스 같은) 기능을 포함해요
  • 둘 다 함수형 프로그래밍(FP)을 위한 (람다 같은) 기능을 포함해요
  • JavaScript는 브라우저와 Node.js 같은 다른 환경에서 실행돼요. Scala의 한 갈래인 Scala.js는 JavaScript를 대상으로 하므로 Scala 프로그램이 같은 환경에서 실행될 수 있어요
  • 개발자들은 Node.js로 JavaScript와 Scala 양쪽에서 서버 측 애플리케이션을 작성해요. Play Framework 같은 프로젝트는 Scala에서도 서버 측 애플리케이션을 작성하게 해 줘요
  • 두 언어 모두 비슷한 if 문, while 루프, for 루프를 가져요
  • 이 Scala.js 페이지에서 시작하면 React, Angular, jQuery와 많은 다른 JavaScript 및 Scala 라이브러리를 지원하는 수십 개의 라이브러리를 찾을 수 있어요
  • JavaScript 객체는 가변이에요. Scala 객체는 명령형 스타일로 쓸 때 가변이 될 수 있어요
  • JavaScript와 Scala 모두 비동기 계산의 결과를 처리하는 수단으로 프로미스(promise) 를 지원해요. (Scala 동시성은 future와 promise를 사용해요)

높은 수준의 차이점 (High-level differences)

또한 높은 수준에서 JavaScript와 Scala의 차이점은 다음과 같아요.

  • JavaScript는 동적 타입이고, Scala는 정적 타입이에요
  • Scala가 정적 타입이긴 하지만, 타입 추론 같은 기능 덕분에 동적 언어처럼 느껴져요. (뒤따르는 예시들에서 보게 될 거예요)
  • Scala 관용구는 기본적으로 불변(immutability)을 선호해요. 불변 변수와 불변 컬렉션을 쓰도록 권장되죠
  • Scala는 간결하지만 읽기 쉬운 문법을 가져요. 우리는 이것을 표현적(expressive) 이라고 불러요
  • Scala는 순수 OOP 언어라서 모든 객체가 클래스의 인스턴스이고, ++=처럼 연산자처럼 보이는 기호들은 사실 메서드예요. 즉 연산자로 동작하는 자신만의 메서드를 만들 수 있다는 뜻이죠
  • 순수 OOP 언어이자 순수 FP 언어인 Scala는 OOP와 FP의 융합을 권장해요. 로직에는 함수를, 모듈성에는 불변 객체를 쓰는 방식이죠
  • Scala는 최첨단의 서드파티 오픈소스 함수형 프로그래밍 라이브러리를 가져요
  • Scala의 모든 것은 표현식(expression) 이에요. if 문, for 루프, match 표현식, 심지어 try/catch 표현식까지 모두 반환 값을 가져요
  • Scala Native 프로젝트는 “시스템(systems)” 수준 코드를 작성하게 해 주고, 네이티브 실행 파일로도 컴파일해요

프로그래밍 수준의 차이점 (Programming level differences)

더 낮은 수준에서, 코드를 쓸 때 매일 보게 될 차이점은 다음과 같아요.

  • Scala 변수와 파라미터는 val(불변, JavaScript의 const처럼) 또는 var(가변, JavaScript의 varlet처럼)로 정의돼요
  • Scala는 줄 끝에 세미콜론을 사용하지 않아요
  • Scala는 정적 타입이지만, 많은 상황에서 타입을 선언할 필요가 없어요
  • Scala는 트레이트를 인터페이스로, 그리고 믹스인(mixin) 을 만들기 위해 사용해요
  • 단순한 for 루프 외에도, Scala에는 알고리즘에 기반한 결과를 만들어내는 강력한 for 컴프리헨션(for comprehension)이 있어요
  • 패턴 매칭과 match 표현식은 코드를 쓰는 방식을 바꿀 거예요
  • Scala의 컨텍스트적 추상화(contextual abstractions)용어 추론(term inference) 은 여러 기능을 모아 제공해요
    • 확장 메서드는 특정 스코프에서만 사용 가능하게 함으로써, 모듈성을 깨지 않으면서 닫힌 클래스에 새 기능을 추가할 수 있게 해 줘요. (코드의 다른 영역을 오염시킬 수 있는 몽키패칭과는 다르죠)
    • given 인스턴스는 컴파일러가 여러분을 위해 코드를 합성(synthesize)할 때 사용할 수 있는 용어를 정의하게 해 줘요
    • 타입 안전성과 다중 보편 동등성(multiversal equality)은 동등성 비교를 — 컴파일 시간에 — 의미 있는 비교로만 제한하게 해 줘요
    • by-name 파라미터, 중위 표기법(infix notation), 선택적 괄호, 확장 메서드, 고차 함수 같은 기능 덕분에, 자신만의 “제어 구조”와 DSL을 만들 수 있어요
  • 책 전반에 걸쳐 읽게 될 다른 좋은 기능들도 많아요. 케이스 클래스, 컴패니언 클래스와 객체, 매크로, 유니언인터섹션 타입, 다중 파라미터 리스트, 이름 있는 인자 등이 있죠

변수와 타입 (Variables and Types)

주석 (Comments)

JavaScript Scala
//
/* ... */
/** ... */
//
/* ... */
/** ... */

가변 변수 (Mutable variables)

JavaScript Scala
let // 현재 가변에 선호
var // 옛 가변 스타일
var // 가변 변수에 사용

불변 값 (Immutable values)

JavaScript Scala
const val

Scala의 경험칙은 특별히 가변 변수가 필요할 이유가 없는 한 val로 변수를 선언하는 거예요.

네이밍 표준 (Naming standards)

JavaScript와 Scala는 일반적으로 같은 CamelCase 네이밍 표준을 사용해요. 변수는 myVariableName처럼, 메서드는 lastIndexOf처럼, 클래스와 객체는 AnimalPrintedBook처럼 이름을 붙이죠.

문자열 (Strings)

JavaScript와 Scala에서 문자열의 많은 사용은 비슷하지만, Scala는 단순 문자열에는 큰따옴표만, 여러 줄 문자열에는 삼중 따옴표만 사용해요.

문자열 기본 (String basics)

JavaScript Scala
// use single- or double-quotes
let msg = 'Hello, world';
let msg = "Hello, world";
// use only double-quotes
val msg = "Hello, world"

보간 (Interpolation)

JavaScript Scala
let name = 'Joe';
// JavaScript uses backticks
let msg = \Hello, ${name}`;`
val name = "Joe"
val age = 42
val weight = 180.5
// use s before a string for simple interpolation
println(s"Hi, $name") // "Hi, Joe"
println(s"\${1 + 1}") // "2"
// f before a string allows printf-style formatting.
// this example prints:
// "Joe is 42 years old, and weighs"
// "180.5 pounds."
println(f"$name is $age years old, and weighs $weight%.1f pounds.")

보간을 사용한 여러 줄 문자열 (Multiline strings with interpolation)

JavaScript Scala
let name = "joe";
let str = \``<br>Hello, ${name}.<br>``This is a multiline string.
\;`
val name = "Martin Odersky"
val quote = s"""
|$name says
|Scala is a fusion of
|OOP and FP.
""".stripMargin.replaceAll("\\n", " ").trim
// result:
// "Martin Odersky says Scala is a fusion of OOP and FP."

JavaScript와 Scala는 또한 charAt, concat, indexOf와 그 외 많은 문자열 작업 메서드를 비슷하게 가져요. \n, \f, \t 같은 이스케이프 문자도 두 언어에서 동일해요.

숫자와 산술 연산 (Numbers and arithmetic)

숫자 연산자는 JavaScript와 Scala 사이에서 비슷해요. 가장 큰 차이는 Scala가 ++-- 연산자를 제공하지 않는다는 거예요.

숫자 연산자 (Numeric operators)

JavaScript Scala
let x = 1;
let y = 2.0;

let a = 1 + 1;
let b = 2 - 1;
let c = 2 * 2;
let d = 4 / 2;
let e = 5 % 2;
val x = 1
val y = 2.0

val a = 1 + 1
val b = 2 - 1
val c = 2 * 2
val d = 4 / 2
val e = 5 % 2

증가와 감소 (Increment and decrement)

JavaScript Scala
i++;
i += 1;
i--;
i -= 1;
i += 1;
i -= 1;

아마도 가장 큰 차이는 +- 같은 “연산자”가 Scala에서는 연산자가 아니라 사실 메서드 라는 점이에요. Scala 숫자에는 또한 이런 관련 메서드들이 있어요.

var a = 2
a *= 2      // 4
a /= 2      // 2

Scala의 Double 타입은 JavaScript의 기본 숫자 타입에 가장 가깝고, Int는 부호 있는 32비트 정수 값을, BigInt는 JavaScript의 bigint에 대응해요.

이것들은 Scala IntDouble 값이에요. 타입을 명시적으로 선언할 필요가 없다는 점을 주목하세요.

val i = 1     // Int
val d = 1.1   // Double

필요에 따라 다른 숫자 타입도 사용할 수 있어요.

val a: Byte = 0    // Byte = 0
val a: Double = 0  // Double = 0.0
val a: Float = 0   // Float = 0.0
val a: Int = 0     // Int = 0
val a: Long = 0    // Long = 0
val a: Short = 0   // Short = 0

val x = BigInt(1_234_456_789)
val y = BigDecimal(1_234_456.890)

불리언 값 (Boolean values)

두 언어 모두 불리언 값에 truefalse를 사용해요.

JavaScript Scala
let a = true;
let b = false;
val a = true
val b = false

날짜 (Dates)

날짜는 두 언어에서 또 흔히 사용되는 타입이에요.

현재 날짜 얻기 (Get the current date)

JavaScript Scala
let d = new Date();
// result:
// Sun Nov 29 2020 18:47:57 GMT-0700 (Mountain Standard Time)
// different ways to get the current date and time
import java.time.*
val a = LocalDate.now
// 2020-11-29
val b = LocalTime.now
// 18:46:38.563737
val c = LocalDateTime.now
// 2020-11-29T18:46:38.563750
val d = Instant.now
// 2020-11-30T01:46:38.563759Z

다른 날짜 지정하기 (Specify a different date)

JavaScript Scala
let d = Date(2020, 1, 21, 1, 0, 0, 0);
let d = Date(2020, 1, 21, 1, 0, 0);
let d = Date(2020, 1, 21, 1, 0);
let d = Date(2020, 1, 21, 1);
let d = Date(2020, 1, 21);
val d = LocalDate.of(2020, 1, 21)
val d = LocalDate.of(2020, Month.JANUARY, 21)
val d = LocalDate.of(2020, 1, 1).plusDays(20)

이 경우 Scala는 Java에 포함된 날짜/시간 클래스를 사용해요. JavaScript와 Scala 사이에 많은 날짜/시간 메서드가 비슷해요. 더 자세한 내용은 java.time 패키지를 참고하세요.

함수 (Functions)

JavaScript와 Scala 모두에서 함수는 객체이므로 기능은 비슷하지만, 문법과 용어는 조금 달라요.

이름 있는 함수, 한 줄 (Named functions, one line)

JavaScript Scala
function add(a, b) {
return a + b;
}
add(2, 2); // 4
// technically this is a method, not a function
def add(a: Int, b: Int) = a + b
add(2, 2) // 4

이름 있는 함수, 여러 줄 (Named functions, multiline)

JavaScript Scala
function addAndDouble(a, b) {
// imagine this requires
// multiple lines
return (a + b) * 2
}
def addAndDouble(a: Int, b: Int): Int =
// imagine this requires
// multiple lines
(a + b) * 2

Scala에서 Int 반환 타입을 보여주는 건 선택 사항이에요. add 예시에서는 표시하지 않고, addAndDouble 예시에서는 표시해서 두 접근 방식을 모두 볼 수 있게 했어요.

익명 함수 (Anonymous functions)

JavaScript와 Scala 모두 익명 함수를 정의할 수 있고, 그걸 다른 함수와 메서드에 전달할 수 있어요.

화살표와 익명 함수 (Arrow and anonymous functions)

JavaScript Scala
// arrow function
let log = (s) => console.log(s)
// anonymous function
let log = function(s) {
console.log(s);
}
// use either of those functions here
function printA(a, log) {
log(a);
}
// a function (an anonymous function assigned to a variable)
val log = (s: String) => console.log(s)
// a scala method. methods tend to be used much more often,
// probably because they’re easier to read.
def log(a: Any) = console.log(a)
// a function or a method can be passed into another
// function or method
def printA(a: Any, log: Any => Unit) = log(a)

Scala에서는 첫 번째 문법을 써서 함수를 정의하는 경우가 드물어요. 대신 사용 지점 바로 그 자리에서 익명 함수를 정의하는 경우가 많죠. 많은 컬렉션 메서드가 고차 함수이고 함수 파라미터를 받으므로, 이렇게 코드를 씁니다.

// map method, long form
List(1,2,3).map(i => i * 10)   // List(10,20,30)

// map, short form (which is more commonly used)
List(1,2,3).map(_ * 10)        // List(10,20,30)

// filter, short form
List(1,2,3).filter(_ < 3)      // List(1,2)

// filter and then map
List(1,2,3,4,5).filter(_ < 3).map(_ * 10)   // List(10, 20)

클래스 (Classes)

Scala에는 클래스와 케이스 클래스가 모두 있어요. 클래스 는 JavaScript 클래스와 비슷하고, 일반적으로 OOP 스타일 애플리케이션에서 쓰도록 설계됐어요. (FP 코드에서도 쓸 수 있지만요.) 케이스 클래스 는 추가 기능이 있어서 FP 스타일 애플리케이션에서 매우 유용해요.

다음 예시는 여러 타입을 열거형으로 만든 다음, OOP 스타일의 Pizza 클래스를 정의하는 방법을 보여줘요. 마지막에는 Pizza 인스턴스를 만들어 사용하죠.

// create some enumerations that the Pizza class will use
enum CrustSize:
  case Small, Medium, Large

enum CrustType:
  case Thin, Thick, Regular

enum Topping:
  case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions

// import those enumerations and the ArrayBuffer,
// so the Pizza class can use them
import CrustSize.*
import CrustType.*
import Topping.*
import scala.collection.mutable.ArrayBuffer

// define an OOP style Pizza class
class Pizza(
  var crustSize: CrustSize,
  var crustType: CrustType
):

  private val toppings = ArrayBuffer[Topping]()

  def addTopping(t: Topping): Unit =
    toppings += t

  def removeTopping(t: Topping): Unit =
    toppings -= t

  def removeAllToppings(): Unit =
    toppings.clear()

  override def toString(): String =
    s"""
      |Pizza:
      |  Crust Size: ${crustSize}
      |  Crust Type: ${crustType}
      |  Toppings:   ${toppings}
    """.stripMargin

end Pizza

// create a Pizza instance
val p = Pizza(Small, Thin)

// change the crust
p.crustSize = Large
p.crustType = Thick

// add and remove toppings
p.addTopping(Cheese)
p.addTopping(Pepperoni)
p.addTopping(BlackOlives)
p.removeTopping(Pepperoni)

// print the pizza, which uses its `toString` method
println(p)

인터페이스, 트레이트, 상속 (Interfaces, traits, and inheritance)

Scala는 트레이트를 인터페이스로, 그리고 믹스인을 만들기 위해 사용해요. 트레이트는 메서드와 필드를 포함해 추상 멤버와 구체적 멤버를 모두 가질 수 있어요.

이 예시는 두 트레이트를 정의하고, 그 트레이트를 확장·구현하는 클래스를 만든 다음, 그 클래스의 인스턴스를 만들어 사용하는 방법을 보여줘요.

trait HasLegs:
  def numLegs: Int
  def walk(): Unit
  def stop() = println("Stopped walking")

trait HasTail:
  def wagTail(): Unit
  def stopTail(): Unit

class Dog(var name: String) extends HasLegs, HasTail:
  val numLegs = 4
  def walk() = println("I’m walking")
  def wagTail() = println("⎞⎜⎛  ⎞⎜⎛")
  def stopTail() = println("Tail is stopped")
  override def toString = s"$name is a Dog"

// create a Dog instance
val d = Dog("Rover")

// use the class’s attributes and behaviors
println(d.numLegs)   // 4
d.wagTail()          // "⎞⎜⎛  ⎞⎜⎛"
d.walk()             // "I’m walking"

제어 구조 (Control Structures)

JavaScript의 ===!== 사용을 제외하면, 비교와 논리 연산자는 JavaScript와 Scala에서 거의 동일해요.

비교 연산자 (Comparison operators)

JavaScript Scala
== ==
=== ==
!= !=
!== !=
> >
< <
>= >=
<= <=

논리 연산자 (Logical operators)

JavaScript Scala
&& || ! && || !

if/then/else 표현식

JavaScript와 Scala의 if/then/else 문은 비슷해요. Scala 2에서는 거의 동일했지만, Scala 3부터는 중괄호가 더 이상 필요하지 않아요. (그래도 여전히 사용할 수 있어요.)

if 문, 한 줄 (if statement, one line)

JavaScript Scala
if (x == 1) { console.log(1); } if x == 1 then println(x)

if 문, 여러 줄 (if statement, multiline)

JavaScript Scala
if (x == 1) {
console.log("x is 1, as you can see:")
console.log(x)
}
if x == 1 then
println("x is 1, as you can see:")
println(x)

if, else if, else

JavaScript Scala
if (x < 0) {
console.log("negative")
} else if (x == 0) {
console.log("zero")
} else {
console.log("positive")
}
if x < 0 then
println("negative")
else if x == 0
println("zero")
else
println("positive")

if에서 값 반환하기 (Returning a value from if)

JavaScript는 삼항 연산자를, Scala는 평소처럼 if 표현식을 사용해요.

JavaScript Scala
let minVal = a < b ? a : b; val minValue = if a < b then a else b

메서드 본문으로서의 if (if as the body of a method)

Scala 메서드는 매우 짧은 경향이 있어서, if를 메서드 본문으로 쉽게 사용할 수 있어요.

JavaScript Scala
function min(a, b) {
return (a < b) ? a : b;
}
def min(a: Int, b: Int): Int =
if a < b then a else b

Scala 3에서도 원한다면 여전히 “중괄호” 스타일을 쓸 수 있어요. 예를 들어 if/else-if/else 표현식을 이렇게 작성할 수 있죠.

if (i == 0) {
  println(0)
} else if (i == 1) {
  println(1)
} else {
  println("other")
}

루프 (Loops)

JavaScript와 Scala 모두 while 루프와 for 루프가 있어요. Scala에는 원래 do/while 루프가 있었지만, 언어에서 제거되었어요.

while 루프 (while loop)

JavaScript Scala
let i = 0;
while (i < 3) {
console.log(i);
i++;
}
var i = 0;
while i < 3 do
println(i)
i += 1

Scala 코드는 원한다면 이렇게도 쓸 수 있어요.

var i = 0
while (i < 3) {
  println(i)
  i += 1
}

다음 예시들은 JavaScript와 Scala의 for 루프를 보여줘요. 이 컬렉션들이 있다고 가정한답니다.

// JavaScript
let nums = [1, 2, 3];

// Scala
val nums = List(1, 2, 3)

for 루프, 한 줄 (for loop, single line)

JavaScript Scala
// newer syntax
for (let i of nums) {
console.log(i);
}
// older
for (i=0; i<nums.length; i++) {
console.log(nums[i]);
}
// preferred
for i <- nums do println(i)
// also available
for (i <- nums) println(i)

for 루프, 본문 여러 줄 (for loop, multiple lines in the body)

JavaScript Scala
// preferred
for (let i of nums) {
let j = i * 2;
console.log(j);
}
// also available
for (i=0; i<nums.length; i++) {
let j = nums[i] * 2;
console.log(j);
}
// preferred
for i <- nums do
val j = i * 2
println(j)
// also available
for (i <- nums) {
val j = i * 2
println(j)
}

for 루프의 여러 제네레이터 (Multiple generators in a for loop)

JavaScript Scala
let str = "ab";
for (let i = 1; i < 3; i++) {
for (var j = 0; j < str.length; j++) {
for (let k = 1; k < 11; k += 5) {
let c = str.charAt(j);
console.log(\i: ${i} j: ${c} k: ${k}`);<br> }<br> }<br>}`
for
i <- 1 to 2
j <- 'a' to 'b'
k <- 1 to 10 by 5
do
println(s"i: $i, j: $j, k: $k")

가드를 가진 제네레이터 (Generator with guards)

가드(guard) 는 for 표현식 안에 있는 if 표현식을 가리키는 이름이에요.

JavaScript Scala
for (let i = 0; i < 10; i++) {
if (i % 2 == 0 && i < 5) {
console.log(i);
}
}
for
i <- 1 to 10
if i % 2 == 0
if i < 5
do
println(i)

for 컴프리헨션 (for comprehension)

for 컴프리헨션은 yield를 사용해 값을 반환하는 for 루프예요. Scala에서 자주 사용돼요.

JavaScript Scala
N/A val list =
for
i <- 1 to 3
yield
i * 10
// result: Vector(10, 20, 30)

switch와 match (switch & match)

JavaScript에 switch 문이 있는 곳에, Scala에는 match 표현식이 있어요. Scala의 다른 모든 것처럼 이것은 진정한 표현식 이라서 결과를 반환해요.

val day = 1

// later in the code ...
val monthAsString = day match
  case 1 => "January"
  case 2 => "February"
  case _ => "Other"

match 표현식은 각 case 문에서 여러 매치를 처리할 수 있어요.

val numAsString = i match
  case 1 | 3 | 5 | 7 | 9 => "odd"
  case 2 | 4 | 6 | 8 | 10 => "even"
  case _ => "too big"

메서드 본문으로도 사용할 수 있어요.

def isTruthy(a: Matchable) = a match
  case 0 | "" => false
  case _ => true

def isPerson(x: Matchable): Boolean = x match
  case p: Person => true
  case _ => false

match 표현식에는 패턴 매칭 옵션이 훨씬 더 많아요.

컬렉션 클래스 (Collections classes)

Scala에는 다양한 요구를 위한 다양한 컬렉션 클래스가 있어요.

흔한 불변 시퀀스는 List, Vector예요. 흔한 가변 시퀀스는 Array, ArrayBuffer이구요. Scala에는 가변과 불변 MapSet도 있어요.

흔한 Scala 컬렉션 타입은 이렇게 만들어요.

val strings = List("a", "b", "c")
val strings = Vector("a", "b", "c")
val strings = ArrayBuffer("a", "b", "c")

val set = Set("a", "b", "a") // result: Set("a", "b")
val map = Map(
  "a" -> 1,
  "b" -> 2,
  "c" -> 3
)

컬렉션의 메서드 (Methods on collections)

다음 예시들은 Scala 컬렉션을 다루는 여러 가지 방법을 보여줘요.

리스트 채우기 (Populating lists)

// to, until
(1 to 5).toList                   // List(1, 2, 3, 4, 5)
(1 until 5).toList                // List(1, 2, 3, 4)

(1 to 10 by 2).toList             // List(1, 3, 5, 7, 9)
(1 until 10 by 2).toList          // List(1, 3, 5, 7, 9)
(1 to 10).by(2).toList            // List(1, 3, 5, 7, 9)

('d' to 'h').toList               // List(d, e, f, g, h)
('d' until 'h').toList            // List(d, e, f, g)
('a' to 'f').by(2).toList         // List(a, c, e)

// range method
List.range(1, 3)                  // List(1, 2)
List.range(1, 6, 2)               // List(1, 3, 5)

List.fill(3)("foo")               // List(foo, foo, foo)
List.tabulate(3)(n => n * n)      // List(0, 1, 4)
List.tabulate(4)(n => n * n)      // List(0, 1, 4, 9)

시퀀스의 함수형 메서드 (Functional methods on sequences)

// these examples use a List, but they’re the same with Vector
val a = List(10, 20, 30, 40, 10)      // List(10, 20, 30, 40, 10)
a.contains(20)                        // true
a.distinct                            // List(10, 20, 30, 40)
a.drop(2)                             // List(30, 40, 10)
a.dropRight(2)                        // List(10, 20, 30)
a.dropWhile(_ < 25)                   // List(30, 40, 10)
a.filter(_ < 25)                      // List(10, 20, 10)
a.filter(_ > 100)                     // List()
a.find(_ > 20)                        // Some(30)
a.head                                // 10
a.headOption                          // Some(10)
a.init                                // List(10, 20, 30, 40)
a.last                                // 10
a.lastOption                          // Some(10)
a.slice(2,4)                          // List(30, 40)
a.tail                                // List(20, 30, 40, 10)
a.take(3)                             // List(10, 20, 30)
a.takeRight(2)                        // List(40, 10)
a.takeWhile(_ < 30)                   // List(10, 20)

// map, flatMap
val fruits = List("apple", "pear")
fruits.map(_.toUpperCase)             // List(APPLE, PEAR)
fruits.flatMap(_.toUpperCase)         // List(A, P, P, L, E, P, E, A, R)

val nums = List(10, 5, 8, 1, 7)
nums.sorted                           // List(1, 5, 7, 8, 10)
nums.sortWith(_ < _)                  // List(1, 5, 7, 8, 10)
nums.sortWith(_ > _)                  // List(10, 8, 7, 5, 1)

List(1,2,3).updated(0,10)             // List(10, 2, 3)
List(2,4).union(List(1,3))            // List(2, 4, 1, 3)

// zip
val women = List("Wilma", "Betty")    // List(Wilma, Betty)
val men = List("Fred", "Barney")      // List(Fred, Barney)
val couples = women.zip(men)          // List((Wilma,Fred), (Betty,Barney))

Scala에는 사용할 수 있는 메서드가 훨씬 더 많아요. 이 모든 메서드의 이점은 다음과 같아요.

  • 문제를 해결하려고 커스텀 for 루프를 쓸 필요가 없어요
  • 다른 사람의 코드를 읽을 때 그들의 커스텀 for 루프를 읽을 필요가 없어요. 이런 흔한 메서드만 발견하면 되므로, 서로 다른 프로젝트의 코드를 읽기 더 쉬워요

튜플 (Tuples)

같은 리스트에 여러 데이터 타입을 넣고 싶을 때 JavaScript에서는 이렇게 해요.

let stuff = ["Joe", 42, 1.0];

Scala에서는 이렇게 하죠.

val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))

Scala에서 이런 타입들은 튜플(tuple)이라 불러요. 보이듯이 하나 이상의 요소를 담을 수 있고, 요소들은 서로 다른 타입일 수 있어요. 요소 접근은 List, Vector, Array 요소에 접근하는 것과 똑같아요.

d(0)   // "eleven"
d(1)   // 11

열거형 (Enumerations)

JavaScript에는 열거형이 없지만, 이렇게 할 수는 있어요.

let Color = {
  RED: 1,
  GREEN: 2,
  BLUE: 3
};
Object.freeze(Color);

Scala 3에서는 열거형으로 꽤 많은 것을 할 수 있어요. 그 코드의 동등한 것을 만들 수 있어요.

enum Color:
  case Red, Green, Blue

파라미터화된 enum을 만들 수도 있어요.

enum Color(val rgb: Int):
  case Red   extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue  extends Color(0x0000FF)

사용자 정의 enum 멤버도 만들 수 있어요.

enum Planet(mass: Double, radius: Double):
  case Mercury extends Planet(3.303e+23, 2.4397e6)
  case Venus   extends Planet(4.869e+24,6.0518e6)
  case Earth   extends Planet(5.976e+24,6.37814e6)
  // more planets here ...

  private final val G = 6.67300E-11
  def surfaceGravity = G * mass / (radius * radius)
  def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

Scala.js DOM 코드 (Scala.js DOM Code)

Scala.js는 브라우저에서 사용할 수 있는 JavaScript 코드로 컴파일되는 Scala 코드를 작성하게 해 줘요. 이 접근 방식은 TypeScript, ReScript와 같이 JavaScript로 컴파일되는 다른 언어와 비슷해요.

필요한 라이브러리를 포함하고 프로젝트에서 필요한 패키지를 import 하면, Scala.js 코드 작성은 JavaScript 코드 작성과 매우 비슷해 보여요.

// show an alert dialog on a button click
jQuery("#hello-button").click{() =>
  dom.window.alert("Hello, world")
}

// define a button and what should happen when it’s clicked
val btn = button(
  "Click me",
  onclick := { () =>
    dom.window.alert("Hello, world")
  })

// create two divs with css classes, an h2 element, and the button
val content =
  div(cls := "foo",
    div(cls := "bar",
      h2("Hello"),
      btn
    )
  )

// add the content to the DOM
val root = dom.document.getElementById("root")
root.innerHTML = ""
root.appendChild(content.render)

Scala가 타입 안전한 언어임에도 위 코드에는 아무 타입도 선언되지 않았다는 점을 주목하세요. Scala의 강력한 타입 추론 능력 덕분에 Scala 코드가 동적 타입처럼 보이는 경우가 많아요. 하지만 실제로는 타입 안전하므로, 개발 주기 초기에 여러 종류의 오류를 잡을 수 있어요.

다른 Scala.js 자료 (Other Scala.js resources)

Scala.js 웹사이트에는 Scala.js를 사용하는 데 관심 있는 JavaScript 개발자를 위한 훌륭한 튜토리얼 모음이 있어요. 초기 튜토리얼 중 몇 가지를 소개할게요.

Scala 고유의 개념 (Concepts that are unique to Scala)

JavaScript에는 현재 대응물이 없는 Scala의 다른 개념들이 있어요.

  • 컨텍스트적 추상화와 관련된 거의 모든 것
  • 메서드 기능:
    • 다중 파라미터 리스트
    • 메서드 호출 시 이름 있는 인자 사용
    • 트레이트를 인터페이스로 사용
    • 케이스 클래스
    • 컴패니언 클래스와 객체
    • 자신만의 제어 구조와 DSL을 만드는 능력
    • match 표현식과 패턴 매칭의 고급 기능
    • for 컴프리헨션
    • 중위(infix) 메서드
    • 매크로와 메타프로그래밍
  • 그 외 더 많은 것…

더 알아보기 (Learn more)