← Back to Python

All Topics

Advertisement

Learn/Python/Python Advanced

Monads in Python

Topic: Functional Programming

Advertisement

Introduction

Monads are a functional programming pattern that provides a way to handle computations with context. Python can implement common monads like Maybe, Either, and Option.

Maybe Monad (Optional)

from typing import TypeVar, Generic, Callable

T = TypeVar("T")
U = TypeVar("U")

class Maybe(Generic[T]):
    def __init__(self, value):
        self._value = value
        self._is_just = value is not None
    
    @staticmethod
    def just(value):
        return Maybe(value)
    
    @staticmethod
    def nothing():
        return Maybe(None)
    
    def bind(self, func: Callable[[T], "Maybe[U]"]) -> "Maybe[U]":
        if self._is_just:
            return func(self._value)
        return Maybe.nothing()
    
    def get_or_else(self, default):
        return self._value if self._is_just else default

def safe_divide(a, b):
    if b == 0:
        return Maybe.nothing()
    return Maybe.just(a / b)

result = Maybe.just(10).bind(lambda x: safe_divide(x, 2))
print(result.get_or_else(0))  # 5.0

Either Monad

class Either(Generic[T, U]):
    def __init__(self, value, is_right=True):
        self._value = value
        self._is_right = is_right
    
    @staticmethod
    def right(value):
        return Either(value, True)
    
    @staticmethod
    def left(value):
        return Either(value, False)
    
    def bind(self, func):
        if self._is_right:
            return func(self._value)
        return self
    
    def get_right(self):
        return self._value if self._is_right else None

def divide(a, b):
    if b == 0:
        return Either.left("Division by zero")
    return Either.right(a / b)

result = Either.right(10).bind(lambda x: divide(x, 2))
print(result.get_right())  # 5.0

Option Type Pattern

from typing import Optional

def find_user(id: int) -> Optional[dict]:
    if id in users:
        return users[id]
    return None

def get_email(user: dict) -> Optional[str]:
    return user.get("email")

# Using Optional chaining pattern
email = (find_user(1)
    .map(get_email)
    .or_else("no email"))

Result Monad

from dataclasses import dataclass

@dataclass
class Result:
    value: any
    error: str = None
    
    @staticmethod
    def success(value):
        return Result(value, None)
    
    @staticmethod
    def failure(error):
        return Result(None, error)
    
    def bind(self, func):
        if self.error:
            return self
        return func(self.value)

Practice Problems

  1. Implement Maybe monad with map
  2. Create Either for error handling
  3. Implement Result pattern for operations
  4. Build Option type with flatmap
  5. Chain multiple monadic operations

Advertisement

Advertisement

Need More Practice?

Get personalized Python help from ChatWhole's AI-powered platform.

Get Expert Help →