Skip to main content

Command Palette

Search for a command to run...

๋ฒˆ์—ญ-Kotlin pearls 1) Scope Functions

๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์Šค์ฝ”ํ”„ ํ•จ์ˆ˜๋“ค์— ๋Œ€ํ•œ ์ดํ•ด๋„๋ฅผ ๋†’์ด์ž

Updated
โ€ข3 min read
๋ฒˆ์—ญ-Kotlin pearls 1) Scope Functions

์›๋ฌธ : [Kotlin pearls 1]Scope Function]


์šฐ๋ฆฌํ•œํ…Œ Kotlin scope ํ•จ์ˆ˜ ๊ด€๋ จ ๊ธ€๋“ค์ด ํ•„์š”ํ• ๊นŒ์š”?

์‚ฌ์‹ค ์ „ ๊ทธ๋ ‡๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. scope ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๊ธ€๋“ค์ด ์ •๋ง ๋งŽ์ง€๋งŒ, ์ด ํ•จ์ˆ˜๋“ค์„ ํ™œ์šฉํ•ด์„œ ๋ฆฌํŒฉํ† ๋ง์„ ํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ์–ด๋–ค scope ํ•จ์ˆ˜๊ฐ€ ์ ํ•ฉํ•œ์ง€ ๊ณ ๋ฅด๋Š” ๋ฒ•์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์ฐพ์ง€ ๋ชป ํ–ˆ๊ฑฐ๋“ ์š”.

์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค:

1_i-Uo4RQDd6tNi2Mj8WSUoA.jpeg

Use this๋Š” a single object์— method๋ฅผ (์ฃผ๋กœ) ํ˜ธ์ถœํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์ด object์— ๋Œ€ํ•œ ์ฐธ์กฐ๋Š” this ์ฃ .

Pass it์€ object๋ฅผ ๋‹ค๋ฅธ methods/functions๋กœ ๋„˜๊ธด๋‹ค๋Š” ๊ฒƒ์œผ๋กœ, ํ•ด๋‹น object์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ผ it์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Result๋Š” block์—์„œ ๋‚˜์˜จ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

Side-effects๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š๊ณ , Unit(์ฆ‰, void)๋ฅผ ๋ฐ˜ํ™˜, ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ Fluent Interface style๋กœ ๊ฒฐํ•ฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2 Questions

๊ทธ๋ž˜์„œ object(๋Œ€์ƒ object)๊ฐ€ ์ˆ˜ํšŒ ์‚ฌ์šฉ๋  ๋•Œ, ์ฝ”๋“œ๋ฅผ ๋‹จ์ˆœํ™” ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์„ ์Šค์Šค๋กœ์—๊ฒŒ ํ•ด๋ณด๋ฉด ๋ฉ๋‹ˆ๋‹ค:

1. ํ•ด๋‹น object์— method๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š”๊ฐ€? ํ˜น์€ ๋‹ค๋ฅธ method/function์˜ arguments๋กœ ๋„˜๊ฒจ์•ผ ํ•˜๋Š”๊ฐ€?

2. ๊ฒฐ๊ณผ๊ฐ’์ด ํ•„์š”ํ•œ๊ฐ€? ํ˜น์€ ์ƒํƒœ(state)๋งŒ ๋ณ€๊ฒฝํ•ด๋„ ๋˜๋Š”๊ฐ€?


How to choose a correct Scope Function

apply

ํ•œ object์— ์—ฌ๋Ÿฌ ๋ฒˆ method ํ˜ธ์ถœํ•ด์•ผ ํ•˜๊ณ , ๋ฐ˜ํ™˜๊ฐ’์ด ํ•„์š”์—†์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค(setter ๊ฐ™์ด).

์•„๋ž˜ ์ฝ”๋“œ์—์„œ ?๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ๋‚˜๋จธ์ง€ ํ™•์žฅ ํ•จ์ˆ˜๋“ค๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋Œ€์ƒ object๊ฐ€ null์ด๋ฉด, block์€ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. block ๋‚ด๋ถ€์˜ object๋Š” ํ•ญ์ƒ non-nullable type์ž…๋‹ˆ๋‹ค.

fun updateItem(itemId: String, newName: String, newPrice: Double) = itemsMap.get(itemId)?.apply { 
    enabled = true
    desc = newName
    price = newPrice
}

with

