고차원 Lift에 대해 알아보자. (map)
lift
은 기본적으로 1변수를 변환할 수 있는 함수이다.(T, U) -> V
로 정의되는 transform 함수를 lift
으로 변환할 수 있을까?ft = T를 감싸는 Functor, 여기서는 Optional
fu = U를 감싸는 Functor, 여기서는 Optional
f_u = u를 고정시킨 함수. T를 받아 V를 반환하는 함수
f_t = t를 고정시킨 함수. U를 받아 V를 반환하는 함수
(T, U) -> V
를(F<T>, U) -> F<V>
로 변환하는 lift1 함수를 만들어라.
F
을 Optional로 생각해보자.internal func lift<T, U>(transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
return { (input: Optional<T>) -> Optional<U> in
switch input {
case .none:
return .none
case .some(let wrapped):
return .some(transform(wrapped))
}
}
}
func lift1<T, U, V>(transform: @escaping (T, U) -> V) -> (Optional<T>, U) -> Optional<V> {
return { (ft: Optional<T>, u: U) in
let f_u = { (t: T) -> V in
transform(t, u)
}
return lift(transform: f_u)(ft)
}
}
u
는 제대로 주어져 있으니, Optional<T>
인 ft
의 원래 알맹이를 받아 V
를 반환하는 함수를 만들고,lift
함수에 넣어나온 결과인 Optional<T> -> Optional<V>
형태의 함수에 ft
를 넣어주어 원하는 결과를 반환 받자.
(T, U) -> V
를(T, F<U>) -> F<V>
로 변환하는 lift2 함수를 만들어라.
F
을 Optional로 생각해보자.internal func lift<T, U>(transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
return { (input: Optional<T>) -> Optional<U> in
switch input {
case .none:
return .none
case .some(let wrapped):
return .some(transform(wrapped))
}
}
}
func lift2<T, U, V>(transform: @escaping (T, U) -> V) -> (T, Optional<U>) -> Optional<V> {
return { (t: T, fu: Optional<U>) in
let f_t = { (u: U) -> V in
return transform(t, u)
}
return lift(transform: f_t)(fu)
}
}
Optional<U>
로 들어왔으니, t
를 고정시키고 fu
안에 있는 값인 U
를 받아 V
를 반환하는 변환 함수를 작성하자.lift
에 넣을 준비가 되었으니 (Functor로 감싸져 있지 않음) 이걸 넣어 나오는 결과인Optional<U> -> Optional<V>
형태의 함수에 fu
를 넣어 Optional<V>
형태의 값을 받는 "함수"를 반환받자.
(T, U) -> V
를(F<T>, F<U>) -> F<F<V>>
로 변환하는 lift2d 함수를 만들어라.
F
을 Optional로 생각해보자.lift1
, lift2
를 섞으면 된다. ㅋㅋ((T, U) -> V)
|
lift1(적용)
|
((F<T>, U) -> F<V>)
|
lift2(적용)
|
((F<T>, F<U>) -> F<F<V>>)
func lift2d(T, U, V)(transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
let lift1Result = lift1(transform: transform) // 결과 타입: (Optional<T>, U) -> Optional<V>
let lift2Result = lift2(transform: lift1Result) // 결과 타입: (Optional<T>, Optional<U>) -> Optional<V>
return lift2Result
}
// 또는
func lift2d<T, U, V>(transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
lift2(transform: lift1(transform: transform))
}
((T, U) -> V) -> ((F<T>, F<U>) -> F<F<V>>)
이 형태로 만들 수 있음은 알았다.
(T, U) -> V
를(F<T>, F<U>) -> F<F<V>>
로 변환하는 lift2d 함수를 만들어라. 단 lift1, lift2를 사용하지 않고 lift으로만 만들어라.
F
을 Optional로 생각해보자.internal func lift<T, U>(_ transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
return { (input: Optional<T>) -> Optional<U> in
switch input {
case .none:
return .none
case .some(let wrapped):
return .some(transform(wrapped))
}
}
}
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
return { (ft: Optional<T>, fu: Optional<U>) in
let g = { (t: T) -> Optional<V> in
let f_t = { (u: U) -> V in
return transform(t, u)
}
return lift(transform: f_t)(fu)
}
return lift(transform: g)(ft)
}
}
// 또는
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
return { (ft: Optional<T>, fu: Optional<U>) in
lift { (t: T) -> Optional<V> in
lift { (u: U) -> V in
transform(t, u)
}(fu)
}(ft)
}
}
// 또는
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
{ (ft: Optional<T>, fu: Optional<U>) in
lift { (t: T) -> Optional<V> in
lift { (u: U) -> V in
transform(t, u)
}(fu)
}(ft)
}
}
// 또는
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
{ (ft, fu) in
lift { (t: T) -> Optional<V> in
lift { (u: U) -> V in
transform(t, u)
}(fu)
}(ft)
}
}
// 또는
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (Optional<T>, Optional<U>) -> Optional<Optional<V>> {
{ ft, fu in
lift { (t: T) -> Optional<V> in
lift { (u: U) -> V in
transform(t, u)
}(fu)
}(ft)
}
}
// 또는
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> (T?, U?) -> V?? {
{ ft, fu in
lift { (t: T) -> V? in
lift { (u: U) -> V in
transform(t, u)
}(fu)
}(ft)
}
}
Optional<T>
거나, 두번째 인자가 Optional<U>
였기 때문에transform
함수를 만들어,lift
에 넣어주었다. (그래야 lift
함수가 동작하니까.)T -> Optional<T>
로 나올거라고 가정하는 거다.U -> V
를 만들어 lift
에 넣어주는 것이다.f_u
, f_t
함수의 리턴값이 V
였는데 지금은 Optional<V>
인데?lift
을 쓰면 Optional<Optional<V>>
가 나오는데?Optional<Optional<V>>
이다.
(T1, T2, T3) -> U
를(F<T1>, F<T2>, F<T3>) -> F<F<F<U>>>
로 변환하는 lift3d 함수를 만들어라.
단lift
함수만 사용한다.
F
을 Optional로 생각해보자.internal func lift<T, U>(transform: @escaping (T) -> U) -> (Optional<T>) -> Optional<U> {
return { (input: Optional<T>) -> Optional<U> in
switch input {
case .none:
return .none
case .some(let wrapped):
return .some(transform(wrapped))
}
}
}
func lift3d<T1, T2, T3, U>(_ transform: @escaping (T1, T2, T3) -> U) -> (T1?, T2?, T3?) -> U??? {
{ ft1, ft2, ft3 in
lift { (t1: T1) in
lift { (t2: T2) in
lift { (t3: T3) in
transform(t1, t2, t3)
}(ft3)
}(ft2)
}(ft1)
}
}
다변수 함수를 일변수 함수의 연속된 동작으로 처리할 수 있다.
func integrate(_ f: (Double) -> Double, _ lower: Double, _ upper: Double) -> Double {
Array(stride(from: lower, through: upper, by: 0.1))
.reduce(.zero) { $0 + f($1) }
}
func f_x(x: Double) -> Double {
integrate({ (y: Double) in
integrate({ (z: Double) in
Double(pow(x, pow(y, z)))
}, 0, 1)
}, 0, 1)
}
integrate(f_x, 0, 1)
integrate
라는 1변수 함수에 대한 적분 방식을 정의하고,integrate
함수를 더 깔끔하게 작성할 수 있을 것이다.public func curry<A, B>(_ f: @escaping (A) -> B) -> (A) -> B {
{ a in f(a) }
}
public func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
{ a in { b in f(a, b) } }
}
public func curry<A, B, C, D>(_ f: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
{ a in { b in { c in f(a, b, c) } } }
}
func f_x(x: Double) -> Double {
curry(integrate)({ (y: Double) in
curry(integrate)({ (z: Double) in
Double(pow(x, pow(y, z)))
})(0)(1)
})(0)(1)
}
func lift<T, U>(_ transform: @escaping (T) -> U) -> ([T]) -> [U] {
{ ft in
var result = [U]()
ft.forEach { t in
result.append(transform(t))
}
return result
}
}
func lift2d<T, U, V>(_ transform: @escaping (T, U) -> V) -> ([T], [U]) -> [[V]] {
{ ft, fu in
lift { t in
lift { u in
transform(t, u)
}(fu)
}(ft)
}
}
flatlift
)lift
함수는 swift의 map
함수와 동일하다.private func testOptionalLift2d() {
let x = Optional(3)
let y = Optional(4)
let result = lift2d { x, y in
x+y
}(x, y)
print(result) // Optional(Optional(7)
}
private func testArrayLift2d() {
let x = [3, 4, 5, 6]
let y = [10, 20, 30]
let result = lift2d { x, y in
x+y
}(x, y)
print(result) // [[13, 23, 33], [14, 24, 34], [15, 25, 35], [16, 26, 36]]
}
private func testResultLift2d() {
let x: Result<Int, Error> = .success(10)
let y: Result<Int, Error> = .success(3)
let result = lift2d { x, y in
x+y
}(x, y)
print(result) // success(Swift.Result<Swift.Int, Swift.Error>.success(13))
}