Functor란 무엇인가?
struct Stack<T> {
private var items: [T] = []
mutating func push(_ item: T) {
items.append(item)
}
mutating func pop() -> T? {
return items.popLast()
}
func peek() -> T? {
return items.last
}
var isEmpty: Bool {
return items.isEmpty
}
var count: Int {
return items.count
}
}
// Int 타입의 스택 생성
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
intStack.push(3)
Optional
Array
Dictionary
Set
Result
Either
Future
Promise
Optional<T>
Array<T>
Dictionary<T, U>
Set<T>
Result<T, E>
Either<T, U>
Future<T>
Promise<T>
Stack<T>
이라는 타입을 Generic으로 만들고 값을 감쌀 수 있다면 이는 Type Constructor이다.Functor: 임의의 타입
T, U
가 주어졌을 때, 연산 lift이 정의되는 Type ConstructorF
lift:
(T -> U) -> (F(<T>) -> F(<U>))
T->U
인 함수가 있을 때, 각각의 원소를 Type Constructor F
로 감싸는 함수로 변환할 수 있으면 Functor(T->U)
로 가는 건 내부에 들어간 값을 변형 하는 것이다. 이제부터 이걸 transform
이라 부르겠다.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))
}
}
}
transform
을 인자로 받아 Optional<T>
를 인자로 받아 Optional<U>
를 반환하는 함수를 반환한다.Optional
은 Functor이다.internal func lift<T, U>(_ transform: @escaping (T) -> U) -> ([T]) -> [U] {
return { (input: [T]) -> [U] in
var result: [U] = []
for index in input.indices {
result.append(transform(input[index]))
}
return result
}
}
internal func lift<T, U>(_ transform: @escaping (T) -> U) -> (Result<T, Error>) -> Result<U, Error> {
return { (input: Result<T, Error>) -> Result<U, Error> in
switch input {
case .failure(let error):
return .failure(error)
case .success(let value):
return .success(transform(value))
}
}
}
<T, U>
를 직접 선언해두지 않고, 타입 내에서 결과 타입만 가지고 있을 수 있게 안에서 구현한다.Array
, Set
과 같이 Sequence
로 묶이는 경우에는 상위 타입의 프로토콜이나 클래스에서 구현해둔다.internal enum Optional<Wrapped> {
case none
case some(Wrapped)
func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? {
switch self {
case .none:
return .none
case .some(let wrapped):
return try .some(transform(wrapped))
}
}
}
lift
라 구현 안되어 있고 map
으로 되어 있다.Optional
, Array
, Result
는 Functor이다.Functor
라는 프로토콜을 만들든, 클래스를 만들든 한 다음에 이걸 상속하게 해서 기본 동작을 제공할 것이다.Set
, Array
같은 경우는 Sequence
Protocol을 상속받아 처리되고,Optional
의 경우에는 자체적으로 가지고 있다.T->U
로 가는 함수를 받아 F(T)
를 받아 F(U)
를 반환하는 함수를 반환한다.id_T = (T -> T)
id_F<T> = (F(T) -> F(T))
lift = (X -> Y) -> (F<X> -> F<Y>)
lift(id_T) = id_F<T>
T->T
인 항등함수는 id_T
라고 표현할 수 있다.F(T)->F(T)
인 항등함수는 id_F<T>
라고 표현할 수 있다.(X -> Y) -> (F<X> -> F<Y>)
라고 표현할 수 있는데, 이 연산의 결과는 뭐가 나올지 모른다.f = (T -> U)
g = (U -> V)
lift = (X -> Y) -> (F<X> -> F<Y>)
h = g∘f
lift(h) = lift(g∘f) = lift(g)∘lift(f)
즉,
lift(g∘f) = lift(g)∘lift(f)
h
가 g
와 f
의 합성이라고 한다면,h
를 lift
한 결과 역시 g
와 f
를 lift
한 결과의 합성과 같다. (lift(g)∘lift(f)
)func f(_ x: Int) -> Int {
return x + 1
}
func g(_ x: Int) -> Int {
return x * 2
}
func h(_ x: Int) -> Int {
return g(f(x))
}
let optional = Optional.some(10)
let result1 = lift({ h($0) })(optional) // Optional.some(22)
let result2 = lift({ g($0) })(lift({ f($0) })(optional)) // Optional.some(22)
let array = [3, 4, 5, 6]
let result1 = lift({ h($0) })(array) // [8, 10, 12, 14]
let result2 = lift({ g($0) })(lift({ f($0) })(array)) // [8, 10, 12, 14]
let result: Result<Int, Error> = .success(10)
let result1 = lift({ h($0) })(result) // success(22)
let result2 = lift({ g($0) })(lift({ f($0) })(result)) // success(22)