Skip to content

Latest commit

Β 

History

History
342 lines (246 loc) Β· 18.2 KB

04-kleisli-categories.md

File metadata and controls

342 lines (246 loc) Β· 18.2 KB

Kleisli λ²”μ£Ό

ν”„λ‘œκ·Έλž˜λ¨Έλ₯Ό μœ„ν•œ λ²”μ£Όλ‘ μ˜ 이전 λͺ‡ 개의 κΈ€μ—μ„œ 크고 μž‘μ€ λ²”μ£Όμ˜ λͺ‡ 가지 μ˜ˆμ‹œλ₯Ό μ œμ‹œν–ˆμŠ΅λ‹ˆλ‹€. 이 κΈ€μ—μ„œλŠ” 더 κ³ κΈ‰ μ˜ˆμ‹œλ₯Ό 톡해 μ„€λͺ…을 진행할 κ²ƒμž…λ‹ˆλ‹€. 이 μ‹œλ¦¬μ¦ˆλ₯Ό 처음 μ ‘ν•˜λŠ” 경우 λͺ©μ°¨λ₯Ό ν™•μΈν•˜μ„Έμš”.

둜그 ν•©μ„±

νƒ€μž…κ³Ό 순수 ν•¨μˆ˜λ₯Ό λ²”μ£Όλ‘œ λͺ¨λΈλ§ ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€. λ˜ν•œ λ²”μ£Όλ‘ μ—μ„œ λΆ€μž‘μš©, 즉 μˆœμˆ˜ν•˜μ§€ μ•Šμ€ ν•¨μˆ˜λ₯Ό λͺ¨λΈλ§ν•˜λŠ” 방법이 μžˆλ‹€κ³  μ–ΈκΈ‰ν–ˆμŠ΅λ‹ˆλ‹€. 싀행을 κΈ°λ‘ν•˜κ±°λ‚˜ μΆ”μ ν•˜λŠ” ν•¨μˆ˜λΌλŠ” μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. λͺ…λ Ήν˜• μ–Έμ–΄μ—μ„œλŠ” μ•„λž˜μ™€ 같이 μ „μ—­ μƒνƒœλ₯Ό λ³€κ²½ν•˜μ—¬ κ΅¬ν˜„λ  κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€.

string logger;

bool negate(bool b) {
     logger += "Not so! ";
     return !b;
}

이 κΈ°μ–΅λœ λ²„μ „μ˜ ν•¨μˆ˜λŠ” 둜그λ₯Ό μƒμ„±ν•˜μ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμ— 순수 ν•¨μˆ˜κ°€ μ•„λ‹ˆλΌλŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. 이 ν•¨μˆ˜μ—λŠ” λΆ€μž‘μš©κ°€ μžˆμŠ΅λ‹ˆλ‹€.

ν˜„λŒ€ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μš°λ¦¬λŠ” κ°€λŠ₯ν•œ ν•œ λ³€κ²½ κ°€λŠ₯ν•œ μ „μ—­ μƒνƒœλ₯Ό ν”Όν•˜λ €κ³  λ…Έλ ₯ν•©λ‹ˆλ‹€. λ™μ‹œμ„±μ˜ λ³΅μž‘μ„± λ•Œλ¬Έμž…λ‹ˆλ‹€. 그리고 λΌμ΄λΈŒλŸ¬λ¦¬μ— 이런 μ½”λ“œλ₯Ό 넣지 μ•Šμ„ κ²ƒμž…λ‹ˆλ‹€.

λ‹€ν–‰νžˆλ„ 이 ν•¨μˆ˜λ₯Ό μˆœμˆ˜ν•˜κ²Œ λ§Œλ“œλŠ” 것이 κ°€λŠ₯ν•©λ‹ˆλ‹€. 둜그λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ•ˆνŒŽμœΌλ‘œ μ „λ‹¬ν•˜κΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€. λ¬Έμžμ—΄ 인자λ₯Ό μΆ”κ°€ν•˜κ³  μ—…λ°μ΄νŠΈλœ λ‘œκ·Έκ°€ ν¬ν•¨λœ λ¬Έμžμ—΄κ³Ό 일반 좜λ ₯을 쌍으둜 λ§Œλ“€μ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

pair<bool, string> negate(bool b, string logger) {
     return make_pair(!b, logger + "Not so! ");
}

이 ν•¨μˆ˜λŠ” μˆœμˆ˜ν•˜κ³  λΆ€μž‘μš©μ΄ μ—†μœΌλ©° λ™μΌν•œ 인자둜 호좜될 λ•Œλ§ˆλ‹€ λ™μΌν•œ μŒμ„ λ°˜ν™˜ν•˜λ©° ν•„μš”ν•œ 경우 κΈ°μ–΅ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 둜그의 λˆ„μ λ˜λŠ” νŠΉμ„±μ„ κ³ λ €ν•  λ•Œ 주어진 호좜둜 μ΄μ–΄μ§ˆ 수 μžˆλŠ” λͺ¨λ“  κ°€λŠ₯ν•œ λ‘œκ·Έλ“€μ„ κΈ°μ–΅ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ•„λž˜μ— 각각 κΈ°μ–΅λ˜λŠ” ν•­λͺ©μ΄ μžˆμŠ΅λ‹ˆλ‹€.

