iOS/Swift

Swift로 함수형 프로그래밍 시작하기

누알라리 2021. 3. 28. 14:13

개요

대학교 4년, C++을 사용하던 모 게임회사 근무 1년 3개월..

제 개발자 경력의 2/3를 OOP 를 생각하며 보냈습니다.

그래서 그런지 Swift를 배우며 함수형 프로그래밍에 대해 공부를 하면 할수록 수박 겉핥기식밖에 되지 않는 기분이 계속 들더라구요.

이대로는 안되겠다 싶어서 드디어 돈을 쓰고 강의를 구매했습니다.

이 글은 강의 개요 부분에 적혀진 "객체지향 프로그래밍 패러다임 속에서 평생을 프로그래밍 해 온 사람"이 이 강의를 통해 함수형 프로그래밍을 어떻게 배웠는지 간단히 기록하는 글이 되겠습니다.

 

함수형 프로그래밍이라는 패러다임을 보고 개인적으로 (공부하기 귀찮아서) 삐딱하게 갖고있던 마음 속 질문들이 있는데, 야곰님이 이런 제 마음을 들여다보시기라도 한 듯 강의 플로우가 제가 갖고있던 질문에 대한 답으로 진행되서....윗 부분 Q에 제가 가졌던 질문들을 적으며 기록하도록 하겠습니다ㅎㅎ

 

programmers.co.kr/learn/courses/4806

 

Swift로 함수형 프로그래밍 시작하기

iOS 오프라인 밋업에서나 겨우 들을 수 있었던 그 강의! Swift로 함수형 프로그래밍 시작하기 온라인 동영상 + 온라인 실습 강의 열람 기간 제한 없음 파트1은 무료 열람 현재 유일한 Swift4 기반의 FP

programmers.co.kr

swift는 OOP(객체지향), POP(프로토콜지향), FP(함수형)을 지원하는 멀티 패러다임 언어

 

1강

Q. 그동안 OOP, OOP!! 객체 지향! 캡슐화! 다형성! 상속! 귀에 박히도록 외웠는데 왜 갑자기 함수형 프로그래밍이 조명을 받게된건지?!

 

프로그래밍 패러다임은 컴퓨터의 발전에 따라 변화되어 왔다.

초기: 적은 메모리, <<최적화>> 중심

중기: 대량, <<재사용성>> 중심, 데이터 설계 -> OOP

현재: CPU 하나에 여러가지 코어, 하나의 컴퓨터에 여러개의 CPU, 하나의 프로세스에도 멀티 쓰레드가 들어감.  -> 높은 메모리

하나의 반복된 작업을 병렬적으로, 여러개 작업을 동시에, 응답이 올 때 까지 기다리면 안되는 <<퍼포먼스, 동시성>>

 

물론 OOP로도 동시성 지원 가능.

JAVA의 경우 Synchronized 키워드를 사용해 동시성 구현 가능 -> 하지만...박터지게 짜증나게함

동시성을 직관적으로 해결하려면?

데이터를 조작하지 않고, 한 번 만들어진 데이터는 변경하지 않고, 변경된 데이터가 필요할 때는 새로운 데이터를 만들어낸다.

데이터를 조작하지 않으면 같은 작업을 수행해도 원본 데이터에 영향을 끼치지 않으므로 사이드 이펙트가 발생하지 않는다.

-> FP가 추구하는 방향과 결이 같다.

 

컴퓨터의 발전에 따라 <퍼포먼스, 동시성>에 대한 관심이 커지면서 FP가 관심을 받기 시작했다!

 

Q. 그동안 겉핥기로 배워둔 FP... 고차함수, 순수함수, Immutable.. FP하려면 이런걸 쓰는건가? 당장 실무에 적용하기 어렵고 복잡해보인다. ㅜㅜㅠ 사실 이해하고 넘어가도 자고 일어나면 까먹는 기분이 든다..이런게 FP라면...나는 적용하기에 오래걸릴거같아

 

많은 FP를 다루는 강의들이 프로그래밍 기법들을 설명하는것으로 내용이 구성되어 있다.

이러한 요소들을 사용해서 프로그래밍을 하면 FP 인것인가...? -> 아니다!!!

