SlideShare ist ein Scribd-Unternehmen logo
1 von 61
더 좋은 코드를 위한 함수형 프로그래밍
모던 C++를 중심으로
Blizzard Entertainment
전이삭
오늘 살펴볼 개념들
• 순수함수와 불변성
• 고차함수와 함수의 합성
• 타입 시스템과 대수적 자료형
• 타입을 매개로 한 함수의 합성 *
순수함수 Pure functions
순수함수 - Pure functions
• 함수의 결과값이 오직 입력 인자 값들에 의해서만 결정
• 특정 입력 값에 대해서 결과값이 결정적으로 대응되는 함수
• 부작용(Side effects)이 없는 함수
int abs(int n);
int atoi(const char *str);
int getDefaultPort(bool https) {
return https ? 443 : 80;
}
int rand();
time_t time (time_t* timer);
bool g_https = false;
int getDefaultPort() {
return g_https ? 443 : 80;
}
부작용 - Side effects
• 상태에 영향을 받거나 변화를 주는 모든 동작
• 화면출력
• 소켓통신
• 디스크 I/O
• 예외 catch
• 현재 시각 읽기
• 난수 발생
참조 투명성 - Referential transparency
• 어떤 표현식을 그 표현식의 결과값으로 교체해도 전체
프로그램의 실행결과에 영향을 주지 않는 성질
• 컴파일러에게 다양한 최적화 기회를 제공
기억 – Memoization
- 입력값 / 함수명을 알면 결과값은 항상 같음
지연연산 - Lazy evaluation
- 실제로 결과값이 필요할 때만 함수를 평가
• 코드에서 가능한 순수함수의 비율이 많아지도록 하자
- 글로벌 변수, 멤버 변수 의존도를 줄이자
- 상태를 없애거나 줄이고 부작용을 격리시키자
• 함수는 인자를 받아서 값을 리턴하는 형태로
- 레퍼런스 인자 보다는 값을 리턴
- 여러 개의 값을 리턴할 때는 tuple을 사용
FP 실천사항 #1 - 순수함수
불변성 Immutability
Java String is immutable
String str = "Welcome to NDC2016!!!";
String newString =
str.replace(“2016”, “2017”);
System.out.println(newString);
String str = "Welcome to NDC2016!!!";
str.replace(“2016”, “2017”);
System.out.println(str);
Welcome to NDC2016!!! Welcome to NDC2017!!!
불변객체의 특징
• 내용의 변경은 새로운 객체를 생성할 때만 가능
• 생성, 테스트, 사용법이 단순하고 쉬움
• 진정한 의미의 불변객체는 그 자체로 Thread-safe
• 쉬운 캐쉬 – 이름이 같으면 내용도 같다
• Temporal coupling 줄임
• Identity mutability problem 없음
Temporal coupling
HttpRequest request;
request.SetUrl("https://hostname/api");
request.SetSSL(true);
request.SetCACert("ca-bundle.crt");
request.Post();
HttpRequest request;
request.SetUrl("https://hostname/api");
request.SetSSL(true);
request.Post(); // Error!
class HttpRequest {
public:
HttpRequest(const std::string& url, bool ssl,
const std::string& cacert);
// no setters
...
};
HttpRequest request("https://hostname/api", true,
"ca-bundle.crt");
request.Post();
Identity mutability problem
class Monster {
public:
…
int64_t id() const { return id_; }
std::string name() const { return name_; }
void setID(int64_t id) { id_ = id; }
void setName(const std::string& name) {
name_ = name;
}
…
};
using MonsterPtr = std::shared_ptr<Monster>;
std::map<int64_t, MonsterPtr> monsters;
MonsterPtr p1 =
std::make_shared<Monster>(1234, “Murloc");
monsters.insert(std::make_pair(p1.id(), p1));
p1->setID(2234); // ???
Memory와 Disk 가격
CPU 클럭 속도의 한계
• 사용하는 객체를 불변으로 일단 바꾸어 놓고 코드를
고쳐보자
- Setter들 대신에 생성자에서 인자로 초기화
- 객체를 변경하지 않는 멤버함수들은 const로
• 다양한 분산 빅데이터 알고리즘들을 조사해보자
- Immutability changes everything -
http://queue.acm.org/detail.cfm?id=2884038
• 영속형 자료 구조에 대해서 알아보자
FP 실천사항 #2 - 불변객체
고차함수와 함수의 합성
고차함수
• 함수의 인자로 한 개 이상의 함수를 넘겨 받거나,
함수의 결과값으로 다른 함수를 돌려주는 함수
• 고차함수의 보물창고 - #include <algorithm>
FP의 기본 초식 3총사
• std::transform
• std::remove_if / std::copy_if
• std::accumulate
std::accumulate
using Strings = std::vector<std::string>;
int64_t hash(const std::string& str, int64_t seed);
int64_t calculate_hash_by_stl(const Strings& strs) {
return std::accumulate(std::begin(strs),
std::end(strs), 12345ll,
[](int64_t seed, const std::string& s)
{ return hash(s, seed); });
}
추천 동영상 – CppCon 2016 "std::accumulate: Exploring an Algorithmic
Empire“ - https://youtu.be/B6twozNPUoA
Unix 명령어들의 합성 - PIPE
history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head
47 ls
26 cd
19 sudo
17 clang++
10 clang
7 history
7 ./a.out
5 ./compose
4 gedit
2 top
예제 – 몬스터에게 범위공격
• 특정 거리 r 이상 R 이하에
있는 대상을 공격
• 방어막이 있으면 방어막의
세기만큼 피해 감소
Dev-art by TH. Ahn
Monster class
class Monster {
public:
Monster();
void setId(int id);
void setHP(int hp);
void setArmor(int armor);
void setLocation(const Point& pt);
int getHP() const;
int getId() const;
bool getArmor() const;
Point getLocation() const;
void gotDamage(int damage);
private:
int id_;
int hp_;
int armor_;
Point pt_;
};
using MonsterPtr = std::shared_ptr<Monster>;
using Monsters = std::vector<MonsterPtr>;
void Monster::gotDamage(int damage) {
hp_ = hp_ - std::min(hp_, damage - armor_);
}
int calc_distance(const Point& pt1, const
Point& pt2);
Monsters doSplashDamage(const Point& my_location, const Monsters& monsters,
int damage, int range_from, int range_to) {
for (auto i = monsters.begin(); i != monsters.end(); ++i) {
auto pt = *i;
Monster& target = *pt;
int distance = calc_distance(my_location, target.getLocation());
if (distance >= range_from && distance <= range_to) {
target.gotDamage(damage);
}
}
Monsters alive_monsters;
for (auto i = monsters.begin(); i != monsters.end(); ++i) {
auto pt = *i;
Monster& target = *pt;
if (target.getHP() > 0) {
alive_monsters.push_back(pt);
}
}
return alive_monsters;
}
전형적인 절차형 코드
우리가 하고 싶은 것들
• 객체를 불변데이터로 다루고 싶다
• 가능한 순수함수를 쓰고 싶다
• 알고리즘 구현 부분과 사용 부분을 코드상에서 분리하고
싶다
• 임시변수들을 없애고 싶다
• 함수들을 쉽게 합성하고 싶다
불변 Monster class와 피해계산 순수함수
class Monster {
public:
Monster(int id, int hp,
int armor, const Point& pt);
int getHP() const;
int getId() const;
int getArmor() const;
Point getLocation() const;
static int calc_damaged_hp(int hp,
int damage, int armor);
private:
int id_;
int hp_;
int armor_;
Point pt_;
};
int Monster::calc_damaged_hp(int hp,
int damage, int armor) {
return hp - std::min(hp, damage - armor);
}
주요 문장들을 함수로 쪼개기
bool is_in_range(const Point& my_location, int range_from, int range_to, const MonsterPtr& pm);
MonsterPtr damaged_monster(int damage, const MonsterPtr& pm);
bool is_alive(const MonsterPtr& pm);
Partial application
bool is_in_range_(const Point& my_location, int range_from, int range_to, const MonsterPtr& pm);
bool partially_applied_is_in_range_(ANY_FIXED_LOC, ANY_FIXED_r, ANY_FIXED_R, const MonsterPtr& pm);
bool still_a_function_with_only_one_parameter(const MonsterPtr& pm);
Lambda로 Partial application 1/2
auto is_in_range_(const Point& my_location, int range_from, int range_to) {
return [my_location, range_from, range_to](const MonsterPtr& pm) {
int distance = calc_distance(my_location, pm->getLocation());
return distance >= range_from && distance <= range_to;
};
}
auto damaged_monster_(int damage) {
return [damage](const MonsterPtr& pm) {
return std::make_shared<Monster>(pm->getId(),
Monster::calc_damaged_hp(pm->getHP(), damage, pm->getArmor()),
pm->getArmor(), pm->getLocation());
};
}
Lambda로 Partial application 2/2
auto do_damage_(const Point& my_location, int damage,int range_from, int range_to) {
return [=](const MonsterPtr& pm) {
return is_in_range_(my_location, range_from, range_to)(pm) ? damaged_monster_(damage)(pm) : pm;
};
}
bool is_alive(const MonsterPtr& pm) {
return pm->getHP() > 0;
}
STL algorithm 이용하기
void doSplashDamage_stl(const Point& my_location, Monsters& monsters,
int damage, int range_from, int range_to) {
std::transform(monsters.begin(), monsters.end(), monsters.begin(),
do_damage_(my_location, damage, range_from, range_to));
auto is_dead = [](const auto& m) { return !is_alive(m); };
monsters.erase(std::remove_if(monsters.begin(), monsters.end(), is_dead), monsters.end());
}
“누가 네 줄로 짤 코드를 열줄로 만들어 놨어요?”
Ranges TS
Most effective STL using ranges
std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 };
std::vector<int> output;
auto is_even = [](int i) { return i % 2 == 0; };
auto mul_2 = [](int i) { return i * 2; };
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(output), is_even);
std::transform(output.begin(), output.end(), output.begin(), mul_2);
int sum = std::accumulate(output.begin(), output.end(), 0);
auto rng = numbers
| ranges::view::filter(is_even)
| ranges::view::transform(mul_2);
int sum = ranges::accumulate(rng, 0);
// numbers is still {1, 2, 3, 4, 5, 6, 7};
ranges::view로 algorithm을 합성하기
auto doSplashDamage_one_liner(const Point& my_location, const Monsters& monsters,
int damage, int range_from, int range_to) {
return monsters
| ranges::view::transform(do_damage_(my_location, damage, range_from, range_to))
| ranges::view::filter(is_alive);
}
“누가 한 줄로 짤 코드를 열줄로 만들어 놨어요?”
합성으로 구현한 코드의 손쉬운 변경
auto doSplashDamage_one_liner(const Point& my_location, const Monsters& monsters,
int damage, int range_from, int range_to) {
return monsters
| ranges::view::transform(do_damage_(my_location, damage, range_from, range_to))
| ranges::view::transform(apply_healing_(hp, range_from, range_to))
| ranges::view::filter(is_alive);
}
“기존의 코드는 건드리지 조차 않아도 되니 안심!”
ranges의 지연연산
auto doSplashDamage_top_3(const Point& my_location, const Monsters& monsters,
int damage, int range_from, int range_to) {
return monsters
| ranges::view::transform(do_damage_(my_location, damage, range_from, range_to))
| ranges::view::filter(is_alive)
| ranges::view::take(3);
}
쓰지 않을 객체는 만들지도 말고, 필요 없는 계산은 하지도
말자!
std::ranges가 되고 싶은 Ranges TS
• STL을 더욱 효과적으로 활용
• 불변 컨테이너
• 합성 / 지연연산 / Monad *
• https://github.com/ericniebler/range-v3
• https://github.com/Microsoft/Range-V3-VS2015
• 고차함수를 사용해서 코드를 간결하게 만들자
- 간결한 코드 -> 가독성 -> 적은 버그
• 알고리즘의 구현 부분과 사용 부분을 분리하자
- 로직 구현에 실제로 관련 없는 임시변수와 상태들을
없애자
• 함수를 합성 가능하게 만들어서 재활용성을 높이자
- 검증된 재활용이 쉬운 Building block들을 합성
• 지연연산을 활용해서 최적화를 시도하자
- 무한리스트에 대해서 알아보자
FP 실천사항 #3 – 고차함수와 합성
타입시스템의 활용
타입의 활용
• 메모리상에 저장된 데이터가 쓰일 방법을 정의
• 가능한 빠른 단계에서 오류 발견
• 코드의 의미 함축성 증가
• 코드 작성중의 힌트
• 로직 설계의 도구
• 지원하지 않는 동작은 표현 자체가 불가능 하도록
• 합성 동작의 연결을 위한 접착제
std::optional in C++17
void optional_demo() {
std::optional<std::string> name;
assert(!name);
if (name) std::cout << *name << std::endl;
std::optional<std::string> a_name("Anonymous");
assert(a_name);
assert(a_name.value() == "Anonymous");
assert(*a_name == "Anonymous");
if (a_name) std::cout << *a_name << std::endl;
}
• null – “The worst mistake of computer science”
• Type safe / no dynamic allocation
std::variant in C++17
std::variant<std::string, bool, int> v = std::string("Hello!");
assert(std::get<std::string>(v) == "Hello!");
std::variant<std::string, bool, int> b = true;
assert(std::get<bool>(b) == true);
try {
std::cout << std::boolalpha << std::get<bool>(v) << std::endl;
} catch (std::bad_variant_access&) {
std::cout << "exception..." << std::endl;
}
• Discriminated union
• Never-empty guarantee
• Type safe / no dynamic allocation
Quiz #1
struct Human {
unsigned char age_;
bool alive_;
};
다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수
있을까요?
256 * 2 = 512
using A_Tuple_Type = std::tuple<unsigned char, bool>;
Quiz #2
enum Gender : int {
MALE,
FEMALE,
UNKNOWN
};
struct Human {
Gender gender_;
unsigned char age_;
bool alive_;
};
Human은 몇 개의 서로 다른 값(상태)를 나타낼 수
있을까요?
3 * 256 * 2 = 1536
Quiz #3
using Another_Tuple_Type = std::tuple<Gender, unsigned char, bool>;
다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수
있을까요?
3 * 256 * 2 = 1536
동등한 타입
struct Human {
Gender gender_;
unsigned char age_;
bool alive_;
};
std::tuple<Gender, unsigned char, bool>;
Quiz #4
std::optional<Human>;
다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수
있을까요?
1536 + 1 = 1537
Quiz #5
struct Alien { bool alive_; };
std::variant<Human, Alien>;
다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수
있을까요?
1536 + 2 = 1538
대수적 자료형 - Algebraic data types (ADTs) in
C++
• 타입도 합성
• Product types (*) – std::pair, std::tuple, struct/class
• Sum types (+) – std::optional, std::variant
ADTs로 표현 한다면? – 타입의 합성
class HttpRequest {
//...
private:
Url url_;
SecureUrl url_ssl_;
bool ssl_;
std::string id_;
std::string password_;
bool need_auth_;
};
class HttpRequest {
//...
private:
std::variant<Url, SecureUrl> url_;
using Credential =
std::tuple<std::string, std::string>;
std::optional<Credential> auth_;
};
• 유효한 상태의 개수보다 데이터 타입의 Cardinality가
크다면?
무효한 상태를 가질 가능성 -> 버그!
• 타입을 최대한 사용해서 안전한 코드를 만들자
- 불가능한 동작은 표현 자체가 불가능하도록
• 타입이 표현가능한 상태의 개수(Cardinality)를 계산해보자
- Product 타입, Sum 타입
• 유효하지 않은 상태를 가질 수 없는 타입을 정의하자
FP 실천사항 #4 – 타입 시스템
모나드 겉핥기
Download a file and decompress
it
std::tuple<int, std::string> download(const std::string& url);
bool verify(const std::string& body);
bool decompress(const std::string& body, std::string& uncompressed);
std::string imperative(const std::string& url) {
auto r = download(url);
int status_code = std::get<int>(r);
const std::string& body = std::get<std::string>(r);
if (status_code == 200) {
bool verified = verify(body);
if (verified) {
std::string decompressed;
bool success = decompress(body, decompressed);
if (success) {
return decompressed;
}
}
}
return std::string();
}
std::optional 을 사용하면
std::optional<std::string> download_fn(const std::string& url) {
auto r = download(url);
int status_code = std::get<int>(r);
const std::string& body = std::get<std::string>(r);
return status_code == 200 ? std::make_optional(body) : std::nullopt;
}
std::optional<std::string> verify_fn(const std::string& body) {
bool verified = verify(body);
return verified ? std::make_optional(body) : std::nullopt;
}
std::optional<std::string> decompress_fn(const std::string& body) {
std::string decompressed;
bool success = decompress(body, decompressed);
return success ? std::make_optional(decompressed) : std::nullopt;
}
조금 단순화된 if else
문들
std::string do_download(const std::string& url) {
auto body = download_fn(url);
if (body) {
auto verified = verify_fn(*body);
if (verified) {
auto decompressed = decompress_fn(*verified);
if (decompressed)
return *decompressed;
}
}
return std::string();
}
template <typename A>
std::optional<A> unit_(A a) {
return std::make_optional(a);
}
template <typename F>
std::optional<std::string> bind_(const std::optional<std::string>& v, F func) {
return v ? func(*v) : std::nullopt;
}
Define two functions
• 이 두개의 함수를 이용해서 표준적인 방법으로 입출력
타입이 호환되는 함수들을 합성하자!
Using Maybe Monad
void monad_demo() {
auto download_ = [](const std::string& url) {
auto r = download(url);
int status_code = std::get<int>(r);
const std::string& body = std::get<std::string>(r);
return status_code == 200 ? unit_(body) : std::nullopt;
};
auto verify_ = [](const std::string& body) {
bool verified = verify(body);
return verified ? unit_(body) : std::nullopt;
};
auto decompress_ = [](const std::string& body) {
std::string decompressed;
bool success = decompress(body, decompressed);
return success ? unit_(decompressed) : std::nullopt;
};
auto m = [=](auto v) {
return bind_(bind_(bind_(unit_(v), download_), verify_), decompress_);
};
auto result = m(std::string("http://ndc.nexon.com/files/testdata.txt"));
if (result) std::cout << *result << std::endl;
}
auto m = [=](auto v) {
return bind_(bind_(bind_(unit_(v), download_), verify_), decompress_);
};
마지막으로 연산자를 오버로딩 하면
• ranges::view 들을 합성하던 것과 같은 방법!
auto m = [=](auto v) {
return v | download_ | verify_ | decompress_;
};
Monad
A monad is created by defining a type constructor M and two operations, bind and return
(where return is often also called unit):
• The unary return operation takes a value from a plain type (a) and puts it into a container
using the constructor, creating a monadic value (with type M a).
• The binary bind operation ">>=" takes as its arguments a monadic value with type M a and
a function (a → M b) that can transform the value.
• The bind operator unwraps the plain value with type a embedded in its input monadic
value with type M a, and feeds it to the function.
• The function then creates a new monadic value, with type M b, that can be fed to the next
bind operators composed in the pipeline.
https://en.wikipedia.org/wiki/Monad_(functional_programming)
Monad in C++
• Maybe monad using std::optional
• Ranges TS - std::ranges in C++20
• Composable std::future in C++20
요점정리
• 상태가 필수적인 경우가 아니라면 가능한 순수함수를
만들자
• 불변데이터와 관련 알고리즘들을 이해하고 활용하자
• 고차함수를 사용해서 코드를 간결하게 하자
• 알고리즘의 구현과 사용 부분을 코드상에서 분리하자
• 타입을 적극 활용해서 안전한 코드를 만들자
• 합성을 사용해서 코드의 변경을 쉽게 하고 재활용성을
높이자
• 함수형 프로그래밍 언어를 한가지 공부해보자 - Haskell
강추!
감사합니다.