negate(true, "It was the best of times. ");
// λ˜λŠ”
negate(true, "It was the worst of times. ");
// λ“±λ“±

λ˜ν•œ 라이브러리 κΈ°λŠ₯을 μœ„ν•œ μ•„μ£Ό 쒋은 μΈν„°νŽ˜μ΄μŠ€λ„ μ•„λ‹™λ‹ˆλ‹€. ν˜ΈμΆœμžλŠ” λ°˜ν™˜ νƒ€μž…μ˜ λ¬Έμžμ—΄μ„ 자유둭게 λ¬΄μ‹œν•  수 μžˆμœΌλ―€λ‘œ 큰 뢀담은 μ•„λ‹™λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μž…λ ₯으둜 λ¬Έμžμ—΄μ„ 전달해야 ν•˜λ―€λ‘œ λΆˆνŽΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

같은 일을 덜 λ°©ν•΄κ°€ λ˜λŠ” λ°©λ²•μœΌλ‘œ 관심사λ₯Ό λΆ„λ¦¬ν•˜λŠ” 방법이 μžˆμ„κΉŒμš”? 이 κ°„λ‹¨ν•œ μ˜ˆμ‹œμ—μ„œ negate ν•¨μˆ˜μ˜ λͺ©μ μ€ ν•˜λ‚˜μ˜ Boolena을 λ‹€λ₯Έ κ²ƒμœΌλ‘œ λ°”κΎΈλŠ” κ²ƒμž…λ‹ˆλ‹€. 둜그λ₯Ό λ‚¨κΈ°λŠ” 것은 2μ°¨μ μž…λ‹ˆλ‹€. λ¬Όλ‘  κΈ°λ‘λ˜λŠ” λ©”μ‹œμ§€λŠ” ν•¨μˆ˜μ— 따라 λ‹€λ₯΄μ§€λ§Œ λ©”μ‹œμ§€λ₯Ό ν•˜λ‚˜μ˜ 연속 둜그둜 μ§‘κ³„ν•˜λŠ” μž‘μ—…μ€ λ³„κ°œμ˜ λ¬Έμ œμž…λ‹ˆλ‹€. μš°λ¦¬λŠ” μ—¬μ „νžˆ ν•¨μˆ˜κ°€ λ¬Έμžμ—΄μ„ μƒμ„±ν•˜κΈ°λ₯Ό μ›ν•˜μ§€λ§Œ, 둜그 μƒμ„±μ—μ„œ 뢀담을 λœμ–΄μ£Όκ³  μ‹ΆμŠ΅λ‹ˆλ‹€. νƒ€ν˜‘ν•  수 μžˆλŠ” 해결책은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

pair<bool, string> negate(bool b) {
     return make_pair(!b, "Not so! ");
}

이 μ•„μ΄λ””μ–΄λŠ” λ‘œκ·Έκ°€ ν•¨μˆ˜ 호좜 간에 μˆ˜μ§‘λœλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

이것이 μ–΄λ–»κ²Œ κ°€λŠ₯ν•œμ§€ 보기 μœ„ν•΄ 쑰금 더 ν˜„μ‹€μ μΈ μ˜ˆμ‹œλ‘œ λ°”κΎΈμ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. λ¬Έμžμ—΄μ—μ„œ μ†Œλ¬Έμžλ₯Ό λŒ€λ¬Έμžλ‘œ λ°”κΎΈλŠ” ν•˜λ‚˜μ˜ ν•¨μˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€.

string toUpper(string s) {
    string result;
    int (*toupperp)(int) = &toupper; // toupperλŠ” overload λ˜μ—ˆμŠ΅λ‹ˆλ‹€.
    transform(begin(s), end(s), back_inserter(result), toupperp);
    return result;
}

λ‹€λ₯Έ ν•˜λ‚˜λŠ” λ¬Έμžμ—΄μ„ 곡백 κ²½κ³„λ‘œ λ¬Έμžμ—΄ λ²‘ν„°λ‘œ λ‚˜λˆ•λ‹ˆλ‹€.

vector<string> toWords(string s) {
    return words(s);
}

μ‹€μ œ μž‘μ—…μ€ 보쑰 ν•¨μˆ˜ wordsμ—μ„œ μˆ˜ν–‰λ©λ‹ˆλ‹€.

vector<string> words(string s) {
    vector<string> result{""};
    for (auto i = begin(s); i != end(s); ++i)
    {
        if (isspace(*i))
            result.push_back("");
        else
            result.back() += *i;
    }
    return result;
}

toUpper 및 toWords ν•¨μˆ˜λ₯Ό μˆ˜μ •ν•΄ 일반 λ°˜ν™˜ κ°’ μœ„μ— λ©”μ‹œμ§€ λ¬Έμžμ—΄μ„ ν”ΌκΈ°λ°±(piggyback) ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.