์•„๋ž˜ ์ฝ”๋“œ์— ๋Œ€ํ•ด ์ž ๊น ์„ค๋ช…ํ•˜์ž๋ฉด, apply์™€ ๊ฑฐ์˜ ์ •ํ™•ํ•˜๊ฒŒ ๋˜‘๊ฐ™์ด ์ž‘๋™ํ•˜๋Š”๋ฐ, object๊ฐ€ nullable type์ผ ๋•Œ์—๋„ object๋ฅผ nullable์ธ ์ƒํƒœ๋กœ block์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ๋ณดํ†ต ํŠน์ • object๋ฅผ ๊ฐ–๊ณ  ํ˜ธ์ถœํ•˜๋Š” method๋“ค์˜ ์–‘์ด ๋„ˆ๋ฌด ๋งŽ์•„์„œ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋ณ€์ˆ˜๋ช…์ด ๊ธธ ๋•Œ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด:

// ... long method
val total = with(ridicurioslyLongNameOfFrameworkSingleton){ 
   setSomething(a)
   setSomethingElse(b)
// do other things...
   getTotal()
}
// ...

let

let์€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜์ผ ๊ฒƒ ๊ฐ™์€๋ฐ(์ ์–ด๋„ ์ €๋Š” ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค), ํŠนํžˆ ?์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. value๊ฐ€ null์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์ฃ . ์ฝ”๋“œ ์ƒ null ์ฒดํฌํ•˜๋Š” ์ฝ”๋“œ(if(obj == null) {โ€ฆ})์ œ๊ฑฐ๋ฅผ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์ฃ :

fun fullName(firstName: String, middleName: String?, familyName: String) = middleName?.let{ 
   "$firstName ${it.first()}. $familyName"
} ?: "$firstName $familyName"

run

run์€ object๋ฅผ ๊ฐ–๊ณ  ์—ฌ๋Ÿฌ method๋“ค์„ ํ˜ธ์ถœํ•œ ๋’ค ๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

fun cartTotal(cart: Cart, items: List<CartItem>) = cart.run{ 
   items.foreach{addItem(it)}
   calcTotal()
}

also

๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์ด ์žˆ๋Š”๋ฐ, ๊ทธ ์ „์— โ€œ๋‹ค๋ฅธ ์ž‘์—…์—๋„โ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค: ์˜ˆ๋ฅผ ๋“ค์–ด:

fun createUser(userName: Stirng): Int =
    myAtomicInt.getAndIncrement().also { users.put(it, userName) }

Scope Function Signatures

์ฐธ๊ณ ๋กœ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }

์ฝ”ํ‹€๋ฆฐ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ scope ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ฃผ๋ฉด, โ€œ๊ทธ๊ฑธ ์จ๋„ ๋ณ„ ์ด์ ๋„ ์—†๊ณ , ๊ทธ๋ƒฅ ๋ณต์žกํ•ดโ€๋ผ๋Š” ๋ฐ˜๋ก ์„ ํ”ํžˆ ๋“ฃ์Šต๋‹ˆ๋‹ค.

scope ํ•จ์ˆ˜ ์‚ฌ์šฉ ์‹œ, ์ž‘์€ ์ด์ ์€ ์ฝ”๋“œ ๋ช‡ ์ค„ ์ ˆ์•ฝํ•˜๋Š” ๊ฒƒ์ด๊ณ , ๋” ํฐ ์ด์ ์€ ์ž„์‹œ ๋ณ€์ˆ˜(temporary variables)์˜ ์‚ฌ์šฉ์„ ํ”ผํ•˜๊ฒŒ ํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž„์‹œ ๋ณ€์ˆ˜๋“ค์€ ๋ณดํ†ต FPFunctional Programming ์Šคํƒ€์ผ์—์„œ ๋ณ„๋กœ ์ข‹์ง€ ์•Š์€ ๋ฐฉ์‹์œผ๋กœ ์—ฌ๊ฒจ์ง€๋ฉฐ, ๋ณ€์ˆ˜๋ช…์„ ํ—ท๊ฐˆ๋ฆฌ๊ฑฐ๋‚˜ ์ฝ”๋“œ ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ ์‹œ, ์‚ฌ์†Œํ•œ ๋ฒ„๊ทธ์˜ ์›์ธ์ด ๋ฉ๋‹ˆ๋‹ค.(๋ฌผ๋ก  ๊ทธ๋Ÿฐ ์‹ค์ˆ˜๋ฅผ ์•ˆ ํ•˜๊ฒ ์ง€๋งŒ์š”!)

๋˜ํ•œ FP ๋ฐฉ์‹์—์„œ (์ข€ ๋” ์ •ํ™•ํ•œ ์˜๋„ ์ „๋‹ฌํ•œ๋‹ค๋Š” ์˜๋ฏธ์—์„œ) ํ•จ์ˆ˜ ์‚ฌ์šฉ ์‹œ, single expression declaration form(์ด์ฒ˜๋Ÿผ ์ƒ๊ธด.. fun f = ...)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€๋ฐ, scope ํ•จ์ˆ˜๊ฐ€ ์ด๋ฅผ ๋„์™€์ค๋‹ˆ๋‹ค.

Reading Room

Part 1 of 1