Programming Technique (프로그래밍 기법) Functional Programming (함수형 프로그래밍)
Immutable Data Side-Effect Free Programming
(using Programming Technique)
Higher Order Function (고차함수)
Currying
Map, Filter, Reduce

FP의 가장 큰 특징은 함수를 중심으로 사이드 이펙트가 없도록 프로그래밍 하는 것 일뿐,

단지 이 프로그래밍 기법들을 사용해서 프로그래밍 하는 것이 아니다.

 

 Q. 아 .. 그럼 나 지금까지 OOP 해왔던걸 FP로 대체(?) 해야하는건가? 패러다임에 대해 이렇게 깊게 생각해본적이 없는데 그럼 둘은 반대되는 개념인건가? 난 근데 OOP도 매우 좋다고 생각하는데. 익숙하기도 하고.

 

현재 내가 당면한 문제에 OOP/FP를 필요에 따라 적절히 사용하도록 하자!!! 둘은 대척관계가 아니다!

 

2강

함수형 프로그래밍(FP)이란 "순수함수"를 이용해서 프로그래밍을 하는 것 이다.

순수 함수(Pure Function)란?

특정 input에 대해서 항상 동일한 output을 반환하는 함수

output을 만드는데 input만을 사용한다는 의미로, 함수 외부의 값을 사용하지 않아 사이드 이펙트가 없습니다.

  • output은 input에 의해서만 결정된다.
  • 함수의 수행 과정에서 외부에 있는 값을 사용하지 않습니다.
  • 외부의 값을 변경하지 않습니다.
  • 외부에 영향을 주지도 ,받지도 않으므로 side-effect가 발생하지 않습니다.

Q. 오키오키. 순수함수 == 특정 input에 대해서 항상 동일한 output이라 이거지? 근데 꼭 parameter로 input을 전달해야 하는지..? 외부의 변수가 let 이면 어차피 불변인데 let도 외부의 값이기 때문에 안쳐주는건가?

let offset: CGFloat = 10.0
func getOffset(curHeight: CGFloat) -> CGFloat {
    return offset + curHeight
}

결과를 만드는데 외부값인 offset을 사용했는데,

offset 변수는 let으로 정의되어 있어 변경이 불가한 immutable data 이다.

immutable data만 사용하는 함수도 특정 input에 대해 항상 동일한 output을 내기 때문에 순수 함수라고 합니다~!

 


함수형 프로그래밍(FP)에서는 함수를 "1급 객체"로 취급한다.

 

1급 객체란?

프로그래밍 언어에서 함수의 파라미터로 전달되거나 리턴값으로 사용될 수 있는 객체

고차 함수(Higher-Order Function) 이란?

함수를 파라미터로 받거나 함수를 리턴하는 함수를 고차함수 라고 합니다.

 

Swift에서는 Foundation 라이브러리에서 고차함수(filter, map, reduce)를 지원하고 있습니다(!!!!!!!!)

-> 2021.03.28 - [iOS/Swift] - Reduce, Map, Filter

 

Reduce, Map, Filter

개요 swift는 함수를 1급 객체로 사용하는 고차함수(Higher-Order-Function)를 Foundation 라이브러리에서 제공하고 있습니다. 대표적으로 Filter, Map, Reduce가 있습니다. Filter 클로저로 배열의 각 항목들을..

hyunndyblog.tistory.com


함수형 프로그래밍(FP)에서는 함수를 1급 객체로 사용하므로 당연히 함수의 합성(Composition)도 가능하다.

 

함수의 합성(Composition) 이란?

함수의 반환값이 다른 함수의 입력값으로 사용되는 것.

당연히 함수의 반환값과 이것을 입력으로 받아들이는 타입이 서로 같아야 합니다.

 

개념만 보면 그냥 후루룩 하면 될거같은데... 역시 간지나는 개념 제네릭이 들어가면서 한 큐에 이해하기엔 살짝 힘들지만 간지나는 코드를 작성할 수 있게 됩니다.

 

간단한 함수 합성

func f1(_ num: Int) -> Int {
    return num + 3
}

func f2(_ i: Int) -> String {
    return "\(i) 빼기 3은 f1의 파라미터"
}

// 함수를 변수에 할당할 수 있는 고차함수적 부분..!
let result: String = f2(f1(10))