μš°λ¦¬λŠ” 이런 ν•¨μˆ˜μ˜ λ°˜ν™˜ 값을 "μž₯식"(embellish)ν•  κ²ƒμž…λ‹ˆλ‹€. 첫 번째 μ»΄ν¬λ„ŒνŠΈκ°€ μž„μ˜ νƒ€μž… A의 값이고 두 번째 ꡬ성 μš”μ†Œκ°€ λ¬Έμžμ—΄μΈ μŒμ„ μΊ‘μŠν™”ν•˜λŠ” ν…œν”Œλ¦Ώ Writerλ₯Ό μ •μ˜ν•΄ 일반적인 λ°©μ‹μœΌλ‘œ μ²˜λ¦¬ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

template<class A>
using Writer = pair<A, string>;

μ•„λž˜λŠ” μž₯μ‹λœ ν•¨μˆ˜μž…λ‹ˆλ‹€.

Writer<string> toUpper(string s) {
    string result;
    int (*toupperp)(int) = &toupper;
    transform(begin(s), end(s), back_inserter(result), toupperp);
    return make_pair(result, "toUpper ");
}

Writer<vector<string>> toWords(string s) {
    return make_pair(words(s), "toWords ");
}

이 두 ν•¨μˆ˜λ₯Ό λ¬Έμžμ—΄μ„ λŒ€λ¬Έμžλ‘œ λ°”κΎΈκ³  이λ₯Ό λ‹¨μ–΄λ‘œ λ‚˜λˆ„λŠ” 또 λ‹€λ₯Έ μž₯μ‹λœ ν•¨μˆ˜λ‘œ ν•©μ„±ν•˜κ³  κ·Έ μž‘μ—…μ˜ 둜그λ₯Ό μƒμ„±ν•˜κΈ°λ₯Ό μ›ν•©λ‹ˆλ‹€. 방법은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

Writer<vector<string>> process(string s) {
    auto p1 = toUpper(s);
    auto p2 = toWords(p1.first);
    return make_pair(p2.first, p1.second + p2.second);
}

μš°λ¦¬λŠ” λͺ©ν‘œλ₯Ό λ‹¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€. 둜그 μˆ˜μ§‘μ€ 더 이상 κ°œλ³„ ν•¨μˆ˜μ˜ λ¬Έμ œκ°€ μ•„λ‹™λ‹ˆλ‹€. 이것듀은 자체적인 λ©”μ‹œμ§€λ₯Ό μƒμ„±ν•œ λ‹€μŒ μ™ΈλΆ€μ—μ„œ 더 큰 둜그둜 μ—°κ²°ν•©λ‹ˆλ‹€.

이제 이 μŠ€νƒ€μΌλ‘œ μž‘μ„±λœ 전체 ν”„λ‘œκ·Έλž¨μ„ 상상해 λ³΄μ„Έμš”. 반볡적이고 였λ₯˜κ°€ λ°œμƒν•˜κΈ° μ‰¬μš΄ μ½”λ“œμ˜ μ•…λͺ½μž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ μš°λ¦¬λŠ” ν”„λ‘œκ·Έλž˜λ¨Έμž…λ‹ˆλ‹€. μš°λ¦¬λŠ” 반볡적인 μ½”λ“œλ₯Ό μ²˜λ¦¬ν•˜λŠ” 방법을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” μ΄κ²ƒλ“€μ˜ ν•¨μˆ˜ ν•©μ„± 자체λ₯Ό 좔상화해야 ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 합성은 λ²”μ£Όλ‘ μ˜ λ³Έμ§ˆμ΄λ―€λ‘œ 더 λ§Žμ€ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κΈ° 전에 λ²”μ£Ό κ΄€μ μ—μ„œ 문제λ₯Ό 뢄석해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

Writer λ²”μ£Ό

λͺ‡ 가지 μΆ”κ°€ ν•¨μˆ˜λ₯Ό ν”ΌκΈ°λ°± ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…μ„ κΎΈλ―ΈλŠ” μ•„μ΄λ””μ–΄λŠ” 맀우 μœ μš©ν•œ κ²ƒμœΌλ‘œ 판λͺ…λ˜μ—ˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬λŠ” 그것에 ν•΄λ‹Ήν•˜λŠ” 더 λ§Žμ€ μ˜ˆμ‹œλ₯Ό 보게 될 κ²ƒμž…λ‹ˆλ‹€. μ‹œμž‘μ μ€ νƒ€μž…κ³Ό ν•¨μˆ˜μ˜ 일반 λ²”μ£Όμž…λ‹ˆλ‹€. μš°λ¦¬λŠ” νƒ€μž…μ„ 객체둜 λ‚¨κ²¨λ‘μ§€λ§Œ 사상을 μž₯μ‹λœ ν•¨μˆ˜λ‘œ μž¬μ •μ˜ν•  κ²ƒμž…λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ intμ—μ„œ bool둜 κ°€λŠ” isEven ν•¨μˆ˜λ₯Ό κΎΈλ―Έκ³  μ‹Άλ‹€κ³  κ°€μ •ν•΄λ΄…μ‹œλ‹€. μš°λ¦¬λŠ” 그것을 μž₯μ‹λœ ν•¨μˆ˜λ‘œ ν‘œν˜„λ˜λŠ” μ‚¬μƒμœΌλ‘œ λ°”κΏ‰λ‹ˆλ‹€. μ€‘μš”ν•œ 점은 μž₯μ‹λœ ν•¨μˆ˜κ°€ μŒμ„ λ°˜ν™˜ν•˜λ”λΌλ„ 이 사상은 μ—¬μ „νžˆ int 객체와 bool μ‚¬μ΄μ˜ ν™”μ‚΄ν‘œλ‘œ κ°„μ£Όν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

