ABOUT ME

-

Total
-
  • 파이썬: Monad (모나드)
    컴퓨터/파이썬 2020. 11. 11. 14:34
    728x90
    반응형

    Monad

    (단자)

     

    Monad (functional programming) - Wikipedia

    Design pattern in functional programming to build generic types In functional programming, a monad is an abstraction that allows structuring programs generically.

    en.wikipedia.org

     

    Monad란

    """ Monad 예제 """
    class List:
        # ...monad...
    
    a = List([1.1, 2.6, 3.5])
    # a.data = [1.1, 2.6, 3.5]
    
    b = a | str | (lambda f: f.zfill(5))
    # b.data = ['001.1', '002.6', '003.5']

    functional programming constructs that capture various notions of computation

    (계산의 다양한 개념, 의향(notion)을 잡아주는 함수형 프로그래밍 구성 방식)

     

    Monad는 함수형 프로그래밍의 중요한 개념 중 하나로, 계산의 다양한 개념과 의도를 담는 구조이다.

    이를 이해하기 위해, 먼저 함수형 프로그래밍의 기본 개념인 함수 합성에 대해 알아보면

    함수형 프로그래밍에서는 여러 함수를 결합하여 프로그램을 구성한다.

    예를 들어, f(g(x))는 함수 g의 결과를 함수 f에 전달하는 구조이고, 간단한 예시:

    from operator import neg
    
    y = str(neg(int(x)))

     

    위와 같은 함수가 있으면,

    1. 문자열을 받는다.
    2. 문자열을 정수형으로 변환한다.
    3. 변환된 값을 음수로 만든다.
    4. 다시 문자열로 변환한다.

    ex) 입력이 "3"이면 결과는 "-3"이 된다.

     

    하지만, "XYZ"를 받으면, int형으로 바꿀 때 오류가 발생한다.

    ValueError: invalid literal for int() with base 10: 'XYZ'

    str, int 함수가 알아서 값을 처리하면 좋겠지만,

    이런 상황에서 Monad의 역할이 중요해진다. 데이터를 처리하고, 다른 함수들과 쉽게 결합할 수 있도록 해주는 패턴이다.

    즉, 복잡한 함수 과정에서 발생할 수 있는 오류나 예외 상황들을 보다 멋지게 다루도록 해준다.

    (컨베이어 벨트 시스템을 떠올려보자.)

     

    1. Failure monad

    Design pattern: string이 int로 변환이 안되면, None을 return

    class Failure:
        def __init__(self, data, failed=False):
            self.data = data
            self.failed = failed
    
        def __str__(self):
            return " ".join([str(self.data), str(self.failed)])
    
        def __or__(self, f):  # |
            return self.bind(f)
    
        def bind(self, f):
            if self.failed:
                return self
            try:
                x = f(self.data)
                return Failure(x)
            except:
                return Failure(None, failed=True)
    
    
    if __name__ == "__main__":
        from operator import neg
    
        # string을 int형으로 바꾸고, 오류 없으면 음수 변환 후, string으로 return
        
        x = "1"
        y = Failure(x) | int | neg | str
        print(y.data)  # "-1"
    
        x = "XYZ"
        y = Failure(x) | int | neg | str
        print(y.data)  # None
    

     

    2. List monad

    아무 타입이나 담겨있는 list를 담는 List monad

    class ListMonad:
        def __init__(self, items):
            self.items = items
    
        def bind(self, function):
            # bind 함수는 리스트의 각 요소에 함수를 적용합니다.
            return ListMonad([function(item) for item in self.items])
    
        def __str__(self):
            # 이 클래스의 문자열 표현을 정의합니다.
            return "ListMonad(" + ", ".join(map(str, self.items)) + ")"
    
        def __or__(self, function):
            # 파이프 연산자(|)를 bind 함수와 동일하게 사용합니다.
            return self.bind(function)
    
    
    if __name__ == "__main__":
        numbers = ListMonad([1.1, 2.6, 3.5])
    
        # 각 숫자를 문자열로 변환한 다음, zfill을 사용하여 5자리로 만듭니다.
        string_numbers = numbers | str | (lambda x: x.zfill(5))
        print(string_numbers)  # ListMonad(001.1, 002.6, 003.5)
    
        # 각 숫자를 반올림합니다.
        rounded_numbers = numbers | float | round
        print(rounded_numbers)  # ListMonad(1, 3, 4)
    1. map 함수로 배열안 모든 요소의 타입을 바꾼다.
    2. 함수에 맞는 식으로 변환 후 list를 return.

     

    참고

    이야기로 이해하는 Functor, Monad @미디엄 (영문)

    Pyinformer monad 탐구 @링크

    728x90

    댓글