Coverage for /home/ubuntu/yoneda/python/yoneda/basic.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-08 16:26 +0000

1from typing import Callable, Generic, TypeVar # noqa: F401 

2 

3import math 

4 

5import yoneda.monad as ym 

6from yoneda.monad import Monad 

7 

8A = TypeVar('A') 

9B = TypeVar('B') 

10C = TypeVar('C') 

11# ------------------------------------------------------------------------------ 

12 

13 

14class Maybe(Monad, Generic[A]): 

15 @classmethod 

16 def just(cls, value): 

17 # type: (A) -> Maybe[A] 

18 ''' 

19 Just constructor for Maybe class. 

20 

21 Args: 

22 value (object): Non-null value. 

23 

24 Returns: 

25 Maybe: Maybe monad of value. 

26 ''' 

27 return cls(value) 

28 

29 @classmethod 

30 def nothing(cls): 

31 # type: () -> Maybe 

32 ''' 

33 Nothing constructor for Maybe class. 

34 

35 Returns: 

36 Maybe: Nothing monad. 

37 ''' 

38 return cls(None) 

39 

40 def __repr__(self): 

41 # type: () -> str 

42 '''String representation of monad.''' 

43 if self.state == 'just': 

44 return f'Just({self._data})' 

45 return 'Nothing' 

46 

47 @property 

48 def state(self): 

49 # type: () -> str 

50 '''State of monad. Either just or nothing.''' 

51 data = self._data 

52 if data is None or math.isnan(data): 

53 return 'nothing' 

54 return 'just' 

55# ------------------------------------------------------------------------------ 

56 

57 

58class Try(Monad, Generic[A]): 

59 @classmethod 

60 def success(cls, value): 

61 # type: (A) -> Try[A] 

62 ''' 

63 Success constructor for Try class. 

64 

65 Args: 

66 value (object): Non-error value. 

67 

68 Returns: 

69 Maybe: Try monad of value. 

70 ''' 

71 return ym.succeed(cls, value) 

72 

73 @classmethod 

74 def failure(cls, error): 

75 # type: (Exception) -> Try[Exception] 

76 ''' 

77 Success constructor for Try class. 

78 

79 Args: 

80 error (Exception): Error. 

81 

82 Returns: 

83 Maybe: Try monad of error. 

84 ''' 

85 return ym.fail(cls, error) 

86 

87 def __repr__(self): 

88 # type: () -> str 

89 '''String representation of monad.''' 

90 return f'{self.state.capitalize()}({self._data})' 

91 

92 @property 

93 def state(self): 

94 # type: () -> str 

95 '''State of monad. Either success or failure.''' 

96 if isinstance(self._data, Exception): 

97 return 'failure' 

98 return 'success' 

99 

100 def fmap(self, func): 

101 # type: (Callable[[A], B]) -> Try[B | Exception] 

102 ''' 

103 Functor map: (A -> B) -> MB 

104 

105 Given a function A to B, return a Monad of B (MB). 

106 Example: m.fmap(lambda x: x + 2) 

107 

108 Args: 

109 func (function): Function (A -> B). 

110 

111 Returns: 

112 Try[B]: Try Monad of B. 

113 ''' 

114 try: 

115 return super().fmap(func) # type: ignore 

116 except Exception as error: 

117 return self.fail(error) 

118 

119 def bind(self, func): 

120 # type: (Callable[[A], Monad[B]]) -> Try[B | Exception] 

121 ''' 

122 Bind: (A -> MB) -> MB 

123 

124 Given a function A to MB, return a Monad of B (MB). 

125 

126 Args: 

127 func (function): Function (A -> MB). 

128 

129 Returns: 

130 Try[B]: Try Monad of B. 

131 ''' 

132 try: 

133 return super().bind(func) # type: ignore 

134 except Exception as error: 

135 return self.fail(error) 

136 

137 def app(self, monad_func): 

138 # type: (Monad[Callable[[A], B]]) -> Try[B | Exception] 

139 ''' 

140 Applicative: M(A -> B) -> MB 

141 

142 Given a Monad of a function A to B, return a Monad of B (MB). 

143 

144 Args: 

145 monad_func (Monad): Monad of function (A -> B). 

146 

147 Returns: 

148 Try[B]: Try Monad of B. 

149 ''' 

150 try: 

151 return super().app(monad_func) # type: ignore 

152 except Exception as error: 

153 return self.fail(error)