pair<bool, string> isEven(int n) {
     return make_pair(n % 2 == 0, "isEven ");
}

λ²”μ£Όμ˜ 법칙에 따라 μš°λ¦¬λŠ” 이 ν˜•νƒœλ₯Ό 객체 boolμ—μ„œ 무엇이든 κ°€λŠ” λ‹€λ₯Έ μ‚¬μƒμœΌλ‘œ ν•©μ„±ν•  수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. 특히, 이전 negate ν•¨μˆ˜λ‘œ ν•©μ„±ν•  수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.

pair<bool, string> negate(bool b) {
     return make_pair(!b, "Not so! ");
}

λΆ„λͺ…νžˆ, μš°λ¦¬λŠ” μž…λ ₯/좜λ ₯ 뢈일치 λ•Œλ¬Έμ— 이 두 νƒ€μž…μ„ 일반 ν•¨μˆ˜λ₯Ό ν•©μ„±ν•˜λŠ” 것과 같은 λ°©μ‹μœΌλ‘œ ν•©μ„±ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 합성은 μ•„λž˜μ™€ κ°™μ•„μ•Ό ν•©λ‹ˆλ‹€.

pair<bool, string> isOdd(int n) {
    pair<bool, string> p1 = isEven(n);
    pair<bool, string> p2 = negate(p1.first);
    return make_pair(p2.first, p1.second + p2.second);
}

κ·Έλž˜μ„œ μš°λ¦¬κ°€ ν•©μ„±ν•˜κ³  μžˆλŠ” 이 μƒˆλ‘œμš΄ λ²”μ£Όμ—μ„œ 두 가지 μ‚¬μƒμ˜ 합성에 λŒ€ν•œ λ ˆμ‹œν”Όκ°€ μžˆμŠ΅λ‹ˆλ‹€.

  1. 첫 번째 사상에 ν•΄λ‹Ήν•˜λŠ” 꾸며진 ν•¨μˆ˜ μ‹€ν–‰
  2. κ²°κ³Ό 쌍의 첫 번째 μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”μΆœν•˜κ³  두 번째 사상에 ν•΄λ‹Ήν•˜λŠ” 꾸며진 ν•¨μˆ˜μ— μ „λ‹¬ν•©λ‹ˆλ‹€.
  3. 첫 번째 결과의 두 번째 μ»΄ν¬λ„ŒνŠΈ(λ¬Έμžμ—΄)와 두 번째 결과의 두 번째 μ»΄ν¬λ„ŒνŠΈ(λ¬Έμžμ—΄)λ₯Ό μ—°κ²°ν•©λ‹ˆλ‹€.
  4. μ΅œμ’… 결과의 첫 번째 μ»΄ν¬λ„ŒνŠΈμ™€ μ—°κ²°λœ λ¬Έμžμ—΄μ„ κ²°ν•©ν•œ μƒˆλ‘œμš΄ μŒμ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.

이 합성을 C++μ—μ„œ κ³ μ°¨ ν•¨μˆ˜λ‘œ μΆ”μƒν™”ν•˜λ €λ©΄ λ²”μ£Όμ˜ μƒˆ 객체에 ν•΄λ‹Ήν•˜λŠ” μ„Έ 가지 νƒ€μž…μœΌλ‘œ 맀개 λ³€μˆ˜ν™”λœ ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·œμΉ™μ— 따라 ν•©μ„±ν•  수 μžˆλŠ” 두 개의 μž₯μ‹λœ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κ³  μ„Έ 번째 μž₯μ‹λœ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

template<class A, class B, class C>
function<Writer<C>(A)> compose(function<Writer<B>(A)> m1,
                               function<Writer<C>(B)> m2)
{
    return [m1, m2](A x) {
        auto p1 = m1(x);
        auto p2 = m2(p1.first);
        return make_pair(p2.first, p1.second + p2.second);
    };
}

이제 이전 μ˜ˆμ‹œλ‘œ λŒμ•„κ°€μ„œ 이 μƒˆ ν…œν”Œλ¦Ώμ„ μ΄μš©ν•΄ toUpper와 toWords의 합성을 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Writer<vector<string>> process(string s) {
   return compose<string, string, vector<string>>(toUpper, toWords)(s);
}