Weitere ähnliche Inhalte

Was ist angesagt?

[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
Sang Don Kim
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
OnGameServer
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
Esun Kim
 

Was ist angesagt? (20)

[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
[Td 2015]녹슨 c++ 코드에 모던 c++로 기름칠하기(옥찬호)
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
 
WTL 소개
WTL 소개WTL 소개
WTL 소개
 
C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발C#을 사용한 빠른 툴 개발
C#을 사용한 빠른 툴 개발
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
 
C++11
C++11C++11
C++11
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
 
프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기프로그래밍 대회: C++11 이야기
프로그래밍 대회: C++11 이야기
 
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기
NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기
 
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
[TechDays Korea 2015] 녹슨 C++ 코드에 모던 C++로 기름칠하기
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 
Boost
BoostBoost
Boost
 
Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리Refelction의 개념과 RTTR 라이브러리
Refelction의 개념과 RTTR 라이브러리
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
Deview 2019 눈발자국
Deview 2019 눈발자국Deview 2019 눈발자국
Deview 2019 눈발자국
 
Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉Modern C++의 타입 추론과 람다, 컨셉
Modern C++의 타입 추론과 람다, 컨셉
 
Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기Android Native Module 안정적으로 개발하기
Android Native Module 안정적으로 개발하기
 
[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)[devil's camp] - 알고리즘 대회와 STL (박인서)
[devil's camp] - 알고리즘 대회와 STL (박인서)
 

Ähnlich wie Nexon Developers Conference 2017 Functional Programming for better code - Modern C++

Effective c++(chapter 5,6)
Effective c++(chapter 5,6)Effective c++(chapter 5,6)
Effective c++(chapter 5,6)
문익 장
 
프로그램은 왜 실패 하는가
프로그램은 왜 실패 하는가프로그램은 왜 실패 하는가
프로그램은 왜 실패 하는가
홍준 김
 
Cpp 0x kimRyungee
Cpp 0x kimRyungeeCpp 0x kimRyungee
Cpp 0x kimRyungee
scor7910
 
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH, 케이티하이텔
 
14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿
유석 남
 
2 1. variables & data types
2 1. variables & data types2 1. variables & data types
2 1. variables & data types
웅식 전
 

Ähnlich wie Nexon Developers Conference 2017 Functional Programming for better code - Modern C++ (20)

6 function
6 function6 function
6 function
 
06장 함수
06장 함수06장 함수
06장 함수
 
W14 chap13
W14 chap13W14 chap13
W14 chap13
 
Effective c++(chapter 5,6)
Effective c++(chapter 5,6)Effective c++(chapter 5,6)
Effective c++(chapter 5,6)
 
Angular2 가기전 Type script소개
 Angular2 가기전 Type script소개 Angular2 가기전 Type script소개
Angular2 가기전 Type script소개
 
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
 
Python vs Java @ PyCon Korea 2017
Python vs Java @ PyCon Korea 2017Python vs Java @ PyCon Korea 2017
Python vs Java @ PyCon Korea 2017
 
강의자료 2
강의자료 2강의자료 2
강의자료 2
 
C++ Advanced 강의 2주차
C++ Advanced 강의 2주차C++ Advanced 강의 2주차
C++ Advanced 강의 2주차
 
Java mentoring of samsung scsc 2
Java mentoring of samsung scsc   2Java mentoring of samsung scsc   2
Java mentoring of samsung scsc 2
 
프로그램은 왜 실패 하는가
프로그램은 왜 실패 하는가프로그램은 왜 실패 하는가
프로그램은 왜 실패 하는가
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 
Cpp 0x kimRyungee
Cpp 0x kimRyungeeCpp 0x kimRyungee
Cpp 0x kimRyungee
 
한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha
한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha
한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha
 
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
 
c++ opencv tutorial
c++ opencv tutorialc++ opencv tutorial
c++ opencv tutorial
 
Visual studio 2010
Visual studio 2010Visual studio 2010
Visual studio 2010
 
5장 객체와클래스
5장 객체와클래스5장 객체와클래스
5장 객체와클래스
 
14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿14장 - 15장 예외처리, 템플릿
14장 - 15장 예외처리, 템플릿
 
2 1. variables & data types
2 1. variables & data types2 1. variables & data types
2 1. variables & data types
 

Nexon Developers Conference 2017 Functional Programming for better code - Modern C++

  • 1. 더 좋은 코드를 위한 함수형 프로그래밍 모던 C++를 중심으로 Blizzard Entertainment 전이삭
  • 2. 오늘 살펴볼 개념들 • 순수함수와 불변성 • 고차함수와 함수의 합성 • 타입 시스템과 대수적 자료형 • 타입을 매개로 한 함수의 합성 *
  • 4. 순수함수 - Pure functions • 함수의 결과값이 오직 입력 인자 값들에 의해서만 결정 • 특정 입력 값에 대해서 결과값이 결정적으로 대응되는 함수 • 부작용(Side effects)이 없는 함수 int abs(int n); int atoi(const char *str); int getDefaultPort(bool https) { return https ? 443 : 80; } int rand(); time_t time (time_t* timer); bool g_https = false; int getDefaultPort() { return g_https ? 443 : 80; }
  • 5. 부작용 - Side effects • 상태에 영향을 받거나 변화를 주는 모든 동작 • 화면출력 • 소켓통신 • 디스크 I/O • 예외 catch • 현재 시각 읽기 • 난수 발생
  • 6. 참조 투명성 - Referential transparency • 어떤 표현식을 그 표현식의 결과값으로 교체해도 전체 프로그램의 실행결과에 영향을 주지 않는 성질 • 컴파일러에게 다양한 최적화 기회를 제공 기억 – Memoization - 입력값 / 함수명을 알면 결과값은 항상 같음 지연연산 - Lazy evaluation - 실제로 결과값이 필요할 때만 함수를 평가
  • 7. • 코드에서 가능한 순수함수의 비율이 많아지도록 하자 - 글로벌 변수, 멤버 변수 의존도를 줄이자 - 상태를 없애거나 줄이고 부작용을 격리시키자 • 함수는 인자를 받아서 값을 리턴하는 형태로 - 레퍼런스 인자 보다는 값을 리턴 - 여러 개의 값을 리턴할 때는 tuple을 사용 FP 실천사항 #1 - 순수함수
  • 9. Java String is immutable String str = "Welcome to NDC2016!!!"; String newString = str.replace(“2016”, “2017”); System.out.println(newString); String str = "Welcome to NDC2016!!!"; str.replace(“2016”, “2017”); System.out.println(str); Welcome to NDC2016!!! Welcome to NDC2017!!!
  • 10. 불변객체의 특징 • 내용의 변경은 새로운 객체를 생성할 때만 가능 • 생성, 테스트, 사용법이 단순하고 쉬움 • 진정한 의미의 불변객체는 그 자체로 Thread-safe • 쉬운 캐쉬 – 이름이 같으면 내용도 같다 • Temporal coupling 줄임 • Identity mutability problem 없음
  • 11. Temporal coupling HttpRequest request; request.SetUrl("https://hostname/api"); request.SetSSL(true); request.SetCACert("ca-bundle.crt"); request.Post(); HttpRequest request; request.SetUrl("https://hostname/api"); request.SetSSL(true); request.Post(); // Error! class HttpRequest { public: HttpRequest(const std::string& url, bool ssl, const std::string& cacert); // no setters ... }; HttpRequest request("https://hostname/api", true, "ca-bundle.crt"); request.Post();
  • 12. Identity mutability problem class Monster { public: … int64_t id() const { return id_; } std::string name() const { return name_; } void setID(int64_t id) { id_ = id; } void setName(const std::string& name) { name_ = name; } … }; using MonsterPtr = std::shared_ptr<Monster>; std::map<int64_t, MonsterPtr> monsters; MonsterPtr p1 = std::make_shared<Monster>(1234, “Murloc"); monsters.insert(std::make_pair(p1.id(), p1)); p1->setID(2234); // ???
  • 15. • 사용하는 객체를 불변으로 일단 바꾸어 놓고 코드를 고쳐보자 - Setter들 대신에 생성자에서 인자로 초기화 - 객체를 변경하지 않는 멤버함수들은 const로 • 다양한 분산 빅데이터 알고리즘들을 조사해보자 - Immutability changes everything - http://queue.acm.org/detail.cfm?id=2884038 • 영속형 자료 구조에 대해서 알아보자 FP 실천사항 #2 - 불변객체
  • 17. 고차함수 • 함수의 인자로 한 개 이상의 함수를 넘겨 받거나, 함수의 결과값으로 다른 함수를 돌려주는 함수 • 고차함수의 보물창고 - #include <algorithm>
  • 18. FP의 기본 초식 3총사 • std::transform • std::remove_if / std::copy_if • std::accumulate
  • 19. std::accumulate using Strings = std::vector<std::string>; int64_t hash(const std::string& str, int64_t seed); int64_t calculate_hash_by_stl(const Strings& strs) { return std::accumulate(std::begin(strs), std::end(strs), 12345ll, [](int64_t seed, const std::string& s) { return hash(s, seed); }); } 추천 동영상 – CppCon 2016 "std::accumulate: Exploring an Algorithmic Empire“ - https://youtu.be/B6twozNPUoA
  • 20. Unix 명령어들의 합성 - PIPE history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head 47 ls 26 cd 19 sudo 17 clang++ 10 clang 7 history 7 ./a.out 5 ./compose 4 gedit 2 top
  • 21. 예제 – 몬스터에게 범위공격 • 특정 거리 r 이상 R 이하에 있는 대상을 공격 • 방어막이 있으면 방어막의 세기만큼 피해 감소 Dev-art by TH. Ahn
  • 22. Monster class class Monster { public: Monster(); void setId(int id); void setHP(int hp); void setArmor(int armor); void setLocation(const Point& pt); int getHP() const; int getId() const; bool getArmor() const; Point getLocation() const; void gotDamage(int damage); private: int id_; int hp_; int armor_; Point pt_; }; using MonsterPtr = std::shared_ptr<Monster>; using Monsters = std::vector<MonsterPtr>; void Monster::gotDamage(int damage) { hp_ = hp_ - std::min(hp_, damage - armor_); } int calc_distance(const Point& pt1, const Point& pt2);
  • 23. Monsters doSplashDamage(const Point& my_location, const Monsters& monsters, int damage, int range_from, int range_to) { for (auto i = monsters.begin(); i != monsters.end(); ++i) { auto pt = *i; Monster& target = *pt; int distance = calc_distance(my_location, target.getLocation()); if (distance >= range_from && distance <= range_to) { target.gotDamage(damage); } } Monsters alive_monsters; for (auto i = monsters.begin(); i != monsters.end(); ++i) { auto pt = *i; Monster& target = *pt; if (target.getHP() > 0) { alive_monsters.push_back(pt); } } return alive_monsters; } 전형적인 절차형 코드
  • 24. 우리가 하고 싶은 것들 • 객체를 불변데이터로 다루고 싶다 • 가능한 순수함수를 쓰고 싶다 • 알고리즘 구현 부분과 사용 부분을 코드상에서 분리하고 싶다 • 임시변수들을 없애고 싶다 • 함수들을 쉽게 합성하고 싶다
  • 25. 불변 Monster class와 피해계산 순수함수 class Monster { public: Monster(int id, int hp, int armor, const Point& pt); int getHP() const; int getId() const; int getArmor() const; Point getLocation() const; static int calc_damaged_hp(int hp, int damage, int armor); private: int id_; int hp_; int armor_; Point pt_; }; int Monster::calc_damaged_hp(int hp, int damage, int armor) { return hp - std::min(hp, damage - armor); }
  • 26. 주요 문장들을 함수로 쪼개기 bool is_in_range(const Point& my_location, int range_from, int range_to, const MonsterPtr& pm); MonsterPtr damaged_monster(int damage, const MonsterPtr& pm); bool is_alive(const MonsterPtr& pm);
  • 27. Partial application bool is_in_range_(const Point& my_location, int range_from, int range_to, const MonsterPtr& pm); bool partially_applied_is_in_range_(ANY_FIXED_LOC, ANY_FIXED_r, ANY_FIXED_R, const MonsterPtr& pm); bool still_a_function_with_only_one_parameter(const MonsterPtr& pm);
  • 28. Lambda로 Partial application 1/2 auto is_in_range_(const Point& my_location, int range_from, int range_to) { return [my_location, range_from, range_to](const MonsterPtr& pm) { int distance = calc_distance(my_location, pm->getLocation()); return distance >= range_from && distance <= range_to; }; } auto damaged_monster_(int damage) { return [damage](const MonsterPtr& pm) { return std::make_shared<Monster>(pm->getId(), Monster::calc_damaged_hp(pm->getHP(), damage, pm->getArmor()), pm->getArmor(), pm->getLocation()); }; }
  • 29. Lambda로 Partial application 2/2 auto do_damage_(const Point& my_location, int damage,int range_from, int range_to) { return [=](const MonsterPtr& pm) { return is_in_range_(my_location, range_from, range_to)(pm) ? damaged_monster_(damage)(pm) : pm; }; } bool is_alive(const MonsterPtr& pm) { return pm->getHP() > 0; }
  • 30. STL algorithm 이용하기 void doSplashDamage_stl(const Point& my_location, Monsters& monsters, int damage, int range_from, int range_to) { std::transform(monsters.begin(), monsters.end(), monsters.begin(), do_damage_(my_location, damage, range_from, range_to)); auto is_dead = [](const auto& m) { return !is_alive(m); }; monsters.erase(std::remove_if(monsters.begin(), monsters.end(), is_dead), monsters.end()); } “누가 네 줄로 짤 코드를 열줄로 만들어 놨어요?”
  • 32. Most effective STL using ranges std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 }; std::vector<int> output; auto is_even = [](int i) { return i % 2 == 0; }; auto mul_2 = [](int i) { return i * 2; }; std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(output), is_even); std::transform(output.begin(), output.end(), output.begin(), mul_2); int sum = std::accumulate(output.begin(), output.end(), 0); auto rng = numbers | ranges::view::filter(is_even) | ranges::view::transform(mul_2); int sum = ranges::accumulate(rng, 0); // numbers is still {1, 2, 3, 4, 5, 6, 7};
  • 33. ranges::view로 algorithm을 합성하기 auto doSplashDamage_one_liner(const Point& my_location, const Monsters& monsters, int damage, int range_from, int range_to) { return monsters | ranges::view::transform(do_damage_(my_location, damage, range_from, range_to)) | ranges::view::filter(is_alive); } “누가 한 줄로 짤 코드를 열줄로 만들어 놨어요?”
  • 34. 합성으로 구현한 코드의 손쉬운 변경 auto doSplashDamage_one_liner(const Point& my_location, const Monsters& monsters, int damage, int range_from, int range_to) { return monsters | ranges::view::transform(do_damage_(my_location, damage, range_from, range_to)) | ranges::view::transform(apply_healing_(hp, range_from, range_to)) | ranges::view::filter(is_alive); } “기존의 코드는 건드리지 조차 않아도 되니 안심!”
  • 35. ranges의 지연연산 auto doSplashDamage_top_3(const Point& my_location, const Monsters& monsters, int damage, int range_from, int range_to) { return monsters | ranges::view::transform(do_damage_(my_location, damage, range_from, range_to)) | ranges::view::filter(is_alive) | ranges::view::take(3); } 쓰지 않을 객체는 만들지도 말고, 필요 없는 계산은 하지도 말자!
  • 36. std::ranges가 되고 싶은 Ranges TS • STL을 더욱 효과적으로 활용 • 불변 컨테이너 • 합성 / 지연연산 / Monad * • https://github.com/ericniebler/range-v3 • https://github.com/Microsoft/Range-V3-VS2015
  • 37. • 고차함수를 사용해서 코드를 간결하게 만들자 - 간결한 코드 -> 가독성 -> 적은 버그 • 알고리즘의 구현 부분과 사용 부분을 분리하자 - 로직 구현에 실제로 관련 없는 임시변수와 상태들을 없애자 • 함수를 합성 가능하게 만들어서 재활용성을 높이자 - 검증된 재활용이 쉬운 Building block들을 합성 • 지연연산을 활용해서 최적화를 시도하자 - 무한리스트에 대해서 알아보자 FP 실천사항 #3 – 고차함수와 합성
  • 39. 타입의 활용 • 메모리상에 저장된 데이터가 쓰일 방법을 정의 • 가능한 빠른 단계에서 오류 발견 • 코드의 의미 함축성 증가 • 코드 작성중의 힌트 • 로직 설계의 도구 • 지원하지 않는 동작은 표현 자체가 불가능 하도록 • 합성 동작의 연결을 위한 접착제
  • 40. std::optional in C++17 void optional_demo() { std::optional<std::string> name; assert(!name); if (name) std::cout << *name << std::endl; std::optional<std::string> a_name("Anonymous"); assert(a_name); assert(a_name.value() == "Anonymous"); assert(*a_name == "Anonymous"); if (a_name) std::cout << *a_name << std::endl; } • null – “The worst mistake of computer science” • Type safe / no dynamic allocation
  • 41. std::variant in C++17 std::variant<std::string, bool, int> v = std::string("Hello!"); assert(std::get<std::string>(v) == "Hello!"); std::variant<std::string, bool, int> b = true; assert(std::get<bool>(b) == true); try { std::cout << std::boolalpha << std::get<bool>(v) << std::endl; } catch (std::bad_variant_access&) { std::cout << "exception..." << std::endl; } • Discriminated union • Never-empty guarantee • Type safe / no dynamic allocation
  • 42. Quiz #1 struct Human { unsigned char age_; bool alive_; }; 다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수 있을까요? 256 * 2 = 512 using A_Tuple_Type = std::tuple<unsigned char, bool>;
  • 43. Quiz #2 enum Gender : int { MALE, FEMALE, UNKNOWN }; struct Human { Gender gender_; unsigned char age_; bool alive_; }; Human은 몇 개의 서로 다른 값(상태)를 나타낼 수 있을까요? 3 * 256 * 2 = 1536
  • 44. Quiz #3 using Another_Tuple_Type = std::tuple<Gender, unsigned char, bool>; 다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수 있을까요? 3 * 256 * 2 = 1536
  • 45. 동등한 타입 struct Human { Gender gender_; unsigned char age_; bool alive_; }; std::tuple<Gender, unsigned char, bool>;
  • 46. Quiz #4 std::optional<Human>; 다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수 있을까요? 1536 + 1 = 1537
  • 47. Quiz #5 struct Alien { bool alive_; }; std::variant<Human, Alien>; 다음 타입은 몇 개의 서로 다른 값(상태)를 나타낼 수 있을까요? 1536 + 2 = 1538
  • 48. 대수적 자료형 - Algebraic data types (ADTs) in C++ • 타입도 합성 • Product types (*) – std::pair, std::tuple, struct/class • Sum types (+) – std::optional, std::variant
  • 49. ADTs로 표현 한다면? – 타입의 합성 class HttpRequest { //... private: Url url_; SecureUrl url_ssl_; bool ssl_; std::string id_; std::string password_; bool need_auth_; }; class HttpRequest { //... private: std::variant<Url, SecureUrl> url_; using Credential = std::tuple<std::string, std::string>; std::optional<Credential> auth_; }; • 유효한 상태의 개수보다 데이터 타입의 Cardinality가 크다면? 무효한 상태를 가질 가능성 -> 버그!
  • 50. • 타입을 최대한 사용해서 안전한 코드를 만들자 - 불가능한 동작은 표현 자체가 불가능하도록 • 타입이 표현가능한 상태의 개수(Cardinality)를 계산해보자 - Product 타입, Sum 타입 • 유효하지 않은 상태를 가질 수 없는 타입을 정의하자 FP 실천사항 #4 – 타입 시스템
  • 52. Download a file and decompress it std::tuple<int, std::string> download(const std::string& url); bool verify(const std::string& body); bool decompress(const std::string& body, std::string& uncompressed); std::string imperative(const std::string& url) { auto r = download(url); int status_code = std::get<int>(r); const std::string& body = std::get<std::string>(r); if (status_code == 200) { bool verified = verify(body); if (verified) { std::string decompressed; bool success = decompress(body, decompressed); if (success) { return decompressed; } } } return std::string(); }
  • 53. std::optional 을 사용하면 std::optional<std::string> download_fn(const std::string& url) { auto r = download(url); int status_code = std::get<int>(r); const std::string& body = std::get<std::string>(r); return status_code == 200 ? std::make_optional(body) : std::nullopt; } std::optional<std::string> verify_fn(const std::string& body) { bool verified = verify(body); return verified ? std::make_optional(body) : std::nullopt; } std::optional<std::string> decompress_fn(const std::string& body) { std::string decompressed; bool success = decompress(body, decompressed); return success ? std::make_optional(decompressed) : std::nullopt; }
  • 54. 조금 단순화된 if else 문들 std::string do_download(const std::string& url) { auto body = download_fn(url); if (body) { auto verified = verify_fn(*body); if (verified) { auto decompressed = decompress_fn(*verified); if (decompressed) return *decompressed; } } return std::string(); }
  • 55. template <typename A> std::optional<A> unit_(A a) { return std::make_optional(a); } template <typename F> std::optional<std::string> bind_(const std::optional<std::string>& v, F func) { return v ? func(*v) : std::nullopt; } Define two functions • 이 두개의 함수를 이용해서 표준적인 방법으로 입출력 타입이 호환되는 함수들을 합성하자!
  • 56. Using Maybe Monad void monad_demo() { auto download_ = [](const std::string& url) { auto r = download(url); int status_code = std::get<int>(r); const std::string& body = std::get<std::string>(r); return status_code == 200 ? unit_(body) : std::nullopt; }; auto verify_ = [](const std::string& body) { bool verified = verify(body); return verified ? unit_(body) : std::nullopt; }; auto decompress_ = [](const std::string& body) { std::string decompressed; bool success = decompress(body, decompressed); return success ? unit_(decompressed) : std::nullopt; }; auto m = [=](auto v) { return bind_(bind_(bind_(unit_(v), download_), verify_), decompress_); }; auto result = m(std::string("http://ndc.nexon.com/files/testdata.txt")); if (result) std::cout << *result << std::endl; }
  • 57. auto m = [=](auto v) { return bind_(bind_(bind_(unit_(v), download_), verify_), decompress_); }; 마지막으로 연산자를 오버로딩 하면 • ranges::view 들을 합성하던 것과 같은 방법! auto m = [=](auto v) { return v | download_ | verify_ | decompress_; };
  • 58. Monad A monad is created by defining a type constructor M and two operations, bind and return (where return is often also called unit): • The unary return operation takes a value from a plain type (a) and puts it into a container using the constructor, creating a monadic value (with type M a). • The binary bind operation ">>=" takes as its arguments a monadic value with type M a and a function (a → M b) that can transform the value. • The bind operator unwraps the plain value with type a embedded in its input monadic value with type M a, and feeds it to the function. • The function then creates a new monadic value, with type M b, that can be fed to the next bind operators composed in the pipeline. https://en.wikipedia.org/wiki/Monad_(functional_programming)
  • 59. Monad in C++ • Maybe monad using std::optional • Ranges TS - std::ranges in C++20 • Composable std::future in C++20
  • 60. 요점정리 • 상태가 필수적인 경우가 아니라면 가능한 순수함수를 만들자 • 불변데이터와 관련 알고리즘들을 이해하고 활용하자 • 고차함수를 사용해서 코드를 간결하게 하자 • 알고리즘의 구현과 사용 부분을 코드상에서 분리하자 • 타입을 적극 활용해서 안전한 코드를 만들자 • 합성을 사용해서 코드의 변경을 쉽게 하고 재활용성을 높이자 • 함수형 프로그래밍 언어를 한가지 공부해보자 - Haskell 강추!