2014년 6월 4일 수요일

Swift - 첫 인상

애플이 어제 WWDC에서 iOS 8을 발표하면서 Swift라는 새 프로그래밍 언어도 함께 발표했다. The Swift Programming Language라는 책도 함께 배포했길래 받아서 첫부분을 읽어 보았다. 그런데 한 중간 정도까지 읽고 딱 든 느낌은

이거 그냥 C#과 F#을 합쳐논 거잖아?

그런데 다르게 본 사람도 있었나 보다. 이 분에 따르면 Swift의 몇몇 문법이 Groovy와 매우 유사하다.

아무튼 여기서는 C#, F#과 얼마나 유사한지 몇가지를 비교해 보기로 한다.

기본 타입

Swift에도 structclass가 있는데 C#처럼 전자는 밸류 타입이고 후자는 레퍼런스 타입이다.

정수는 Int8, UInt8, Int16, UInt16, …, Int64, UInt64까지 있다. C#과 대체로 같지만 차이점이라면 Int/UInt가 32비트 시스템에선 Int32/UInt32이고 64비트 시스템에선 Int64/UInt64라는 것1. 이름 앞에 U가 붙은 것은 부호없는 타입이다.

실수는 FloatDouble이 있고 이는 C#의 float, double과 같다(물론 C#의 실수 타입 이름은 C/C++에서 왔음).

괄호와 세미콜론

C 계열의 언어지만 괄호와 세미콜론을 쓸 필요가 없을 때는 생략해도 된다. 이 문법은 Groovy의 것을 거의 그대로 물려 받았는데, C 계열의 언어는 아니지만 F#도 괄호와 세미콜론을 생략할 수 있다. 덕분에 Swift의 몇가지 문법은 F#과 상당히 유사하게 보인다.

변수와 상수

변수는 var를 앞에 붙이고, 상수는 let을 붙인다:

var myVariable: Int = 42
let myConstant: Double = 70

var 키워드는 C#의 것과 동일하고, let은 F#과 동일한데, 전반적인 형태는 F#과 매우 유사하다. 위의 myConstant를 F#에선 아래처럼 쓴다:

let myConstant: Double = 70.0

Swift는 특이하게도 리터럴 자체에 타입이 없다. 70이란 리터럴을 정수에 대입하면 정수, 실수에 대입하면 실수가 된다. 반면 F#은 타입 체크가 엄격해서 실수 리터럴은 반드시 소숫점을 붙여주어야 한다.

그리고 Swift는 기본적으로 암묵적 타이핑을 지원하기 때문에 변수와 상수의 타입을 생략해도—물론 생략해도 될 때만—된다:

var myVariable = 42
let myConstant = 70.0

F#에서도 대개 타입을 생략한다:

let mutable myVariable = 42
let myConstant = 70.0

이뉴머레이션

Swift의 enum은 여러 언어의 영향을 받았는데, 아래 세 가지 형태를 모두 지원한다:

  • C/C++/C#의 enum: 단순히 정수값에 이름을 붙일 때 쓰는 기초적인 타입이다.
  • 자바의 enum: C/C++의 형태를 기본으로 지원하지만 내부에 메쏘드까지 정의할 수 있어서 더 편리하다.
  • F#의 discriminated union: 같은 종류면서 서로 다른 파라미터를 갖는 타입을 하나로 묶어서 정의할 수 있다.

C/C++/C#에서처럼 정수를 대체하는 목적으로 쓸 때는 이름 뒤에 : Int를 붙여 준다(String이나 Double 등 다른 타입도 가능):

enum Number: Int {
    case Zero = 0
    case One, Two, Three, Four, Five, Six, Seven, Eight, Nine
}

Zero0으로 지정하고 나면 그 뒤에 나오는 이름들은 차례대로 1, 2, 3, …의 값을 갖게 된다. C/C++/C#에선 최초 값을 생략하면 0부터 매기는데 Swift에선 꼭 명시해 주어야 한다.

자바의 enum과 비슷하게 내부에 메쏘드를 정의할 수도 있다:

enum Number: Int {
    case Zero = 0
    case One, Two, Three, Four, Five, Six, Seven, Eight, Nine
    func getUnicodeName() -> String {
        switch self {
        case .Zero:
            return "DIGIT ZERO"
        ...
        }
    }
}

타입명 뒤에 : ...를 떼고 나면 F# discriminated union같은 복합 타입으로 쓸 수 있다:

enum Barcode {
    case UPCA(Int, Int64, Int)
    case QRCode(String)
}