compose ν…œν”Œλ¦ΏμœΌλ‘œ νƒ€μž…μ„ μ „λ‹¬ν•˜λŠ” 것은 μ—¬μ „νžˆ λ³΅μž‘ν•©λ‹ˆλ‹€. μ΄λŠ” λ°˜ν™˜ νƒ€μž… 좔둠이 μžˆλŠ” μΌλ°˜ν™”λœ λžŒλ‹€ ν•¨μˆ˜λ₯Ό μ§€μ›ν•˜λŠ” C++14 ν˜Έν™˜ 컴파일러λ₯Ό μ‚¬μš©ν•˜λ©΄ ν”Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. (이 μ½”λ“œμ— λŒ€ν•œ ν¬λ ˆλ”§μ€ Eric Nieblerμ—κ²Œ μžˆμŠ΅λ‹ˆλ‹€)

auto const compose = [](auto m1, auto m2) {
    return [m1, m2](auto x) {
        auto p1 = m1(x);
        auto p2 = m2(p1.first);
        return make_pair(p2.first, p1.second + p2.second);
    };
};

이 μƒˆλ‘œμš΄ μ •μ˜μ—μ„œ process의 κ΅¬ν˜„μ€ μ•„λž˜μ™€ 같이 κ°„λ‹¨ν•΄μ§‘λ‹ˆλ‹€.

Writer<vector<string>> process(string s){
   return compose(toUpper, toWords)(s);
}

κ·ΈλŸ¬λ‚˜ 아직 λλ‚˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 우리의 μƒˆλ‘œμš΄ λ²”μ£Όμ—μ„œ 합성을 μ •μ˜ν–ˆμ§€λ§Œ, ν•­λ“± 사상은 λ¬΄μ—‡μΈκ°€μš”? 이것은 우리의 일반적인 ν•­λ“±ν•¨μˆ˜κ°€ μ•„λ‹™λ‹ˆλ‹€. 이것듀은 A νƒ€μž…μ—μ„œ Aνƒ€μž…μœΌλ‘œμ˜ 사상이어야 ν•˜λ©° μ΄λŠ” 꾸며진 ν•¨μˆ˜λ“€μ˜ ν˜•μ‹μ„ μ˜λ―Έν•©λ‹ˆλ‹€.

Writer<A> identity(A);

이것듀은 ν•©μ„±κ³Ό κ΄€λ ¨ν•˜μ—¬ unit처럼 λ™μž‘ν•΄μ•Ό ν•©λ‹ˆλ‹€. 합성에 λŒ€ν•œ μ •μ˜λ₯Ό 보면 ν•­λ“± 사상이 λ³€κ²½ 없이 인자λ₯Ό μ „λ‹¬ν•˜κ³  λ‘œκ·Έμ— 빈 λ¬Έμžμ—΄λ§Œ μ œκ³΅ν•΄μ•Ό 함을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

template<class A>
Writer<A> identity(A x) {
    return make_pair(x, "");
}

방금 μ •μ˜ν•œ λ²”μ£Όκ°€ μ‹€μ œλ‘œ κ·œμΉ™μ„ λ”°λ₯΄λŠ” λ²”μ£Όμž„μ„ μ‰½κ²Œ ν™•μ‹ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 특히, 우리의 합성은 μ‚¬μ†Œν•˜κ²Œ 결합성이 μžˆμŠ΅λ‹ˆλ‹€. 각 쌍의 첫 번째 μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ–΄λ–€ 일이 μΌμ–΄λ‚˜λŠ”μ§€ 따라가면 κ²°ν•©ν•  수 μžˆλŠ” 일반 ν•¨μˆ˜μ˜ 합성일 λΏμž…λ‹ˆλ‹€. 두 번째 μ»΄ν¬λ„ŒνŠΈκ°€ μ—°κ²°λ˜μ—ˆλ‹€λ©΄ μ—°κ²° λ˜ν•œ κ²°ν•©μž…λ‹ˆλ‹€.

μ˜ˆλ¦¬ν•œ λ…μžλŠ” 이 ꡬ쑰λ₯Ό λ¬Έμžμ—΄ λͺ¨λ…Έμ΄λ“œλΏλ§Œ μ•„λ‹ˆλΌ λͺ¨λ“  λͺ¨λ…Έμ΄λ“œλ‘œ μΌλ°˜ν™”ν•˜λŠ” 것이 μ‰½λ‹€λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. compose 내뢀에 +λŒ€μ‹  mappendλ₯Ό μ‚¬μš©ν•˜κ³  identity 내뢀에 "" λŒ€μ‹  memptyλ₯Ό μ‚¬μš©ν•  κ²ƒμž…λ‹ˆλ‹€. λ¬Έμžμ—΄λ§Œ κΈ°λ‘ν•˜λŠ” κ²ƒμœΌλ‘œ μ œν•œν•  μ΄μœ λŠ” μ—†μŠ΅λ‹ˆλ‹€. 쒋은 라이브러리 μž‘μ„±μžλŠ” λΌμ΄λΈŒλŸ¬λ¦¬κ°€ λ™μž‘ν•˜λ„λ‘ ν•˜λŠ” μ΅œμ†Œν•œμ˜ μ œμ•½ 쑰건을 식별할 수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ λ‘œκΉ… 라이브러리의 μœ μΌν•œ μš”κ΅¬ 사항은 λ‘œκ·Έμ— 단일 속성이 μžˆμ–΄μ•Ό ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

