예시코드는 곰튀김님의 영상 을 참고하였습니다
객체지향의 코드와 함수형의 코드 비교
protocol ImageDownloaderDelegate {
func imageDownloaded(_ image: UIImage)
}
class ImageDownloader {
var delegate: ImageDownloaderDelegate? = nil
func requestDownload(_ url :URL) {
URLSession().dataTask(with: url) { data, response, error in
guard error == nil else { return }
guard self.delegate == nil else { return }
let image = UIImage(data: data!)!
self.delegate!.imageDownloaded(image)
}
}
}
class ViewController: UIViewController, ImageDownloaderDelegate {
@IBOutlet var label: UILabel!
@IBOutlet var imageView: UIImageView!
let downloader = ImageDownloader()
override func viewDidLoad() {
super.viewDidLoad()
downloader.delegate = self
getImageUrl { (imageUrlString) in
let url = URL(string: imageUrlString)!
self.downloader.requestDownload(url)
}
}
func getImageUrl(completed: @escaping ((String) -> Void)) {
let urlString = "https://api.server/image"
let url = URL(string: urlString)!
URLSession().dataTask(with: url) { data, response, error in
guard error == nil else { return }
completed(String(data: data!, encoding: .utf8)!)
}
}
func imageDownloaded(_ image: UIImage) {
imageView.image = image
}
}
class ViewController: UIViewController, ImageDownloaderDelegate {
@IBOutlet var label: UILabel!
@IBOutlet var imageView: UIImageView!
let downloader = ImageDownloader()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
downloader.delegate = self
getImageUrl()
.flatMap { self.requestDownload(URL(string: $0)!)}
.subscribe(onNext: { self.imageView.image = $0 })
.disposed(by: disposeBag)
}
func getImageUrl() -> Observable<String> {
let urlString = "https://api.server/image"
return Observable.create { emitter in
let url = URL(string: urlString)!
URLSession().dataTask(with: url) { data, response, error in
guard error == nil else { return }
emitter.onNext(String(data: data!, encoding: .utf8)!)
emitter.onCompleted()
}
return Disposables.create()
}
}
func requestDownload(_ url: URL) -> Observable<UIImage> {
return Observable.create { emitter in
URLSession().dataTask(with: url) { data, response, error in
guard error == nil else { return }
let image = UIImage(data: data!)!
emitter.onNext(image)
emitter.onCompleted()
}
return Disposables.create()
}
}
}
파라미터로 넣을 수 있다.
변수에 할당할 수 있다.
리턴값으로 반환할 수 있다.
@inlinable public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
@inlinable public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
@inlinable public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
swift의 대표적인 고차함수들인 map, filter, reduce 로
함수를 파라미터로 받을 수 있고 반환값을 함수로 리턴할 수 있습니다.
let numbers: [Int] = [1,2,3,4,5]
let filterNumbers = numbers.filter { $0 % 2 == 0 }
함수형 프로그래밍의 큰 특징이 side effect 제거이기 때문에
데이터값을 가질 때 변수가 아닌 상수에 할당하여
데이터가 변형되었을 때 가질 수 있는 side effect를 방지해야 합니다.
순수 함수란 특정 input에 대해서 항상 동일한 output을 반환하는 함수를 말합니다.
외부 값을 사용하지 않아 side effect가 없습니다.
func setName(_ personName: String) -> String {
return "Hello, \(personName)"
}
함수의 반환값이 다른 함수의 입력값으로 사용되는 것을 함수의 합성이라고 합니다.
func filterNumbers(_ numbers: [Int]) -> [Int] {
return numbers.filter { $0 % 2 == 0 }
}
func printNumbers(_ numbers: [Int]) -> String {
return "필터링된 숫자의 합은 \(numbers.reduce(0, +))입니다."
}
func composition(_ input: @escaping ([Int]) -> [Int],
_ input2: @escaping (([Int]) -> String)) -> ([Int]) -> String {
return { num in
return input2(input(num))
}
}
let comp = composition(filterNumbers(_:), printNumbers(_:))
print(comp([1,2,3,4,5])) // 필터링된 숫자의 합은 6입니다.
함수의 파라미터가 여러 개를 가질 경우 파라미터를 하나만 사용하는 함수로
나누는 것을 말합니다.
커링을 쓰는 이유는 함수의 반환값이 다른 함수의 input으로 사용되는 경우가
많기 때문에 원활한 합성을 위해서 쓰입니다
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
// ->
func newSum(_ a: Int) -> (Int) -> Int {
return { b in
return a + b
}
}
print(newSum(5)(20))
Rx를 이용하여 함수형 프로그래밍으로 간단하게 프로젝트를 만들어보았을 때
선언형으로 구현하기에 가독성은 좋다는 것과 side effect를 고려할 필요가 적다는 장점이 있었지만 함수를 순수함수와 커링, composition을 고려하여 구현하다보니
파일 내부 코드가 길어진다는 단점이 있었습니다.
그리고 기존의 명령형으로 작성하다가 선언형으로 생각하여 구현하다보니
익숙하지 않아 몇 번의 사이드 프로젝트를 만들어봐야 장단점을 확실히 알 수 있을 것 같습니다.
https://mangkyu.tistory.com/111
https://www.youtube.com/watch?v=cXi_CmZuBgg&t=1019s