Coverage for src/monadc/try_/try_.py: 73%

55 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-19 20:24 -0700

1from typing import TypeVar, Generic, Callable, Union, Any, Optional, Type 

2 

3T = TypeVar('T') # Success type 

4U = TypeVar('U') # Result type 

5E = TypeVar('E', bound=Exception) # Exception type 

6 

7 

8class Try(Generic[T]): 

9 """ 

10 Scala inspired Try monad for representing computations that may throw exceptions. 

11 

12 Try[T] represents a computation that either: 

13 - Success[T]: A successful computation with result of type T 

14 - Failure: A failed computation that caught an exception 

15 

16 Try automatically catches exceptions and wraps them in Failure. 

17 """ 

18 

19 def __new__(cls, func: Callable[[], T]) -> 'Try[T]': 

20 """Factory constructor that executes function and catches exceptions.""" 

21 if cls is not Try: 

22 # Direct subclass instantiation (Success, Failure) 

23 return super().__new__(cls) 

24 

25 try: 

26 result = func() 

27 from .success import Success 

28 return Success(result) 

29 except Exception as e: 

30 from .failure import Failure 

31 return Failure(e) 

32 

33 @classmethod 

34 def of_value(cls, value: T) -> 'Try[T]': 

35 """Create a Success directly from a value without function execution.""" 

36 from .success import Success 

37 return Success(value) 

38 

39 # Type checking methods 

40 def is_success(self) -> bool: 

41 """Returns True if this Try is a Success, False otherwise.""" 

42 raise NotImplementedError("Use Success or Failure, not Try directly") 

43 

44 def is_failure(self) -> bool: 

45 """Returns True if this Try is a Failure, False otherwise.""" 

46 raise NotImplementedError("Use Success or Failure, not Try directly") 

47 

48 # Value extraction 

49 def get(self) -> T: 

50 """Get the value. Raises the original exception if this is a Failure.""" 

51 raise NotImplementedError("Use Success or Failure, not Try directly") 

52 

53 def get_or_else(self, default: Union[T, Callable[[], T]]) -> T: 

54 """Get the value or return default if this is a Failure.""" 

55 raise NotImplementedError("Use Success or Failure, not Try directly") 

56 

57 # Exception access 

58 def exception(self) -> Optional[Exception]: 

59 """Get the exception if this is a Failure, None otherwise.""" 

60 raise NotImplementedError("Use Success or Failure, not Try directly") 

61 

62 # Transformations 

63 def map(self, func: Callable[[T], U]) -> 'Try[U]': 

64 """Transform the Success value if present, otherwise return unchanged Failure.""" 

65 raise NotImplementedError("Use Success or Failure, not Try directly") 

66 

67 def flat_map(self, func: Callable[[T], 'Try[U]']) -> 'Try[U]': 

68 """Transform Success value to Try if present, otherwise return unchanged Failure.""" 

69 raise NotImplementedError("Use Success or Failure, not Try directly") 

70 

71 def filter(self, predicate: Callable[[T], bool]) -> 'Try[T]': 

72 """Keep Success if predicate matches, otherwise return Failure.""" 

73 raise NotImplementedError("Use Success or Failure, not Try directly") 

74 

75 # Recovery operations 

76 def recover(self, func: Callable[[Exception], T]) -> 'Try[T]': 

77 """Recover from Failure by applying function to exception.""" 

78 raise NotImplementedError("Use Success or Failure, not Try directly") 

79 

80 def recover_with(self, func: Callable[[Exception], 'Try[T]']) -> 'Try[T]': 

81 """Recover from Failure by applying function that returns Try.""" 

82 raise NotImplementedError("Use Success or Failure, not Try directly") 

83 

84 def or_else(self, alternative: Union['Try[T]', Callable[[], 'Try[T]']]) -> 'Try[T]': 

85 """Return self if Success, otherwise return alternative Try.""" 

86 raise NotImplementedError("Use Success or Failure, not Try directly") 

87 

88 def flatten(self) -> 'Try[Any]': 

89 """Flatten nested Try[Try[T]] to Try[T]. Raises TypeError if not nested.""" 

90 raise NotImplementedError("Use Success or Failure, not Try directly") 

91 

92 def flatten_safe(self) -> 'Try[Any]': 

93 """Safe flatten that returns self if not nested (idempotent behavior).""" 

94 raise NotImplementedError("Use Success or Failure, not Try directly") 

95 

96 # Folding 

97 def fold(self, if_failure: Callable[[Exception], U], if_success: Callable[[T], U]) -> U: 

98 """Apply if_failure to exception or if_success to value.""" 

99 raise NotImplementedError("Use Success or Failure, not Try directly") 

100 

101 def transform(self, success_func: Callable[[T], 'Try[U]'], 

102 failure_func: Callable[[Exception], 'Try[U]']) -> 'Try[U]': 

103 """Transform both Success and Failure cases.""" 

104 raise NotImplementedError("Use Success or Failure, not Try directly") 

105 

106 # Side effects 

107 def foreach(self, func: Callable[[T], Any]) -> None: 

108 """Execute function on Success value, do nothing for Failure.""" 

109 raise NotImplementedError("Use Success or Failure, not Try directly") 

110 

111 # Conversions 

112 def to_option(self) -> Any: # Returns Option[T] 

113 """Convert Try to Option, losing exception information.""" 

114 raise NotImplementedError("Use Success or Failure, not Try directly") 

115 

116 def to_either(self) -> Any: # Returns Either[Exception, T] 

117 """Convert Try to Either with exception as Left value.""" 

118 raise NotImplementedError("Use Success or Failure, not Try directly")