본문 바로가기
Swift

FP에서 Setter 사용법

by zinozino 2022. 12. 10.

 

 

이번에는  데이터에 값을 세팅하는 방법은 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")

 

잘 나오네요!

 

그렇지만 이전 개념보다  이해하는 게 어려웠던 만큼 설명하는 것도 어려웠어요.

그래서 좀 더 잘 설명할 수 있을 때 보충해 놓도록 하겠습니다.

 

 

 

참고 아티클

https://www.pointfree.co/episodes/ep6-functional-setters

'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