Haskellμ—μ„œμ˜ Writer

Haskell둜 μž‘μ„±ν•œ λ™μΌν•œ λ‚΄μš©μ€ 더 κ°„κ²°ν•˜λ©° μ»΄νŒŒμΌλŸ¬λ‘œλΆ€ν„° 더 λ§Žμ€ 도움을 λ°›μŠ΅λ‹ˆλ‹€. Writer νƒ€μž…μ„ μ •μ˜ν•˜λŠ” κ²ƒμœΌλ‘œ μ‹œμž‘ν•˜κ² μŠ΅λ‹ˆλ‹€.

type Writer a = (a, String)

μ—¬κΈ°μ—μ„œλŠ” C++의 typedef(λ˜λŠ” using)에 ν•΄λ‹Ήν•˜λŠ” νƒ€μž… 별칭을 μ •μ˜ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. Writer νƒ€μž…μ€ a νƒ€μž… λ³€μˆ˜λ‘œ 맀개 λ³€μˆ˜ν™”λ˜λ©° a와 String 쌍과 λ™μΌν•©λ‹ˆλ‹€. μŒμ— λŒ€ν•œ ꡬ문은 μ‰Όν‘œλ‘œ κ΅¬λΆ„λœ κ΄„ν˜Έ μ•ˆμ— 두 ν•­λͺ©λ§Œ 있으며 문법이 μ΅œμ†Œν™”λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€.

우리의 사상은 μž„μ˜μ˜ νƒ€μž…μ—μ„œ 일뢀 Writer νƒ€μž…μ— 이λ₯΄λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

a -> Writer b

μš°λ¦¬λŠ” 합성을 "λ¬Όκ³ κΈ°"라고도 ν•˜λŠ” μž¬λ―ΈμžˆλŠ” μ€‘μœ„ μ—°μ‚°μžλ‘œ μ„ μ–Έν•  κ²ƒμž…λ‹ˆλ‹€.

(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)

두 개의 인자λ₯Ό λ°›λŠ” ν•¨μˆ˜μ΄λ©°, 각각은 자체적으둜 ν•¨μˆ˜μ΄κ³  ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€. 첫 번째 μΈμžλŠ” (a->Writer b) νƒ€μž…μ΄κ³  두 번째 μΈμžλŠ” (b->Writer c)이며 κ²°κ³ΌλŠ” (a->Writer c)μž…λ‹ˆλ‹€.

이 μ€‘μœ„ μ—°μ‚°μžμ˜ μ •μ˜λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€. 두 개의 인자 m1κ³Ό m2κ°€ λ¬Όκ³ κΈ° 기호의 μ–‘μͺ½μ— ν‘œμ‹œλ©λ‹ˆλ‹€.

m1 >=> m2 = \x ->
    let (y, s1) = m1 x
        (z, s2) = m2 y
    in (z, s1 ++ s2)

κ²°κ³ΌλŠ” ν•˜λ‚˜μ˜ 인자 x의 λžŒλ‹€ ν•¨μˆ˜μž…λ‹ˆλ‹€. λžŒλ‹€λŠ” λ°± μŠ¬λž˜μ‹œλ‘œ μž‘μ„±λ©λ‹ˆλ‹€. 닀리가 μ ˆλ‹¨λœ 그리슀 문자 λ둜 μƒκ°ν•˜μ„Έμš”.

let ν‘œν˜„ 식을 μ‚¬μš©ν•˜λ©΄ 보쑰 λ³€μˆ˜λ₯Ό μ„ μ–Έν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ m1을 ν˜ΈμΆœν•œ κ²°κ³ΌλŠ” (y, s1) λ³€μˆ˜ 쌍의 νŒ¨ν„΄μ— μΌμΉ˜λ©λ‹ˆλ‹€. 첫 번째 νŒ¨ν„΄μ˜ y 인자λ₯Ό μ‚¬μš©ν•΄ m2λ₯Ό ν˜ΈμΆœν•œ κ²°κ³ΌλŠ” (z, s2)와 μΌμΉ˜ν•©λ‹ˆλ‹€.

Haskellμ—μ„œλŠ” C++μ—μ„œμ™€ 같이 μ ‘κ·Όμžλ₯Ό μ‚¬μš©ν•˜λŠ” λŒ€μ‹  일치 μŒμ„ νŒ¨ν„΄ν™”ν•˜λŠ” 것이 μΌλ°˜μ μž…λ‹ˆλ‹€. κ·Έ μ™Έμ—λŠ” 두 κ΅¬ν˜„ 사이에 맀우 직접적인 λŒ€μ‘μ΄ μžˆμŠ΅λ‹ˆλ‹€.

