Algebraic Effects System for Rust

refactor(has): make Has::get consuming, update all impls, macros, and tests 🦀 (#16)

BREAKING CHANGE: The Has trait now requires fn get(self) -> T, replacing the previous reference-based API. All manual and macro-generated Has impls, as well as all test and usage code, have been updated to match. This enables more ergonomic value extraction and aligns with strict TDD and incremental refactoring rules. Lens API and macro crates were also updated for compatibility. Future work: consider ergonomic helpers for reference extraction if needed, and audit downstream crates for usage patterns.

authored by oeiuwq.com and committed by

GitHub 9e87770a c095f9e6

+144 -144
+8 -8
crates/do_macro/tests/basic.rs
··· 67 67 } 68 68 69 69 impl Has<A> for Ctx { 70 - fn get(&self) -> &A { 71 - &self.a 70 + fn get(self) -> A { 71 + self.a 72 72 } 73 73 } 74 74 impl Has<B> for Ctx { 75 - fn get(&self) -> &B { 76 - &self.b 75 + fn get(self) -> B { 76 + self.b 77 77 } 78 78 } 79 79 ··· 89 89 } 90 90 91 91 impl Has<A> for (A, B) { 92 - fn get(&self) -> &A { 93 - &self.0 92 + fn get(self) -> A { 93 + self.0 94 94 } 95 95 } 96 96 impl Has<B> for (A, B) { 97 - fn get(&self) -> &B { 98 - &self.1 97 + fn get(self) -> B { 98 + self.1 99 99 } 100 100 } 101 101
+2 -2
crates/field_macro/src/lib.rs
··· 23 23 let field_ty = &field.ty; 24 24 impls.push(quote! { 25 25 impl #impl_generics ::fx::Has<#field_ty> for #name #ty_generics #where_clause { 26 - fn get(&self) -> &#field_ty { 27 - &self.#field_name 26 + fn get(self) -> #field_ty { 27 + self.#field_name 28 28 } 29 29 } 30 30 });
+2 -2
crates/field_macro/tests/struct_field.rs
··· 8 8 b: &'static str, 9 9 } 10 10 let ctx = Ctx { a: 42, b: "hello" }; 11 - assert_eq!(*<Ctx as Has<u32>>::get(&ctx), 42u32); 12 - assert_eq!(*<Ctx as Has<&'static str>>::get(&ctx), "hello"); 11 + assert_eq!(<Ctx as Has<u32>>::get(ctx.clone()), 42u32); 12 + assert_eq!(<Ctx as Has<&'static str>>::get(ctx), "hello"); 13 13 }
+1 -1
crates/fx/src/core/fx.rs
··· 146 146 X: Clone, 147 147 { 148 148 Fx::pending(move |ctx: S| { 149 - let x = Has::get(&ctx).clone(); 149 + let x = Has::get(ctx.clone()); 150 150 f(x) 151 151 }) 152 152 }
+2 -2
crates/fx/src/core/has_put.rs
··· 4 4 where 5 5 Self: DynClone, 6 6 { 7 - fn get<'f>(&'f self) -> &'f T; 7 + fn get(self) -> T; 8 8 } 9 9 10 10 impl<T: Clone> Has<T> for T { 11 - fn get<'f>(&'f self) -> &'f T { 11 + fn get(self) -> T { 12 12 self 13 13 } 14 14 }
+4 -4
crates/fx/src/core/tests/ability_test.rs
··· 103 103 n: i32, 104 104 } 105 105 impl Has<MyAbility> for Ctx { 106 - fn get<'f>(&'f self) -> &'f MyAbility { 107 - &self.ab 106 + fn get(self) -> MyAbility { 107 + self.ab 108 108 } 109 109 } 110 110 impl Put<MyAbility> for Ctx { ··· 114 114 } 115 115 } 116 116 impl Has<i32> for Ctx { 117 - fn get<'f>(&'f self) -> &'f i32 { 118 - &self.n 117 + fn get(self) -> i32 { 118 + self.n 119 119 } 120 120 } 121 121 impl Put<i32> for Ctx {
+4 -4
crates/fx/src/core/tests/forall_test.rs
··· 40 40 } 41 41 } 42 42 impl Has<i32> for S2 { 43 - fn get(&self) -> &i32 { 44 - &self.x 43 + fn get(self) -> i32 { 44 + self.x 45 45 } 46 46 } 47 47 impl Has<bool> for S2 { 48 - fn get(&self) -> &bool { 49 - &self.y 48 + fn get(self) -> bool { 49 + self.y 50 50 } 51 51 } 52 52 impl Put<i32> for S2 {
+10 -10
crates/fx/src/core/tests/fx_test.rs
··· 15 15 b: B, 16 16 } 17 17 impl Has<A> for S { 18 - fn get<'f>(&'f self) -> &'f A { 19 - &self.a 18 + fn get(self) -> A { 19 + self.a 20 20 } 21 21 } 22 22 impl Put<A> for S { ··· 26 26 } 27 27 } 28 28 impl Has<B> for S { 29 - fn get<'f>(&'f self) -> &'f B { 30 - &self.b 29 + fn get(self) -> B { 30 + self.b 31 31 } 32 32 } 33 33 impl Put<B> for S { ··· 157 157 #[derive(Clone)] 158 158 struct N(i32); 159 159 impl Has<N> for (N, ()) { 160 - fn get(&self) -> &N { 161 - &self.0 160 + fn get(self) -> N { 161 + self.0 162 162 } 163 163 } 164 164 ··· 176 176 x: i32, 177 177 } 178 178 impl Has<i32> for Ctx { 179 - fn get<'f>(&'f self) -> &'f i32 { 180 - &self.x 179 + fn get(self) -> i32 { 180 + self.x 181 181 } 182 182 } 183 183 let fx = Fx::has_pending(|x: i32| Fx::value(x * 2)); ··· 194 194 y: i32, 195 195 } 196 196 impl Has<i32> for Ctx { 197 - fn get<'f>(&'f self) -> &'f i32 { 198 - &self.x 197 + fn get(self) -> i32 { 198 + self.x 199 199 } 200 200 } 201 201 // Compose two has_pending calls
+92 -92
crates/fx/src/core/tests/get_n_test.rs
··· 8 8 b: u16, 9 9 } 10 10 impl Has<u8> for Ctx2 { 11 - fn get(&self) -> &u8 { 12 - &self.a 11 + fn get(self) -> u8 { 12 + self.a 13 13 } 14 14 } 15 15 impl Has<u16> for Ctx2 { 16 - fn get(&self) -> &u16 { 17 - &self.b 16 + fn get(self) -> u16 { 17 + self.b 18 18 } 19 19 } 20 20 ··· 25 25 c: u32, 26 26 } 27 27 impl Has<u8> for Ctx3 { 28 - fn get(&self) -> &u8 { 29 - &self.a 28 + fn get(self) -> u8 { 29 + self.a 30 30 } 31 31 } 32 32 impl Has<u16> for Ctx3 { 33 - fn get(&self) -> &u16 { 34 - &self.b 33 + fn get(self) -> u16 { 34 + self.b 35 35 } 36 36 } 37 37 impl Has<u32> for Ctx3 { 38 - fn get(&self) -> &u32 { 39 - &self.c 38 + fn get(self) -> u32 { 39 + self.c 40 40 } 41 41 } 42 42 ··· 48 48 d: u64, 49 49 } 50 50 impl Has<u8> for Ctx4 { 51 - fn get(&self) -> &u8 { 52 - &self.a 51 + fn get(self) -> u8 { 52 + self.a 53 53 } 54 54 } 55 55 impl Has<u16> for Ctx4 { 56 - fn get(&self) -> &u16 { 57 - &self.b 56 + fn get(self) -> u16 { 57 + self.b 58 58 } 59 59 } 60 60 impl Has<u32> for Ctx4 { 61 - fn get(&self) -> &u32 { 62 - &self.c 61 + fn get(self) -> u32 { 62 + self.c 63 63 } 64 64 } 65 65 impl Has<u64> for Ctx4 { 66 - fn get(&self) -> &u64 { 67 - &self.d 66 + fn get(self) -> u64 { 67 + self.d 68 68 } 69 69 } 70 70 ··· 77 77 e: usize, 78 78 } 79 79 impl Has<u8> for Ctx5 { 80 - fn get(&self) -> &u8 { 81 - &self.a 80 + fn get(self) -> u8 { 81 + self.a 82 82 } 83 83 } 84 84 impl Has<u16> for Ctx5 { 85 - fn get(&self) -> &u16 { 86 - &self.b 85 + fn get(self) -> u16 { 86 + self.b 87 87 } 88 88 } 89 89 impl Has<u32> for Ctx5 { 90 - fn get(&self) -> &u32 { 91 - &self.c 90 + fn get(self) -> u32 { 91 + self.c 92 92 } 93 93 } 94 94 impl Has<u64> for Ctx5 { 95 - fn get(&self) -> &u64 { 96 - &self.d 95 + fn get(self) -> u64 { 96 + self.d 97 97 } 98 98 } 99 99 impl Has<usize> for Ctx5 { 100 - fn get(&self) -> &usize { 101 - &self.e 100 + fn get(self) -> usize { 101 + self.e 102 102 } 103 103 } 104 104 ··· 112 112 f: i8, 113 113 } 114 114 impl Has<u8> for Ctx6 { 115 - fn get(&self) -> &u8 { 116 - &self.a 115 + fn get(self) -> u8 { 116 + self.a 117 117 } 118 118 } 119 119 impl Has<u16> for Ctx6 { 120 - fn get(&self) -> &u16 { 121 - &self.b 120 + fn get(self) -> u16 { 121 + self.b 122 122 } 123 123 } 124 124 impl Has<u32> for Ctx6 { 125 - fn get(&self) -> &u32 { 126 - &self.c 125 + fn get(self) -> u32 { 126 + self.c 127 127 } 128 128 } 129 129 impl Has<u64> for Ctx6 { 130 - fn get(&self) -> &u64 { 131 - &self.d 130 + fn get(self) -> u64 { 131 + self.d 132 132 } 133 133 } 134 134 impl Has<usize> for Ctx6 { 135 - fn get(&self) -> &usize { 136 - &self.e 135 + fn get(self) -> usize { 136 + self.e 137 137 } 138 138 } 139 139 impl Has<i8> for Ctx6 { 140 - fn get(&self) -> &i8 { 141 - &self.f 140 + fn get(self) -> i8 { 141 + self.f 142 142 } 143 143 } 144 144 ··· 153 153 g: i16, 154 154 } 155 155 impl Has<u8> for Ctx7 { 156 - fn get(&self) -> &u8 { 157 - &self.a 156 + fn get(self) -> u8 { 157 + self.a 158 158 } 159 159 } 160 160 impl Has<u16> for Ctx7 { 161 - fn get(&self) -> &u16 { 162 - &self.b 161 + fn get(self) -> u16 { 162 + self.b 163 163 } 164 164 } 165 165 impl Has<u32> for Ctx7 { 166 - fn get(&self) -> &u32 { 167 - &self.c 166 + fn get(self) -> u32 { 167 + self.c 168 168 } 169 169 } 170 170 impl Has<u64> for Ctx7 { 171 - fn get(&self) -> &u64 { 172 - &self.d 171 + fn get(self) -> u64 { 172 + self.d 173 173 } 174 174 } 175 175 impl Has<usize> for Ctx7 { 176 - fn get(&self) -> &usize { 177 - &self.e 176 + fn get(self) -> usize { 177 + self.e 178 178 } 179 179 } 180 180 impl Has<i8> for Ctx7 { 181 - fn get(&self) -> &i8 { 182 - &self.f 181 + fn get(self) -> i8 { 182 + self.f 183 183 } 184 184 } 185 185 impl Has<i16> for Ctx7 { 186 - fn get(&self) -> &i16 { 187 - &self.g 186 + fn get(self) -> i16 { 187 + self.g 188 188 } 189 189 } 190 190 ··· 200 200 h: i32, 201 201 } 202 202 impl Has<u8> for Ctx8 { 203 - fn get(&self) -> &u8 { 204 - &self.a 203 + fn get(self) -> u8 { 204 + self.a 205 205 } 206 206 } 207 207 impl Has<u16> for Ctx8 { 208 - fn get(&self) -> &u16 { 209 - &self.b 208 + fn get(self) -> u16 { 209 + self.b 210 210 } 211 211 } 212 212 impl Has<u32> for Ctx8 { 213 - fn get(&self) -> &u32 { 214 - &self.c 213 + fn get(self) -> u32 { 214 + self.c 215 215 } 216 216 } 217 217 impl Has<u64> for Ctx8 { 218 - fn get(&self) -> &u64 { 219 - &self.d 218 + fn get(self) -> u64 { 219 + self.d 220 220 } 221 221 } 222 222 impl Has<usize> for Ctx8 { 223 - fn get(&self) -> &usize { 224 - &self.e 223 + fn get(self) -> usize { 224 + self.e 225 225 } 226 226 } 227 227 impl Has<i8> for Ctx8 { 228 - fn get(&self) -> &i8 { 229 - &self.f 228 + fn get(self) -> i8 { 229 + self.f 230 230 } 231 231 } 232 232 impl Has<i16> for Ctx8 { 233 - fn get(&self) -> &i16 { 234 - &self.g 233 + fn get(self) -> i16 { 234 + self.g 235 235 } 236 236 } 237 237 impl Has<i32> for Ctx8 { 238 - fn get(&self) -> &i32 { 239 - &self.h 238 + fn get(self) -> i32 { 239 + self.h 240 240 } 241 241 } 242 242 ··· 253 253 i: i64, 254 254 } 255 255 impl Has<u8> for Ctx9 { 256 - fn get(&self) -> &u8 { 257 - &self.a 256 + fn get(self) -> u8 { 257 + self.a 258 258 } 259 259 } 260 260 impl Has<u16> for Ctx9 { 261 - fn get(&self) -> &u16 { 262 - &self.b 261 + fn get(self) -> u16 { 262 + self.b 263 263 } 264 264 } 265 265 impl Has<u32> for Ctx9 { 266 - fn get(&self) -> &u32 { 267 - &self.c 266 + fn get(self) -> u32 { 267 + self.c 268 268 } 269 269 } 270 270 impl Has<u64> for Ctx9 { 271 - fn get(&self) -> &u64 { 272 - &self.d 271 + fn get(self) -> u64 { 272 + self.d 273 273 } 274 274 } 275 275 impl Has<usize> for Ctx9 { 276 - fn get(&self) -> &usize { 277 - &self.e 276 + fn get(self) -> usize { 277 + self.e 278 278 } 279 279 } 280 280 impl Has<i8> for Ctx9 { 281 - fn get(&self) -> &i8 { 282 - &self.f 281 + fn get(self) -> i8 { 282 + self.f 283 283 } 284 284 } 285 285 impl Has<i16> for Ctx9 { 286 - fn get(&self) -> &i16 { 287 - &self.g 286 + fn get(self) -> i16 { 287 + self.g 288 288 } 289 289 } 290 290 impl Has<i32> for Ctx9 { 291 - fn get(&self) -> &i32 { 292 - &self.h 291 + fn get(self) -> i32 { 292 + self.h 293 293 } 294 294 } 295 295 impl Has<i64> for Ctx9 { 296 - fn get(&self) -> &i64 { 297 - &self.i 296 + fn get(self) -> i64 { 297 + self.i 298 298 } 299 299 } 300 300 ··· 310 310 bar: Bar, 311 311 } 312 312 impl Has<Foo> for Ctx { 313 - fn get(&self) -> &Foo { 314 - &self.foo 313 + fn get(self) -> Foo { 314 + self.foo 315 315 } 316 316 } 317 317 impl Has<Bar> for Ctx { 318 - fn get(&self) -> &Bar { 319 - &self.bar 318 + fn get(self) -> Bar { 319 + self.bar 320 320 } 321 321 } 322 322 let ctx = Ctx {
+2 -2
crates/fx/src/core/tests/has_put_test.rs
··· 3 3 #[test] 4 4 fn has_and_put_for_u32() { 5 5 let x: u32 = 7; 6 - assert_eq!(*x.get(), 7); 6 + assert_eq!(x.get(), 7); 7 7 let y = x.put(42); 8 8 assert_eq!(y, 42); 9 9 } ··· 11 11 #[test] 12 12 fn has_and_put_for_string() { 13 13 let s: String = "hello".to_owned(); 14 - assert_eq!(s.get(), "hello"); 14 + assert_eq!(s.clone().get(), "hello".to_owned()); 15 15 let t = s.put("world".to_owned()); 16 16 assert_eq!(t, "world"); 17 17 }
+15 -15
crates/fx/src/core/tests/lens_test.rs
··· 10 10 } 11 11 12 12 impl Has<u32> for Ctx { 13 - fn get(&self) -> &u32 { 14 - &self.a 13 + fn get(self) -> u32 { 14 + self.a 15 15 } 16 16 } 17 17 impl Put<u32> for Ctx { ··· 28 28 } 29 29 30 30 impl Has<i32> for ST { 31 - fn get(&self) -> &i32 { 32 - &self.a 31 + fn get(self) -> i32 { 32 + self.a 33 33 } 34 34 } 35 35 impl Put<i32> for ST { ··· 39 39 } 40 40 } 41 41 impl Has<String> for ST { 42 - fn get(&self) -> &String { 43 - &self.b 42 + fn get(self) -> String { 43 + self.b 44 44 } 45 45 } 46 46 impl Put<String> for ST { ··· 62 62 fn from_has_put_lens() { 63 63 let lens: Lens<Ctx, u32> = Lens::new(); 64 64 let ctx = Ctx { a: 1, b: "hi" }; 65 - assert_eq!(lens.get(ctx.clone()), 1); 65 + assert_eq!(<Ctx as Has<u32>>::get(ctx.clone()), 1); 66 66 let updated = lens.set(ctx, 42); 67 67 assert_eq!(updated, Ctx { a: 42, b: "hi" }); 68 68 } ··· 146 146 st: ST, 147 147 } 148 148 impl Has<ST> for Outer { 149 - fn get(&self) -> &ST { 150 - &self.st 149 + fn get(self) -> ST { 150 + self.st 151 151 } 152 152 } 153 153 impl Put<ST> for Outer { ··· 165 165 let st_lens = Lens::<Outer, ST>::new(); 166 166 let a_lens = Lens::<ST, i32>::new(); 167 167 let composed = a_lens.prepend(st_lens); 168 - assert_eq!(composed.get(outer.clone()), 5); 168 + assert_eq!(Lens::get(&composed, outer.clone()), 5); 169 169 let updated = composed.set(outer, 99); 170 170 assert_eq!(updated.st.a, 99); 171 171 } ··· 179 179 st: ST, 180 180 } 181 181 impl Has<ST> for Outer { 182 - fn get(&self) -> &ST { 183 - &self.st 182 + fn get(self) -> ST { 183 + self.st 184 184 } 185 185 } 186 186 impl Put<ST> for Outer { ··· 198 198 let st_lens = Lens::<Outer, ST>::new(); 199 199 let a_lens = Lens::<ST, i32>::new(); 200 200 let composed = st_lens.append(a_lens); 201 - assert_eq!(composed.get(outer.clone()), 5); 201 + assert_eq!(Lens::get(&composed, outer.clone()), 5); 202 202 let updated = composed.set(outer, 123); 203 203 assert_eq!(updated.st.a, 123); 204 204 } ··· 207 207 fn left_lens_accesses_left_of_tuple() { 208 208 let pair: (i32, &str) = (10, "hi"); 209 209 let left = Lens::<'_, (i32, &str), i32>::left::<&str>(); 210 - assert_eq!(left.get(pair.clone()), 10); 210 + assert_eq!(Lens::get(&left, pair.clone()), 10); 211 211 let updated = left.set(pair, 42); 212 212 assert_eq!(updated.0, 42); 213 213 } ··· 216 216 fn right_lens_accesses_right_of_tuple() { 217 217 let pair: (i32, &str) = (10, "hi"); 218 218 let right = Lens::<'_, (i32, &str), &str>::right::<i32>(); 219 - assert_eq!(right.get(pair.clone()), "hi"); 219 + assert_eq!(Lens::get(&right, pair.clone()), "hi"); 220 220 let updated = right.set(pair, "bye"); 221 221 assert_eq!(updated.1, "bye"); 222 222 }
+2 -2
crates/lens_macro/src/lib.rs
··· 57 57 let fty = &f.ty; 58 58 quote! { 59 59 impl #impl_generics ::fx::Has<#fty> for #name #ty_generics #where_clause { 60 - fn get(&self) -> &#fty { 61 - &self.#fname 60 + fn get(self) -> #fty { 61 + self.#fname 62 62 } 63 63 } 64 64 }