조금 복잡한 함수 합성

func ff(_ pf1: @escaping (Int) -> Int, _ pf2: @escaping (Int) -> String) -> (Int) -> String {
    return { (s1: Int) in
            return pf2(pf1(s1))
    }
}

let f3 = ff(f1, f2)
let result = f3(100)

f3에 ff의 반환 값인 (Int) -> String 을 할당!

 

제네릭이 들어간다면?

func comp<A, B, C>(_ pf1: @escaping (A) -> B, _ pf2: @escaping (B) -> C) -> (A) -> C {
    return { i: in
            return pf2(pf1(i))
    }
}

let resultComp = comp(f1, f2)
return resultComp(타입이 A인 파라미터)

 


커링(Currying) 이란?

여러개의 파라미터를 받는 함수를 하나의 파라미터를 받는 여러 개의 함수로 쪼개는 것.

 

Q. 왜,,,? 그래야하지...? 파라미터 여러개 받는걸 쪼개는건 그냥 함수만 많아지고 코드 가독성이 좀 그렇지 않나?

-> 함수의 합성(Composition)을 원활하게 하기 위함.

 

함수의 Output이 다른 함수의 Input으로 연결되면서 합성(Composition)이 되는데,

함수들이 서로 chain을 이루면서 연속적으로 연결이 되려면 output과 input의 타입이과 갯수가 같아야 한다.

함수의 Output은 하나밖에 없으니, Input도 모두 하나씩만 갖도록 한다면 합성하기가 쉬워질 것이다.

 

-> 음..근데 솔직히 모르겠다.  코드 스타일의 차이인가? ㅎㅎ.. ㅜㅜ 난 가독성이 더 중요하다고 보는데 내가 아직 함수 합성을 복잡하게만 봐서 그런거겠지. 일단 더 공부해보겠삼.

 

커링 전)

func multiply(_ s1: Int, s2: Int) -> Int {
    return s1*s2
}

커링 후)

// 이렇게 파라미터 분리!
func f1(_ s1: Int) -> Int
func f2(_ s2: Int) -> Int

func multiply(_ s1: Int) -> (Int) -> Int {
    return { s2 in
        return s1 * s2
    }
}

let result = multiply(10)(20)

커링 전)

func filterSum(_ arr: [Int], _ n: Int) -> Int {
    return arr.filter({$0 % n == 0}).reduce(0, +)
}

 

커링 후) 

이해하기 존내 힘들었으니 아마 지속적으로 봐줘야할듯 ㅎㅎ ㅜㅜ

func filterSum2(_ n: Int) -> [Int] -> Int {
    return { arr in
        return arr.filter{ $0 % n == 0}.reduce(0, +)
    }
}

func solution(_ nums: [Int], _ r: Int) -> Int {
    let filterResult = filterSum2(r) // 이게 지금 Int가 반환되야할 <<함수>>가 할당된거임.
    return result = filterResult(nums)
}

Async Result

UI 작업에서 시간이 걸리는 작업(ex. 연산이 오래 걸리거나, 네트워크를 통해 결과를 받아야 하거나, 딜레이가 포함되는 경우)은 비동기 방식으로 함수를 구현하는 것이 좋다.

비동기 방식이란 결과는 나중에 생길 때 전달받기로 하고 프로그램의 수행을 멈추지 않는 방식을 말한다.

 

func loadResult(_ nums: [Int], _ result: @escaping (Int) -> Void) {
	DispatchQueue.main,async {
    	sleep(1)
        let sum = nums.reduce(0, +)
        result(sum)
    }
}

이 함수의 핵심 부분은 결과가 리턴값으로 전달되는 것이 아니라, 전달 받은 함수(result)를 호출함으로써 전달된다는 것이다.

@escaping 키워드가 눈에 띄는데, 이는 인자로 전달된 함수(result)가 호출한 함수(loadResult) 가 수행되고 난 다음에 수행되기 때문에 붙여진 것이다.

 

 

'iOS > Swift' 카테고리의 다른 글

Reduce, Map, Filter  (0) 2021.03.28
[Swift] lazy 키워드  (0) 2020.12.20
[Swift] weak, unowned, Retain cycle 톺아보기  (1) 2020.12.10