let ν‘œν˜„ μ‹μ˜ 전체 값은 in μ ˆμ— μ§€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ μŒμ€ 첫 번째 μ»΄ν¬λ„ŒνŠΈκ°€ z이고 두 번째 μ»΄ν¬λ„ŒνŠΈκ°€ 두 λ¬Έμžμ—΄μ˜ 연결인 s1++s2μž…λ‹ˆλ‹€.

λ˜ν•œ 우리의 범주에 λŒ€ν•œ ν•­λ“± 사상을 μ •μ˜ν•  κ²ƒμ΄μ§€λ§Œ, 훨씬 λ‚˜μ€‘μ— λͺ…ν™•ν•΄μ§ˆ 이유둜 이것을 return이라고 λΆ€λ₯Ό κ²ƒμž…λ‹ˆλ‹€.

return :: a -> Writer a
return x = (x, "")

완성을 μœ„ν•΄ upCase 및 toWords ν•¨μˆ˜μ˜ Haskell 버전을 μ‚¬μš©ν•˜κ² μŠ΅λ‹ˆλ‹€.

upCase :: String -> Writer String
upCase s = (map toUpper s, "upCase ")
toWords :: String -> Writer [String]
toWords s = (words s, "toWords ")

map ν•¨μˆ˜λŠ” C++의 transform에 ν•΄λ‹Ήν•©λ‹ˆλ‹€. λ¬Έμžμ—΄ s에 문자 ν•¨μˆ˜ toUpperλ₯Ό μ μš©ν•©λ‹ˆλ‹€. 보쑰 κΈ°λŠ₯ wordsλŠ” ν‘œμ€€ Prelude λΌμ΄λΈŒλŸ¬λ¦¬μ— μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ 두 ν•¨μˆ˜μ˜ 합성은 λ¬Όκ³ κΈ° μ—°μ‚°μžμ˜ λ„μ›€μœΌλ‘œ μˆ˜ν–‰λ©λ‹ˆλ‹€.

process :: String -> Writer [String]
process = upCase >=> toWords

Kleisli λ²”μ£Ό

μ—¬λŸ¬λΆ„λ“€μ€ μ œκ°€ κ·Έ μžλ¦¬μ—μ„œ 이 λ²”μ£Όλ₯Ό 발λͺ…ν•˜μ§€ μ•Šμ•˜λ‹€κ³  μ§μž‘ν–ˆμ„ κ²ƒμž…λ‹ˆλ‹€. 이것은 λͺ¨λ‚˜λ“œλ₯Ό 기반으둜 ν•˜λŠ” 범주인 Kleisli λ²”μ£Όμ˜ μ˜ˆμ‹œμž…λ‹ˆλ‹€. μš°λ¦¬λŠ” 아직 λͺ¨λ‚˜λ“œμ— λŒ€ν•΄ λ…Όμ˜ν•  μ€€λΉ„κ°€ λ˜μ§€ μ•Šμ•˜μ§€λ§Œ, λͺ¨λ‚˜λ“œκ°€ 무엇을 ν•  수 μžˆλŠ”μ§€ 맛보고 μ‹Άμ—ˆμŠ΅λ‹ˆλ‹€. μ œν•œλœ λͺ©μ μ„ μœ„ν•΄ Kleisli λ²”μ£Όμ—λŠ” κΈ°λ³Έ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄μ˜ νƒ€μž…μ΄ 객체둜 μžˆμŠ΅λ‹ˆλ‹€. νƒ€μž… Aμ—μ„œ νƒ€μž… B둜의 사상은 νŠΉμ • κΎΈλ°ˆμ„ μ‚¬μš©ν•΄ Aμ—μ„œ B둜 νŒŒμƒλœ νƒ€μž…μœΌλ‘œ μ΄λ™ν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€. 각 Kleisli λ²”μ£ΌλŠ” 그런 μ‚¬μƒμ˜ 자체적인 ν•©μ„±κ³Ό ν•΄λ‹Ή 합성에 λŒ€ν•œ ν•­λ“± 사상을 μ •μ˜ν•©λ‹ˆλ‹€. (λ‚˜μ€‘μ— "꾸밈"μ΄λΌλŠ” λΆ€μ •ν™•ν•œ μš©μ–΄κ°€ λ²”μ£Όμ˜ μ—”λ„νŽ‘ν„°(endofunctor) κ°œλ…μ— 해당함을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€)

