-
[TCA] Episodes 1 - FunctionsTCA/Episodes 2023. 5. 6. 18:09
Introduce
Point-Free๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ๊ณผ Swift๋ฅผ ๋ค๋ฃจ๋ ๋น๋์ค ์๋ฆฌ์ฆ์ ๋๋ค.
์ฌ๊ธฐ์๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ฐ๋ ์ ๋ํด์ ๋ง์ด ๋ค๋ฃจ๊ธฐ ๋๋ฌธ์, ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ํด์ ์ ์์๋ฌ์ผ ํฉ๋๋ค.
Swift์์๋ ์ผ๋ฐ์ ์ผ๋ก free function์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
์๋?! free function์ global namespace์ ๋ ๋ค๋๊ณ , ํ์ ์ ๋ฌถ์ฌ์์ง ์์์ messyํ๊ฒ ๋ณด์ผ ์๋ ์์ต๋๋ค. ํ์ง๋ง ์ฌ๊ธฐ์๋ free function์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค.
์ฐธ๊ณ ๋ก ์ฌ๊ธฐ์ ๋งํ๋ free function์ด๋, ๋ฉค๋ฒ ํจ์๊ฐ ์๋ ํจ์๋ฅผ ์๋ฏธํฉ๋๋ค.
struct NotFreeFunction { func hi() { } // Not Free Function } func bye() { } // Free Function func byeee() -> Int { return 1 } // Free Function|> ์ฐ์ฐ์ (ํ์ดํ ์ฐ์ฐ์)
์ผ์ชฝ์ A ํ์ ์ ๊ฐ์ด๊ณ ์ค๋ฅธ์ชฝ์ A์์ B๋ก์ ํจ์๋ก, ๊ฐ A์ ํจ์๋ฅผ ์ ์ฉํด์ B๋ฅผ ๋ฐํํฉ๋๋ค.
precedencegroup ForwardApplication { associativity: left } infix operator |>: ForwardApplication // ์ผ์ชฝ์ ํํ์์ ๋จผ์ ํ๊ฐ func |> <A, B>(a: A, f: (A) -> B) -> B { return f(a) } func incr(_ x: Int) -> Int { return x + 1 } print(2 |> incr) // 3 print(2 |> incr |> square) // 9์ฐ์ฐ์ ์ ์ ์กฐ๊ฑด
- ๊ธฐ์กด์ ์๋ฏธ๋ฅผ ๊ฐ๋ ์ฐ์ฐ์์ ์๋ก์ด ์๋ฏธ๋ฅผ ๋ถ์ฌํ์ฌ overload๋ฅผ ์ผ์ผ์ผ์๋ ์๋ฉ๋๋ค.
- ๊ฐ๋ฅํ ํ ์ ํ ๊ธฐ์ ์ ํ์ฉํ๊ณ , ์ฐ์ฐ์๊ฐ ๊ทธ ์๋ฏธ๋ฅผ ์ฐ์์ํค๋ ๋ฉ์ง ๋ชจ์์ ๊ฐ๋๋ก ํด์ผ ํฉ๋๋ค.
- ์์ ์ ์ํ |> ์ฐ์ฐ์์ ๊ฒฝ์ฐ, ๊ฐ์ ํจ์์ ์ ๋ฌํ๋ค๋ ๊ฒ์ ๋ช ํํ ์ค๋ช ํ๊ณ ์์ต๋๋ค. ๋ํ |>๋ F#, Elixir, Elm์์ ์ด๋ฏธ ์ฌ์ฉ๋๊ณ ์์ต๋๋ค.
- ๋งค์ฐ ํน์ ํ ๋๋ฉ์ธ์ ๊ตญํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ์ฐ์ฐ์๋ฅผ ๋ง๋ค์ด์๋ ์๋ฉ๋๋ค. ๋งค์ฐ ์ผ๋ฐ์ ์ธ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๊ณ , ์ฌ์ฌ์ฉํ ์ ์๋ ์ฐ์ฐ์๋ง ์ ์ํ๊ณ ๋์ ํด์ผ ํฉ๋๋ค.
์์ ์ ์ํ |> ์ฐ์ฐ์๋ ์์ 3๊ฐ์ง ์กฐ๊ฑด์ ๋ชจ๋ ๋ง์กฑํฉ๋๋ค.
>>> ์ฐ์ฐ์
ํจ์ ํฉ์ฑ ์ฐ์ฐ์๋ก, ํ ํจ์์ ์ถ๋ ฅ์ด ๋ค๋ฅธ ํจ์์ ์ ๋ ฅ์ ์ผ์นํ๋๋ก 2๊ฐ์ ํจ์๋ฅผ ๊ฐ์ ธ์์ ํฉ์ฑํ์ฌ ์์ ํ ์๋ก์ด ํจ์๋ฅผ ๋ง๋๋ ํจ์์ ๋๋ค.
precedencegroup: ForwardComposition { associativity: left higherThan: ForwardApplication } infix operator >>>: ForwardComposition func >>> <A, B, C>(f: @escaping (A) -> B, g: @escaping (B) -> C) -> ((A) -> C) { return { a in g(f(a)) } }3๊ฐ์ generic ๋งค๊ฐ๋ณ์ (A, B, C)์ ๋ํ generic ํจ์์ ๋๋ค.
A์์ B๋ก, B์์ C๋ก ์ ๋ฌํ๋ 2๊ฐ์ ํจ์๋ฅผ ์ฌ์ฉํ๊ณ , A์ ๊ฐ์ A๋ฅผ ์ทจํ๋ ํจ์๋ก ์ ๋ฌํ๋ ์๋ก์ด ํจ์๋ฅผ ๋ฐํํ๊ณ , ๋ฐํ ๊ฒฐ๊ณผ์ธ B๋ฅผ B๋ฅผ ์ทจํ๋ ํจ์๋ก ์ ๋ฌํจ์ผ๋ก์จ ๋ ํจ์๋ฅผ ๋ถ์ ๋๋ค.
>>> ์ฐ์ฐ์ ๋ํ ์ฐ์ฐ์ ์ ์ ์กฐ๊ฑด 3๊ฐ์ง๋ฅผ ๋ชจ๋ ๋ง์กฑํฉ๋๋ค.
์์ ์ฐ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
(square >>> incr)(3) // 10 2 |> incr >>> square // 9๋ฉ์๋ ํฉ์ฑ
๋ฉ์๋ ์ธ๊ณ์์ function composition์ ํ๋ ค๋ฉด,
ํ์ ์ ํ์ฅํ๊ณ ๊ฐ ๋ฉ์๋๋ค์ ํจ๊ป ๊ตฌ์ฑํ๋ ๋ค๋ฅธ ๋ฉ์๋๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค.
extension Int { func incrAndSquare() -> Int { return self.incr().square() } } 2.incrAndSquare() // 9๋ฐ๋ฉด, ํจ์ ํฉ์ฑ (function composition)์ ์์ฃผ ๊ฐ๋จํฉ๋๋ค.
incr >>> square๋ฟ๋ง ์๋๋ผ, ์์ ๊ตฌ์ฑ ์์๋ค ํ๋ ํ๋ ๋ชจ๋ ๋ค ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
2 |> incr >>> square 2 |> incr 2 |> square incr >>> square๋ฐ๋ฉด ๋ฉ์๋๋ ๊ฐ์ด ์์ผ๋ฉด ๋ฉ์๋๋ ๋ฉ์๋์ ํฉ์ฑ์ ์ฐธ์กฐํ ์ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฌ์ฉ์ฑ์ด ๋จ์ด์ง๋๋ค.
// valid 2.incr().square() // invalid .incr().square() incr().square()ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ free function์ ์ ๋ ฅ์ผ๋ก ๋ฐ๋ ์๋ง์ ํจ์๋ค์ด ์์ต๋๋ค.
์๋ฅผ ๋ค๋ฉด ๋ฐฐ์ด์๋ map์ด๋ผ๋ ๋ฉ์๋๊ฐ ์์ต๋๋ค.
[1, 2, 3].map // (transform: (Int) throws -> T) rethrows -> [T]์ด ๋ฉ์๋๋ ๋ฐฐ์ด์ ์์ ํ์ ์์ ๋ค๋ฅธ ํ์ T๋ก free function์ ๊ฐ์ ธ์์ ๋ณํํ๊ณ T ์์๋ก ์ด๋ฃจ์ด์ง ์๋ก์ด ํ์ ์ ๋ฐฐ์ด์ ๋ฐํํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์์ ํจ์๋ฅผ ์ ๋ฌํด์ ๋ค์๊ณผ ๊ฐ์ด ์ฆ๋ถํ๊ณ ์ ๊ณฑํ ์ ์์ต๋๋ค.
[1, 2, 3].map { ($0 + 1) * ($0 + 1) } // [4, 9, 16]๋ฉ์๋๋ก๋ง ์์ ํ ๋์๋ ์ฌ์ฌ์ฉ์ ํผํ๊ฒ ๋๋ ๊ฒ ๊ฐ์ต๋๋ค.
ํ์ง๋ง ํจ์๋ก ์์ ํ๋ฉด, ๋ฉ์๋๋ฅผ ์ง์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
[1, 2, 3] .map(incr) .map(square) // [4, 9, 16]์๋ก์ด ์์ ํจ์๋ฅผ ์ด๊ฑฐ๋ ์ธ์๋ฅผ ์ง์ ํ ํ์๋ ์์๊ณ , ์ด๋ฅผ “point-free” ์คํ์ผ์ด๋ผ๊ณ ํฉ๋๋ค. ํจ์๋ฅผ ์ ์ํ๊ฑฐ๋, ์ธ์๋ฅผ ์ง์ ํ๊ฑฐ๋, ์ฌ์ง์ด $0๋ฅผ ์ง์ ํ๋ ์ด๋ฌํ ๋ชจ๋ ๊ฒ๋ค์ ๋ชจ๋ “points”๋ผ๊ณ ํฉ๋๋ค.
“point-free” ์คํ์ผ ํ๋ก๊ทธ๋๋ฐ์ ํจ์์ ํฉ์ฑ์ ์ค์ ์ ๋๊ธฐ ๋๋ฌธ์, ์ฐ์ฐ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ ํ์์กฐ์ฐจ ์์ต๋๋ค. ์ด ์๋ฆฌ์ฆ์ ์ด๋ฆ๋ ๋ฐ๋ก ์ฌ๊ธฐ์ ๋ฐ์จ ๊ฒ์ ๋๋ค.
๋ฐฐ์ด์ square๋ก ๋งคํํ๊ธฐ ์ ์, incr๋ก ๋งคํํ๋ ๊ฒ์ ํจ์ ํฉ์ฑ๊ณผ ๋์ผํฉ๋๋ค.
์ฐ๋ฆฌ๋ ๋จ์ํ ํ ๋ฒ๋ง ๋งคํํ๊ณ , incr์ square๋ก forward-compose (์๋ฐฉํฅ ํฉ์ฑ)ํ๋ฉด ๋ฉ๋๋ค.
[1, 2, 3].map(incr >>> square) // [4, 9, 16]์ด๋ฅผ ํตํด ๋ฉ์๋์์๋ ๋ณด๊ธฐ ์ด๋ ค์ ๋ composition ์ฐ์ฐ์์ map์ ๊ด๊ณ๋ฅผ ํ์ธํ ์ ์์์ต๋๋ค.
์ฌ๊ธฐ์ map์ >>> ํฉ์ฑ ํ์ ์คํ๋ฉ๋๋ค.
๋ ํจ์์ ๊ฑธ์ณ map์ ๋ ๋ฒ ํฉ์ฑํ๋ ๊ฒ์, map์ ๋ํ ๋ ํจ์์ ํฉ์ฑ๊ณผ ๊ฐ์ต๋๋ค.
์ด์ ๊ฐ์ ํจํด์ ๋งค์ฐ ๋ง๊ณ ์์ผ๋ก๋ ๊ณ์ ์ดํด๋ณผ ์์ ์ ๋๋ค.
๊ฒฐ๋ก
์ค๋์ ์ฌ๊ธฐ์ free function์ผ๋ก global namespace๋ฅผ ์ค์ผ์์ผฐ์ต๋๋ค.
๋ฉ์๋๋ก ํจ์๋ฅผ ํฉ์ฑํ๋ ค๋ฉด, ๋ง์ ์์ ๊ณผ ์์ฉ๊ตฌ๊ฐ ํ์ํ๊ณ ๋ณต์กํฉ๋๋ค.
ํ์ง๋ง ํจ์๋ก ํฉ์ฑ์ ํ๋ฉด ํจ์ฌ ๋ ํธ๋ฆฌํ๋ฉฐ, ๊ฐ๋ ์ฑ ๋ํ ํจ์ฌ ์ข๊ฒ ์ ์งํ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ์ค Swift๋ global namespace์ ๋ํด ๊ฑฑ์ ํ ํ์๊ฐ ์์ต๋๋ค. ๋ค์ํ ๋ฐฉ์์ผ๋ก ํจ์์ ๋ฒ์๋ฅผ ์ง์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด์ฃ !
- ํ์ผ์ ๋น๊ณต๊ฐ์ธ ํจ์ ์ ์ ๊ฐ๋ฅ
- ๊ตฌ์กฐ์ฒด์ ์ด๊ฑฐํ์ ์ ์ ๋ฉค๋ฒ์ธ ํจ์ ์ ์ ๊ฐ๋ฅ
- ๋ชจ๋๋ก ๋ฒ์๊ฐ ์ง์ ๋ ํจ์ ์ ์ ๊ฐ๋ฅ. ๋์ผํ ํจ์ ์ด๋ฆ์ ์ ์ํ๋ ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์์ง๋ง, ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ชจ๋ ์ด๋ฆ์ผ๋ก ํ์ ํ ์ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํจ์๋ฅผ ๋๋ ค์ํ์ง ๋ง์ธ์!
์์ผ๋ก Point-Free์์๋ ํจ์๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ ๋๋ค.
Episode #0: We launched!
Point-Free is here, bringing you videos covering functional programming concepts using the Swift language. Take a moment to hear from the hosts about what to expect from this new series.
www.pointfree.co
Episode #1: Functions
Our first episode is all about functions! We talk a bit about what makes functions special, contrasting them with the way we usually write code, and have some exploratory discussions about operators and composition.
www.pointfree.co