ReaderT
funstruct.experimental.monadtransformer.reader_t
ReaderT — reader monad transformer over any monad.
ReaderT — shared environment + inner monad's effects (failure, state, etc.)
ReaderT[F, Ctx, A] = Ctx -> F[A]
bind: chain computations that share context, any can fail. and_then: pipe output forward as the next context (Kleisli composition).
Examples:
>>> from funstruct.experimental.monadtransformer import ReaderT
>>> from funstruct.monad.either import Either, Right, Left
bind — shared context, with failure:
>>> get_db = ReaderT(lambda cfg: (
... Right(cfg["db"]) if "db" in cfg
... else Left("missing db")))
>>> validate = lambda url: ReaderT(lambda cfg: (
... Right(url) if url.startswith("postgres://")
... else Left(f"bad url: {url}")))
>>> connect = lambda url: ReaderT(lambda cfg: (
... Right(f"{url} as {cfg['user']}")))
>>> pipeline = (
... get_db
... .bind(validate)
... .bind(connect)
... )
>>> pipeline.run({"db": "postgres://localhost/app", "user": "admin"})
Right('postgres://localhost/app as admin')
>>> pipeline.run({"db": "mysql://bad", "user": "admin"})
Left('bad url: mysql://bad')
and_then — output feeds as next input, short-circuits on failure:
>>> parse_int = ReaderT(lambda s: (
... Right(int(s)) if s.isdigit()
... else Left(f"not a number: {s}")))
>>> double = ReaderT(lambda n: Right(n * 2))
>>> to_str = ReaderT(lambda n: Right(str(n)))
>>> pipeline = (
... parse_int
... .and_then(double)
... .and_then(to_str)
... )
>>> pipeline.run("21")
Right('42')
>>> pipeline.run("abc")
Left('not a number: abc')
ReaderT
Bases: MonadTransformer, Generic[_Ctx, _M, _A]
ReaderT: Ctx -> M[A].
M is the inner monad (StateT, Result, etc.). Composition delegates to M's bind/map/lash — ReaderT just threads the context to both sides.
Source code in funstruct/experimental/monadtransformer/reader_t.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | |
run(ctx)
Apply context, returning the inner M[A].
Source code in funstruct/experimental/monadtransformer/reader_t.py
83 84 85 | |
__call__(ctx)
Apply context (alias for run).
Source code in funstruct/experimental/monadtransformer/reader_t.py
87 88 89 | |
bind(f)
FlatMap: compose via M's bind, threading ctx.
Source code in funstruct/experimental/monadtransformer/reader_t.py
91 92 93 94 95 96 97 98 99 100 | |
map(f)
Transform value via M's map.
Source code in funstruct/experimental/monadtransformer/reader_t.py
102 103 104 105 106 107 108 | |
handle_error_with(f)
Recover from failure via inner monad's handle_error_with.
Source code in funstruct/experimental/monadtransformer/reader_t.py
110 111 112 113 114 115 116 | |
local(f)
Transform the environment before running.
Cats: Kleisli.local
from funstruct.monad.either import Right r = ReaderT(lambda ctx: Right(ctx["name"])) r.local(lambda outer: {"name": outer}).run("Alice") Right('Alice')
Source code in funstruct/experimental/monadtransformer/reader_t.py
118 119 120 121 122 123 124 125 126 127 128 | |
ask(monad)
classmethod
Get the environment as the value.
Cats: Kleisli.ask
from funstruct.monad.either import Either, Right ReaderT.ask(Either).run(42) Right(42)
Source code in funstruct/experimental/monadtransformer/reader_t.py
130 131 132 133 134 135 136 137 138 139 140 | |
and_then(other)
Kleisli composition: output of self becomes input (ctx) of other.
Short-circuits on inner monad failure.
Source code in funstruct/experimental/monadtransformer/reader_t.py
144 145 146 147 148 149 150 151 | |
do(gen_fn)
classmethod
Do-notation via generators. Returns a callable.
Each yield extracts the value from a ReaderT (shared ctx).
Short-circuits on inner monad failure.
from funstruct.monad.either import Right def pipeline(): ... x = yield ReaderT(lambda ctx: Right(ctx)) ... y = yield ReaderT(lambda ctx: Right(x + ctx)) ... return y ReaderT.do(pipeline)().run(5) Right(10)
Source code in funstruct/experimental/monadtransformer/reader_t.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
pure(value, monad)
classmethod
Lift a plain value into ReaderT via monad.pure.
Source code in funstruct/experimental/monadtransformer/reader_t.py
190 191 192 193 | |
from_reader(reader_fn, monad)
classmethod
Lift a pure Reader (Ctx → A) into ReaderT.
Use when you have the inner reader function but not the outer monad.
from funstruct.monad.either import Either, Right ReaderT.from_reader(lambda ctx: ctx["name"], Either).run({"name": "Alice"}) Right('Alice')
Source code in funstruct/experimental/monadtransformer/reader_t.py
195 196 197 198 199 200 201 202 203 204 205 | |
lift_f(m)
classmethod
Lift M[A] into ReaderT (ignoring context).
Haskell equivalent: lift :: m a -> ReaderT r m a
Source code in funstruct/experimental/monadtransformer/reader_t.py
207 208 209 210 211 212 213 | |