var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
let anotherBarcode = .QRCode("ABCDEFGHIJKLMNOP")

UPCAQRCode는 같은 바코드의 일종이지만 전자는 파라미터를 3개 갖고, 후자는 1개만 갖는다. C/C++/C#/자바로는 이런 타입을 한번에 쉽게 만들어 내지 못한다.

한편 F#에선 아래처럼 쓴다:

type Barcode =
| UPCA of int * int64 * int
| QRCode of string

let mutable productBarCode = Barcode.UPCA(8, 8590951226L, 3)
let anotherBarCode = QRCode("ABCDEFGHIJKLMNOP")

함수

맨 앞에 func를 쓴 다음 함수 이름을 적고 파라미터 리스트를 괄호 안에 쓴 다음 리턴 타입은 -> 뒤에 적어 준다:

func add(a: Int, b: Int) -> Int {
    return a + b
}

F#에서는 아래와 같이 쓴다:

let add(a: int, b: int): int = a + b

F# Interactive에서 실행해 보면 둘 간의 유사점이 조금 더 느껴진다:

> let add(a, b) = a + b;;

val add2 : a:int * b:int -> int

Swift는 리턴 타입이 없는 함수는 -> 부분을 생략해도 된다. 이런 함수는 Void를 리턴 타입으로 갖는데, 표기할 때는 빈 튜플을 나타내는 ()로 쓴다. F#은 이에 상응하는 타입으로 unit이 있고 표기할 때는 같은 형태의 ()를 쓴다.

클로저

최신 언어답게 클로저는 기본 지원한다(책의 예제를 그대로 가져옴):

let digitNames = [
    0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10
    }
    return output
}

C#에서는 클로저보단 람다란 말을 많이 쓰는데, 바꿔 써보면 아래와 같다:

var digitNames = new Dictionary<int, string> {
    { 0, "Zero"}, { 1, "One" }, { 2, "Two" }, { 3, "Three" }, { 4, "Four" },
    { 5, "Five"}, { 6, "Six" }, { 7, "Seven" }, { 8, "Eight" }, { 9, "Nine" }
};
var numbers = new[] { 16, 58, 510 };
var strings = numbers.Select(
    number => {
        var output = "";
        while (number > 0) {
            output = digitNames[number % 10] + output;
            number /= 10;
        }
        return output;
    });

거의 똑같은데 세미콜론이 없고 괄호가 적은 Swift가 좀 더 간결해 보인다. Swift는 함수의 마지막 파라미터가 클로저일 경우 위에서처럼 괄호를 미리 닫거나 아예 생략해도 된다. 그리고 자바에서처럼 final 변수만 캡쳐가 된다든지, 사실상 final인 변수만 캡쳐가 된다든지 하는 이상한 제약은 없는 것 같다.

이외에도…

  • C#/F#처럼 연산자 오버로딩을 지원한다.
  • switch 문의 패턴 매칭은 F# match와 매우 유사하다.
  • getter, setter를 지원하는 프로퍼티 문법은 C# 프로퍼티를 닮았다2.
  • []로 인덱스를 넘겨줄 수 있는 서브스크립트는 C# 인덱서와 유사하다.

첫 인상

일단 괄호와 세미콜론이 별로 없다 보니 읽기가 편하다. 기존 언어에 있던 기능들을 그냥 잘 베끼는 것만으로도 한층 더 깔끔하고 강력한 언어를 만드는 것이 가능하단 생각. 그리고 아이폰이 안드로이드에 비해 유일하게(?) 떨어지던 것이 구닥다리 같은 개발 언어였는데, 일거에 역전이 된 것 같은 느낌이다3. 다만 Swift 덕분에 Objective-C의 잔여수명이 급격하게 단축될지도 모르는 건 Objective-C 애호가들에겐 좋지 않은 소식.


  1. C/C++의 최악의 결함 중 하나가 정수 타입의 크기가 플랫폼에 따라 멋대로 변한다는 것인데, 왜 그걸 따라했는지는 좀 이해가 되지 않는다.
  2. 다만 프로퍼티의 의미 자체가 C#과 약간 다르다.
  3. 구글은 어차피 오라클과 사이도 안좋은데 그냥 이번 기회에 C#으로 갈아 탄다면…

댓글 없음:

댓글 쓰기

댓글을 입력하세요. 링크를 걸려면 <a href="">..</a> 태그를 쓰면 됩니다. <b>와 <i> 태그도 사용 가능합니다.

게시한지 14일이 지난 글에는 댓글이 등록되지 않습니다. 날짜를 반드시 확인해 주세요.