이 κΈ€μ—μ„œ λ²”μ£Όμ˜ 기초둜 μ‚¬μš©ν•œ νŠΉμ • λͺ¨λ‚˜λ“œλ₯Ό Writer λͺ¨λ‚˜λ“œλΌκ³  ν•˜λ©° ν•¨μˆ˜μ˜ μ‹€ν–‰κ³Ό 기둝을 μΆ”μ ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. λ˜ν•œ 순수 계산에 μ΄νŽ™νŠΈλ₯Ό ν¬ν•¨ν•˜κΈ° μœ„ν•œ 보닀 일반적인 λ°©λ²•μ˜ μ˜ˆμ‹œμ΄κΈ°λ„ ν•©λ‹ˆλ‹€. 이전에 bottom을 λ¬΄μ‹œν•˜λ©΄ 집합 λ²”μ£Όμ—μ„œ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄ νƒ€μž…κ³Ό ν•¨μˆ˜λ₯Ό λͺ¨λΈλ§ν•  수 μžˆμŒμ„ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ—μ„œ μš°λ¦¬λŠ” 이 λͺ¨λΈμ„ μ•½κ°„ λ‹€λ₯Έ λ²”μ£Όλ‘œ ν™•μž₯ν–ˆμŠ΅λ‹ˆλ‹€. 이 λ²”μ£ΌλŠ” 사상이 꾸며진 ν•¨μˆ˜λ‘œ ν‘œν˜„λ˜κ³  κ·Έ 합성은 ν•œ ν•¨μˆ˜μ˜ 좜λ ₯을 λ‹€λ₯Έ ν•¨μˆ˜μ˜ μž…λ ₯으둜 μ „λ‹¬ν•˜λŠ” 것 이상을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. μš°λ¦¬λŠ” ν•œ 가지 더 자유둭게 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ°”λ‘œ ν•©μ„± μžμ²΄μž…λ‹ˆλ‹€. 이것은 λͺ…λ Ήν˜• μ–Έμ–΄μ—μ„œ μ „ν†΅μ μœΌλ‘œ λΆ€μž‘μš©μ„ μ‚¬μš©ν•˜μ—¬ κ΅¬ν˜„λ˜λŠ” ν”„λ‘œκ·Έλž¨μ— λ‹¨μˆœν•œ ν‘œκΈ° μ˜λ―Έλ‘ μ„ λΆ€μ—¬ν•˜λŠ” 것을 κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” μ •ν™•ν•œ μžμœ λ„μž…λ‹ˆλ‹€.

도전

인자의 κ°€λŠ₯ν•œ λͺ¨λ“  값에 λŒ€ν•΄ μ •μ˜λ˜μ§€ μ•Šμ€ ν•¨μˆ˜λ₯Ό λΆ€λΆ„ ν•¨μˆ˜λΌκ³  ν•©λ‹ˆλ‹€. μˆ˜ν•™μ  의미의 ν•¨μˆ˜κ°€ μ•„λ‹ˆλ―€λ‘œ ν‘œμ€€ λ²”μ£Όν˜• 틀에 λ§žμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ optional둜 꾸며진 νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λ‘œ λ‚˜νƒ€λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

template<class A> class optional {
    bool _isValid;
    A    _value;
public:
    optional()    : _isValid(false) {}
    optional(A v) : _isValid(true), _value(v) {}
    bool isValid() const { return _isValid; }
    A value() const { return _value; }
};

예λ₯Ό λ“€μ–΄, μ•„λž˜λŠ” μž₯μ‹λœ ν•¨μˆ˜ safe_root의 κ΅¬ν˜„μž…λ‹ˆλ‹€.

optional<double> safe_root(double x) {
    if (x >= 0) return optional<double>{sqrt(x)};
    else return optional<double>{};
}

도전할 것듀은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  1. λΆ€λΆ„ ν•¨μˆ˜μ— λŒ€ν•œ Kleisli λ²”μ£Όλ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€. (ν•©μ„± 및 ν•­λ“± μ •μ˜)
  2. μΈμžκ°€ 0κ³Ό λ‹€λ₯Έ 경우 인자의 μœ νš¨ν•œ μ—­μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” 꾸며진 ν•¨μˆ˜ safe_reciprocal을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.
  3. safe_root와 safe_reciprocal을 ν•©μ„±ν•˜μ—¬ κ°€λŠ₯ν•  λ•Œλ§ˆλ‹€ sqrt(1/x)λ₯Ό κ³„μ‚°ν•˜λŠ” safe_root_reciprocal을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

κ°μ‚¬μ˜ 말

μ΄ˆμ•ˆμ„ 읽고 C++14의 κ³ κΈ‰ κΈ°λŠ₯을 μ‚¬μš©ν•˜μ—¬ νƒ€μž… 좔둠을 μœ λ„ν•˜λŠ” 더 쒋은 compose의 κ΅¬ν˜„μ„ μ œκ³΅ν•œ Eric Nieblerμ—κ²Œ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€. νƒ€μž… νŠΉμ„±(type traits)을 μ‚¬μš©ν•˜μ—¬ λ™μΌν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ꡬ식 ν…œν”Œλ¦Ώ λ§ˆλ²•μ„ μ „μ²΄μ μœΌλ‘œ μž˜λΌλ‚Ό 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. λͺ‡ 가지 μ€‘μš”ν•œ 사항을 λͺ…ν™•νžˆ ν•˜λŠ” 데 도움이 λ˜λŠ” μœ μš©ν•œ μ˜κ²¬μ„ μ£Όμ‹  Gershom Bazermanμ—κ²Œλ„ κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€.

β¬… λ’€λ‘œκ°€κΈ° / λ‹€μŒμœΌλ‘œ ➑

Translated by @Minsu Kim✌