๊ฐœ๋ฐœ/Android

Android ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ Part 2

๋„๋ฆฌ ๐ŸŸ 2021. 4. 8. 23:43

์ž‘์„ฑ์ผ: 2019.12.13


์›๋ฌธ Functional Programming for Android Developers — Part 2์„ ๋ฒˆ์—ญํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

 

์ด์ „ ํŒŒํŠธ์—์„œ Purity, Side effects, Ordering์— ๋Œ€ํ•ด ๋ฐฐ์› ๊ณ , ์ด๋ฒˆ ํŒŒํŠธ์—์„œ๋Š” immutability์™€ Concurrency์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Part 1์„ ์•„์ง ์ฝ์ง€ ์•Š์•˜๋‹ค๋ฉด, ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์„ธ์š”.

Immutability

Immutability๋Š” ๊ฐ’์ด ํ•œ ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด ์ ˆ๋Œ€ ๋ฐ”๋€” ์ˆ˜ ์—†๋‹ค๋Š” ๊ฐœ๋…์ž…๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์€ Car ํด๋ž˜์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

Java

public final class Car {
    private String name;

    public Car(final String name) {
        this.name = name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Kotlin

class Car(var name: String?)

Java ์ฝ”๋“œ์—์„œ๋Š” setter๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , Kotlin์—์„œ๋Š” name์ด ์ˆ˜์ • ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ ํ›„ Car์˜ ์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Java

Car car = new Car("BMW");
car.setName("Audi");

Kotlin

val car = Car("BMW")
car.name = "Audi"

์ƒ์„ฑ ํ›„ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ํด๋ž˜์Šค๋Š” immutable์ด ์•„๋‹™๋‹ˆ๋‹ค.

immutable๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” (Java ์ฝ”๋“œ์—์„œ):

  • name ๋ณ€์ˆ˜๋ฅผ final๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • setter๋ฅผ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ์ƒ์†๋ฐ›์•„ ๋‚ด๋ถ€๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋„๋ก ํด๋ž˜์Šค๋„ final๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Java

public final class Car {
    private final String name;

    public Car(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Kotlin์—์„œ๋Š” immutable ๋ณ€์ˆ˜๋กœ ์„ ์–ธ๋งŒ ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Kotlin

class Car(val name: String)

์ด์ œ ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ ํ•œ ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด ์•„๋ฌด๋„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ์ด์ œ immutable ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ Java์˜ getName() getter๋‚˜ Kotlin์˜ name ์ ‘๊ทผ์ž๋Š” ์–ด๋–จ๊นŒ์š”? ํ˜ธ์ถœํ•œ ์ชฝ์— name ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๋งž๊ฒŒ ๋ฐ˜ํ™˜ํ• ๊นŒ์š”? ๋งŒ์•ฝ ๋ˆ„๊ตฐ๊ฐ€ ์ด getter๋ฅผ ํ†ตํ•ด ์ฐธ์กฐ๋ฅผ ์–ป์€ ํ›„ name ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

 

Java์—์„œ๋Š” String์€ ๊ธฐ๋ณธ์ ์œผ๋กœ immutable ์ž…๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€ name์˜ ์ฐธ์กฐ๋ฅผ ์–ป์–ด ์ˆ˜์ •ํ•˜๋ฉด, name ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์›๋ž˜์˜ string์€ ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ immutable์ด ์•„๋‹Œ List ๊ฐ™์€ ๋‹ค๋ฅธ ํƒ€์ž…๋“ค์€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? Car ํด๋ž˜์Šค๊ฐ€ Driver ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ–๋„๋ก ์ˆ˜์ •ํ•ด๋ด…์‹œ๋‹ค.

Java

public final class Car {
    private final List<String> listOfDrivers;

    public Car(final List<String> listOfDrivers) {
        this.listOfDrivers = listOfDrivers;
    }

    public List<String> getListOfDrivers() {
        return listOfDrivers;
    }
}

์ด ๊ฒฝ์šฐ์—” ๋ˆ„๊ตฐ๊ฐ€ getListOfDrivers()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฐธ์กฐ๋ฅผ ์–ป์–ด ๋‚ด๋ถ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ๋”ฐ๋ผ์„œ ์ด ํด๋ž˜์Šค๋Š” mutable์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

immutable๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š”, getter์—์„œ ๋‚ด๋ถ€์˜ ๋ฆฌ์ŠคํŠธ์™€๋Š” ๋ณ„๋„์ธ deep copy๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋„˜๊ฒจ์„œ ์ƒˆ๋กœ์šด ๋ฆฌ์ŠคํŠธ๊ฐ€ ํ˜ธ์ถœ๋œ ์ชฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ˆ˜์ •๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋ฐ›์€ ๋ฆฌ์ŠคํŠธ ๋˜ํ•œ deep copy๋ฅผ ํ†ตํ•ด Car ์ƒ์„ฑ ํ›„์—๋Š” ์™ธ๋ถ€์—์„œ ์•„๋ฌด๋„ ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

Deep copy๋Š” ๊ฐ€์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋ชจ๋‘ ๋ณต์‚ฌํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ผ๋ฐ˜ String์ด ์•„๋‹Œ Driver ๊ฐ์ฒด์˜ ๋ฆฌ์ŠคํŠธ๋ผ๋ฉด, Driver๊ฐ์ฒด๋„ ๊ฐ๊ฐ ๋ชจ๋‘ ๋ณต์‚ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด mutableํ•œ ์›๋ž˜์˜ Driver ๊ฐ์ฒด์˜ ์ฐธ์กฐ๋ฅผ ๊ฐ–๋Š” ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค์—์„œ๋Š”, ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ด๋ฏธ immutable์ธ String์œผ๋กœ ๊ตฌ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์™€ ๊ฐ™์ด deep copy๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Java

public final class Car {
    private final List<String> listOfDrivers;

    public Car(final List<String> listOfDrivers) {
        this.listOfDrivers = deepCopy(listOfDrivers);
    }

    public List<String> getListOfDrivers() {
        return deepCopy(listOfDrivers);
    }

    private List<String> deepCopy(List<String> oldList) {
        List<String> newList = new ArrayList<>();
        for (String driver : oldList) {
            newList.add(driver);
        }
        return newList;
    }
}

์ด์ œ ์ด ํด๋ž˜์Šค๋Š” ์ •๋ง๋กœ immutable ํ•ฉ๋‹ˆ๋‹ค.

 

Kotlin์—์„œ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ํด๋ž˜์Šค์—์„œ ๋ฆฌ์ŠคํŠธ๋ฅผ immutable๋กœ ์„ ์–ธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉํ•  ๋•Œ๋„ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค(Java ์ฝ”๋“œ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ๋“ฑ์˜ ์˜ˆ์™ธ ์ƒํ™ฉ๋งŒ ์—†์œผ๋ฉด์š”).

Kotlin

class Car(val listOfDrivers: List<String>)

 

Concurrency

Part 1์—์„œ ์–˜๊ธฐํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” concurrency๋ฅผ ์‰ฝ๊ฒŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ด์ฃผ๊ณ , ๊ฐ์ฒด๊ฐ€ immutable์ด๋ฉด ์ˆ˜์ •์œผ๋กœ ์ธํ•œ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ผ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์ˆ˜ ํ•จ์ˆ˜์—์„œ์˜ ์‚ฌ์šฉ์ด ๋งค์šฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ๋ฅผ ๋ด…์‹œ๋‹ค. Car ํด๋ž˜์Šค์— Java๋Š” getNoOfDrivers() ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  Kotlin์—์„œ๋Š” mutable ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์™ธ๋ถ€์—์„œ driver์˜ ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์„ธ์š”:

Java

public class Car {
    private int noOfDrivers;

    public Car(final int noOfDrivers) {
        this.noOfDrivers = noOfDrivers;
    }

    public int getNoOfDrivers() {
        return noOfDrivers;
    }

    public void setNoOfDrivers(final int noOfDrivers) {
        this.noOfDrivers = noOfDrivers;
    }
}

Kotlin

class Car(var noOfDrivers: Int)

Car ์ธ์Šคํ„ด์Šค๋ฅผ Thread_1, Thread_2์ด๋ผ๋Š” ๋‘ ์Šค๋ ˆ๋“œ ๊ฐ„์— ๊ณต์œ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. Thread_1์€ Driver ์ˆ˜์— ๊ธฐ๋ฐ˜ํ•œ ์–ด๋–ค ๊ณ„์‚ฐ์„ ํ•˜๊ธฐ ์œ„ํ•ด Java์˜ noOfDrivers()๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ Kotlin์˜ noOfDrivers ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ Thread_2๋Š” noOfDrivers ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Thread_1์€ ์ด๋Ÿฐ ๋ณ€ํ™”์— ๋Œ€ํ•ด ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ณ„์‚ฐ๋“ค์€ ํ‹€๋ฆด ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

 

์•„๋ž˜ ์‹œํ€€์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์€ ์ด ์ด์Šˆ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค: 

์ด๊ฑด Read-Modify-Write ๋ฌธ์ œ๋ผ๊ณ  ํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ race condition์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ์ „์ ์ธ ๋ฐฉ๋ฒ•์€ locks์™€ mutexes๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณต์œ ๋œ ๋ฐ์ดํ„ฐ์— ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•˜๋„๋ก ํ•˜๊ณ , ํ•œ ์—ฐ์‚ฐ์ด ๋๋‚˜๋ฉด lock์„ ํ•ด์ œํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค(์šฐ๋ฆฌ ์˜ˆ์ œ์—์„œ๋Š”, Thread_1์ด ๊ณ„์‚ฐ์„ ๋๋‚ผ ๋•Œ๊นŒ์ง€ Car์— lock์„ ๊ฑธ๊ณ  ์žˆ๋Š” ๊ฒ๋‹ˆ๋‹ค).

 

์ด๋Ÿฐ lock ๊ธฐ๋ฐ˜์˜ ์ž์› ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•์€ ์•ˆ์ •์ ์œผ๋กœ ๋งŒ๋“ค์ด ์•„์ฃผ ์–ด๋ ต๊ณ , ๋ถ„์„ํ•˜๊ธฐ๋„ ๋งค์šฐ ํž˜๋“  ๋™์‹œ์„ฑ ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด deadlock๊ณผ livelock ๋•Œ๋ฌธ์— ๋ฉ˜๋ถ•์ด ์˜ค๊ณค ํ•˜์ฃ .

 

immutability(๋ถˆ๋ณ€์„ฑ)๊ฐ€ ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์ค„ ์ˆ˜ ์žˆ์„๊นŒ์š”? Car๋ฅผ immutable๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค:

Java

public final class Car {
    private final int noOfDrivers;

    public Car(final int noOfDrivers) {
        this.noOfDrivers = noOfDrivers;
    }

    public int getNoOfDrivers() {
        return noOfDrivers;
    }
}

Kotlin

class Car(val noOfDrivers: Int)

Thread_2๊ฐ€ Car ์ธ์Šคํ„ด์Šค๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์—, Thread_1์€ ์ด์ œ ๊ฑฑ์ •์—†์ด ๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Thread_2๊ฐ€ Car ์ธ์Šคํ„ด์Šค ์ˆ˜์ •์„ ์›ํ•˜๋ฉด, ์ˆ˜์ •์„ ์œ„ํ•ด ๋ณต์‚ฌํ•˜์—ฌ ์ƒˆ๋กœ ๋งŒ๋“ค ๊ฒƒ์ด๊ณ , Thread_1์€ ์ „ํ˜€ ์˜ํ–ฅ๋ฐ›์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. lock์ด ํ•„์š”์—†์Šต๋‹ˆ๋‹ค.

Immutability๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณต์œ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ thread-safeํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ •๋˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ๋“ค์€ ์ˆ˜์ •๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ˆ˜์ • ๊ฐ€๋Šฅํ•œ ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•  ๋• ์–ด๋–กํ•˜์ฃ ?

์‹ค์ œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ๋•, ๋งŽ์€ ์ธ์Šคํ„ด์Šค์— ์ˆ˜์ • ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋“ค์„ ๊ณต์œ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. noOfDrivers๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ๊ทธ ์—…๋ฐ์ดํŠธ๋ฅผ ์‹œ์Šคํ…œ ์ „๋ฐ˜์— ๋ฐ˜์˜ํ•ด์•ผ ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ๋ถ„๋ช… ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ์ฑ•ํ„ฐ์˜ functional architecture๋ฅผ ๋‹ค๋ฃฐ ๋•Œ, ์ƒํƒœ ๊ณ ๋ฆฝ์„ ์ด์šฉํ•˜๊ณ  ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ์‹œ์Šคํ…œ ๊ตฌ์„์œผ๋กœ ๋ชฐ์•„๋†”์„œ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

Persistent data structures

Immutable ๊ฐ์ฒด๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ, ์ œํ•œ์—†์ด ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ์— ๋ถ€ํ•˜๊ฐ€ ๋˜๊ณ  ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Functional programming์€ ๊ฐ์ฒด ์ƒ์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ immutability๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ ํ•ฉํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ตฌ์กฐ๊ฐ€ Persistent Data Structures์ž…๋‹ˆ๋‹ค.

 

Persistent Data Structure๋Š” ์ˆ˜์ •๋  ๋•Œ ์ž์‹ ์˜ ์ด์ „ ๋ฒ„์ „์„ ํ•ญ์ƒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์—ฐ์‚ฐ๋“ค์ด (๋ช…์‹œ์ ์œผ๋กœ) ๊ฐ™์€ ์ž๋ฆฌ์— ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š๊ณ , ํ•ญ์ƒ ์ƒˆ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํšจ์œจ์ ์ธ immutable์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์•„๋ž˜์™€ ๊ฐ™์€ ๋‹จ์–ด๋“ค์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค: reborn, rebate, realize, realizes, relief, red, redder.

 

๊ฐ๊ฐ์„ ๋ชจ๋‘ ๋”ฐ๋กœ ์ €์žฅํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ํ•„์š” ์ด์ƒ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์“ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํžˆ ๋ณด๋ฉด, ๋‹จ์–ด์— ๊ฒน์น˜๋Š” ๊ธ€์ž๋“ค์ด ๋งŽ์€ ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ด ๋‹จ์–ด๋“ค์„ ์•„๋ž˜์ฒ˜๋Ÿผ ํ•˜๋‚˜์˜ ํŠธ๋ฆฌ(Trie) ๊ตฌ์กฐ๋กœ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋ชจ๋“  ํŠธ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ์˜๊ตฌ์ ์ด์ง„ ์•Š์ง€๋งŒ persistent data structure ๊ตฌํ˜„์„ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค):

Persistent Data Structure๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๋‹จ์–ด๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด, ์ƒˆ ๋…ธ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ ์ ˆํ•œ ๊ณณ์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฐ์ฒด์—์„œ ๋…ธ๋“œ ์‚ญ์ œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ฐ์ฒด์—์„œ ์ฐธ์กฐ๋งŒ ์ œ๊ฑฐํ•  ๋ฟ ์‹ค์ œ ๋…ธ๋“œ๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„์žˆ์–ด์„œ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์€ ๊ณ„์† ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•„๋ฌด ๊ฐ์ฒด๋„ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด GC๋ฅผ ํ†ตํ•ด ์ „์ฒด ๊ตฌ์กฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ง€์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Java์—์„œ Persistent Data Structure๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์€ ์•„๋‹™๋‹ˆ๋‹ค. Clojure๋Š” JVM์—์„œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜ํ˜• ์–ธ์–ด์ด๊ณ , Persistent Data Structure๋ฅผ ์œ„ํ•œ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Android ์ฝ”๋“œ์—์„œ Clojure์˜ statndard lib์„ ์ง์ ‘ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ ์šฉ๋Ÿ‰๊ณผ ๋ฉ”์„œ๋“œ ์ˆ˜๊ฐ€ ์ƒ๋‹นํžˆ ํฝ๋‹ˆ๋‹ค. ๋” ์ข‹์€ ๋Œ€์•ˆ์€ ์ œ๊ฐ€ ์ „์— ๋ฐœ๊ฒฌํ•œ PCollections๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. 427๊ฐœ์˜ ๋ฉ”์„œ๋“œ์™€ 48kb dex size๋งŒ์œผ๋กœ, ์ถฉ๋ถ„ํžˆ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ๋กœ PCollections๋ฅผ ์ด์šฉํ•ด persistent linked list๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š” ๋ฒ•์ž…๋‹ˆ๋‹ค:

Java

ConsPStack<String> list = ConsPStack.empty();
System.out.println(list);  // []

ConsPStack<String> list2 = list.plus("hello");
System.out.println(list);  // []
System.out.println(list2); // [hello]

ConsPStack<String> list3 = list2.plus("hi");
System.out.println(list);  // []
System.out.println(list2); // [hello]
System.out.println(list3); // [hi, hello]

ConsPStack<String> list4 = list3.minus("hello");
System.out.println(list);  // []
System.out.println(list2); // [hello]
System.out.println(list3); // [hi, hello]
System.out.println(list4); // [hi]

์—ฌ๊ธฐ์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ๋ฐ”๋กœ ์ˆ˜์ •๋˜๋Š” ๋ฆฌ์ŠคํŠธ๋Š” ์—†๊ณ , ์ˆ˜์ •์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ชจ๋‘ ์ƒˆ๋กœ์šด ๋ณต์‚ฌ๋ณธ์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

 

PCollections๋Š” ๋‹ค์–‘ํ•œ use case์— ๋งž๊ฒŒ ๊ตฌํ˜„๋œ ํ‘œ์ค€ Persistent data structure๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ Java์˜ ํ‘œ์ค€ Collection ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€๋„ ๊ฝค ํŽธํ•˜๊ฒŒ ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค.

 

Kotlin์€ ์ด๋ฏธ immutable collection์ด ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ง€์›๋˜๋‹ˆ, Kotlin์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์ข‹์€ ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€๊ณ  ์žˆ๋Š” ๊ฒ๋‹ˆ๋‹ค.

 

Persistent data structures๋Š” ์•„์ฃผ ๊ด‘๋ฒ”์œ„ํ•œ ์ฃผ์ œ์ด๊ณ  ์ด ์„น์…˜์—์„œ๋Š” ๋น™์‚ฐ์˜ ์ผ๊ฐ๋งŒ ๋‹ค๋ค˜์„ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ข€ ๋” ์•Œ๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด, Chris Okasaki’s Purely Functional Data Structures๋ฅผ ์™„์ „ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.

Summary

Immutability์™€ Purity๋Š” ์•ˆ์ •์„ฑ์žˆ๊ณ  ๋™์‹œ์„ฑ์„ ๊ฐ–๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์งœ๋Š” ๋ฐ ์žˆ์–ด ๊ฐ•๋ ฅํ•œ ์กฐํ•ฉ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ํŒŒํŠธ์—์„œ๋Š” higher order functions์™€ closures์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.