이번에는 데이터에 값을 세팅하는 방법은 FP적으로 생각해보도록 하겠습니다.
간단한 튜플 하나를 만들었습니다.
let pair = (42, "Swift")
42를 43으로 증가시키고 싶다면
값에 접근하는 방법이 있을 수 있겠습니다.
let IncresedPair = ((pair.0 + 1), pair.1)
그렇다면 좀 더 나아가 FP방법으로 생각해보면 어떨까요?
먼저 증가하는 함수가 있어야 할 것이고 증가한 값을 바탕으로 새로운 튜플을 반환해야 하는 함수가 있어야 할 것입니다.
증가하는 함수 |> 연산자 정의에서 본 간단한 함수입니다.
func increase(value: Int) -> Int { // (Int) -> Int
value + 1
}
이제 increase함수를 파리미터로 받고 튜플을 새로 반환하도록 하겠습니다.
func first<A,B,C>(_ fuction: @escaping (A) -> (C)) -> ((A,B)) -> (C,B) {
return { pair in
return (fuction(pair.0), pair.1)
}
}
let pair = (42, "Swift")
first(increase)(pair) // (43, "swift"
이번엔 튜플의 두 번째인 "Swift"를 바꾸는 함수를 만들어 보겠습니다.
func second<A,B,C>(_ fuction: @escaping (B) -> C) -> ((A,B)) -> (A,C) {
return { pair in
return
(pair.0, fuction(pair.1))
}
}
let newPair = pair
|> first(increase)
|> first(String.init)
|> second { $0 + "!" }
print(newPair) // ("43", "Swift!")
좀 더 복잡한 튜플에서 적용할 수 있도록 해보겠습니다.
잘 동작하지만 코드를 읽기 쉽지 않습니다ㅠㅠ
let nestedTuple = ((1, true), "Swift")
let newNestedTuple = nestedTuple |> first{ $0 $0 |> second { !$0 } }
print(newNestedTuple) // ((1, false), "Swift")
이렇게 쓰고 싶은 유혹인 생길 수 있지만,, 되지 않습니다..
nestedTuple
|> (first => second) { !$0 }
그런데 또 second를 앞에 두면 됩니다..?
let newNestedTuple = nestedTuple
|> (second => first) { !$0 }
저번에 => 연산자를 만들 때를 보면
func => <A,B,C>(
leftFunction: @escaping (A) -> (B),
rightFunction: @escaping (B) -> C) -> ((A) -> C) {
return { a in rightFunction(leftFunction(a)) } //1)
}
1)
지금 상태에서
leftFunction == second
rightFunction == first
겠죠?
rightFuction은 leftFunction의 결괏값을 인수로 사용하게 됩니다.
결국 불리는 rightFunction부터 불리게 된다는 것..!
rightFunction == first
먼저 right(first) (1, true)의 left(second)가 되게 되는 거죠.
하지만 이렇게 하면 헷갈리잖아요.
사실 저걸 이해하는 게 더 힘들지만 저는 한 20분을 이해하는데만 시간을 썼어요...
어쨌든 =>는 left의 함수 다음 right로 넘기는 연산자 즉 체이닝 연산자로 만들었는데
중첩된 구조에서는 반대로 써야 한다면 더 복잡한 구조에서는 더 이해하기 어려울 거예요.
그래서 하나 더 연산자를 만들어보겠습니다.
infix operator <==: BackwordLogicProcessingPrecedence
precedencegroup BackwordLogicProcessingPrecedence {
associativity: left
}
func <== <A,B,C>(_ leftFunction: @escaping (B) -> C, rightFunction: @escaping (A) -> B) -> (A) -> C {
return { leftFunction(rightFunction($0)) }
}
==>와 left와 right 바뀌었습니다.
사용해보면
let newNestedTuple = nestedTuple
|> (first <== second) { !$0 }
print(newNestedTuple2) // ((1, false), "Swift")
잘 나오네요!
그렇지만 이전 개념보다 이해하는 게 어려웠던 만큼 설명하는 것도 어려웠어요.
그래서 좀 더 잘 설명할 수 있을 때 보충해 놓도록 하겠습니다.
참고 아티클
'Swift' 카테고리의 다른 글
Array의 index를 안전하게 처리하는 방법 (2) | 2023.04.18 |
---|---|
KeyPath (2) | 2023.01.16 |
Custom Operator => (0) | 2022.12.02 |
Currying (2) | 2022.11.30 |
Custom Operator |> (2) | 2022.11.29 |