이번엔 더 복잡한 객체를 만들어 볼게요.
먼저 렌즈 함수를 다시 볼게요
struct Lens<Whole, Part> {
let view: (Whole) -> Part
let set: (Part, Whole) -> Whole
}
스트리머의 채널정보를 알 수 있도록 속성을 하나 더 추가해 볼게요.
struct Channel {
let subscribers: UInt
let videos: UInt
}
좀 더 복잡한 객체가 되었습니다.
struct Streamer {
let name: String
let mainPlatform: String
let channel: Channel
}
카더정원이라는 유튜버를 만들었습니다.
let youtuber = Streamer(
name: "카더정원",
mainPlatform: "Youtube",
channel: Channel(subscribers: 835, videos: 20)
)
이 유튜버의 채널을 들여다보는 Lens를 만들겠습니다.
let channelLens = Lens<Streamer, Channel>(
view: {streamer in streamer.channel},
set: {
newChannel,
streamer in Streamer(
name: streamer.name,
mainPlatform: streamer.mainPlatform,
channel: newChannel
)
}
)
채널을 가져와보겠습니다.
channelLens.view(youtuber)
//Channel(subscribers: 835, videos: 20)
유튜버의 구독자수가 변경되어 채널 정보가 변경됩니다.
여기서 Channel의 구독자가 변경되는 것이니 이것 또한 Lens로 볼 수 있습니다. Part는 자기 자체로는 전체가 될 수 있기 때문입니다.
(Whole) -> Part
let subscriberLens = Lens<Channel, UInt>(
view: { channel in channel.subscribers},
set: {
newSubscriber,
channel in Channel(
subscribers: newSubscriber,
videos: channel.videos
)
}
)
여기서 생각해 볼 부분은 channelLens Streamer의 Channel까지만 가져오고,
subscrberLens는 Channel에서 Subscrber까지 가져오는데요.
이대로 두면 연결이 끊겨있기 때문에 Streamer에서 바로 channe의 Subscriber를 가져올 수 없습니다. 값을 경신할 수도 없습니다.
그래서 저는 이 간극을 이어주는 함수하나를 만들어 보도록 하겠습니다.
표현하자면 이렇게 되겠네요.
=> (Whole) -> Part ⛓️💥 (Part) -> PartOfPart
=> (Whole) -> Part ⛓️ (Part) -> PartOfPart => (Whole) -> PartOfPart ✅
함수의 시그니처는 이렇게 되겠네요.
func link<Whole, Part, PartOfPart>(_ wholeLens: Lens<Whole, Part>, _ partLens: Lens<Part, PartOfPart>)
-> Lens<Whole, PartOfPart>
구현해 보도록 하겠습니다.
func link<Whole, Part, PartOfPart>(_ wholeLens: Lens<Whole, Part>, _ partLens: Lens<Part, PartOfPart>) -> Lens<Whole, PartOfPart> {
let linkedLens: Lens<Whole, PartOfPart> = .init(
view: {whole in
let part = wholeLens.view(whole) //wholeLens는 whole을 넣으면 part를 반환합니다.
let partOfPart = partLens.view(part) //partLens는 part을 넣으면 partOfPart를 반환합니다.
return partOfPart
},
set: { partOfPart, whole in
let part = wholeLens.view(whole) // partOfPart를 먼저 Part에 넣기위해서 wholeLens를 통해서 Part를 반환합니다.
let newPart = partLens.set(partOfPart, part) // PartLens로 part에 partOfpart를 갱신시키고 반환합니다.
let newWhole = wholeLens.set(newPart, whole) // 반환받은 Part를 wholeLens통해서 Whole의 Part를 갱신시키고 반환합니다.
return newWhole
}
)
return linkedLens
}
이제 끊긴 channelLens과 subscriberLens를 연결시켜 보도록 하겠습니다.
let linkedToSubscriber = link(channelLens, subscriberLens)
구독자수를 가져와보도록 하겠습니다.
let subscriberOfYoutube = linkedToSubscriber.view(youtuber)
print("youtuber의 구독자수는 \(subscriberOfYoutube)") //835
구독자수가 늘어서 구독자수를 증가시키겠습니다.
let updatedSubscriberOfYoutuber = linkedToSubscriber.set(1000, youtuber)
print(updatedSubscriberOfYoutuber)
//Streamer(name: "카더정원", mainPlatform: "Youtube", channel: Channel(subscribers: 1000, videos: 20))
이번 글은 Lens를 이어주는 함수를 만들어보고, Lens기능을 수직방향으로 확장시켰다고 볼 수 있는데요.
다음 글은 수평적인 방향으로 사용할 수 있도록 해볼게요.
감사합니당👐
참조
https://medium.com/javascript-scene/lenses-b85976cb0534
Lenses
Composable Getters and Setters for Functional Programming
medium.com
https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/
Category Theory for Programmers: The Preface
Table of Contents Part One Category: The Essence of Composition Types and Functions Categories Great and Small Kleisli Categories Products and Coproducts Simple Algebraic Data Types Functors Functo…
bartoszmilewski.com
'Functional Programming' 카테고리의 다른 글
Swift에서의 렌즈(Lens) - Basic (0) | 2025.03.13 |
---|