pub struct Lens<S, T, A, B> { /* private fields */ }Expand description
A van Laarhoven–style lens encoded with getter/setter function pointers.
S — source type, T — modified source type,
A — focus type, B — replacement type.
For simple (non-polymorphic) lenses, use SimpleLens.
Implementations§
Source§impl<S, T, A, B> Lens<S, T, A, B>
impl<S, T, A, B> Lens<S, T, A, B>
Sourcepub fn new(getter: fn(&S) -> A, setter: fn(S, B) -> T) -> Lens<S, T, A, B>
pub fn new(getter: fn(&S) -> A, setter: fn(S, B) -> T) -> Lens<S, T, A, B>
Examples found in repository?
examples/domain_model_optics.rs (lines 58-61)
57fn customer_lens() -> SimpleLens<Order, Customer> {
58 Lens::new(
59 |o: &Order| o.customer.clone(),
60 |o, customer| Order { customer, ..o },
61 )
62}
63
64fn address_lens() -> SimpleLens<Customer, Address> {
65 Lens::new(
66 |c: &Customer| c.address.clone(),
67 |c, address| Customer { address, ..c },
68 )
69}
70
71fn city_lens() -> SimpleLens<Address, String> {
72 Lens::new(
73 |a: &Address| a.city.clone(),
74 |a, city| Address { city, ..a },
75 )
76}
77
78fn zip_lens() -> SimpleLens<Address, String> {
79 Lens::new(|a: &Address| a.zip.clone(), |a, zip| Address { zip, ..a })
80}
81
82fn name_lens() -> SimpleLens<Customer, String> {
83 Lens::new(
84 |c: &Customer| c.name.clone(),
85 |c, name| Customer { name, ..c },
86 )
87}More examples
examples/data_transformation.rs (lines 73-76)
72fn amount_lens() -> SimpleLens<Transaction, i64> {
73 Lens::new(
74 |t: &Transaction| t.amount_cents,
75 |t, amount_cents| Transaction { amount_cents, ..t },
76 )
77}
78
79fn name_lens() -> SimpleLens<Transaction, String> {
80 Lens::new(
81 |t: &Transaction| t.name.clone(),
82 |t, name| Transaction { name, ..t },
83 )
84}Sourcepub fn get(&self, s: &S) -> A
pub fn get(&self, s: &S) -> A
Examples found in repository?
examples/domain_model_optics.rs (line 170)
159fn main() {
160 println!("=== Domain Model with Optics Example ===\n");
161
162 let order = sample_order();
163
164 // 1. Lens: get/set/over
165 println!("--- Lens: basic get/set/over ---");
166 let customer = customer_lens();
167 let address = address_lens();
168 let city = city_lens();
169
170 println!(" Customer name: {}", name_lens().get(&order.customer));
171 println!(" City: {}", city.get(&address.get(&customer.get(&order))));
172
173 // 2. Composed Lens: deep access with .then()
174 println!("\n--- ComposedLens: deep access with .then() ---");
175 let order_city = customer_lens().then(address_lens()).then(city_lens());
176 let order_zip = customer_lens().then(address_lens()).then(zip_lens());
177
178 println!(" Order city: {}", order_city.get(&order));
179 println!(" Order zip: {}", order_zip.get(&order));
180
181 // Deep set
182 let updated = order_city.set(order.clone(), "Shelbyville".into());
183 println!(" After city update: {}", order_city.get(&updated));
184 println!(" Original unchanged: {}", order_city.get(&order));
185
186 // Deep over (modify)
187 let uppercased = order_city.over(order.clone(), |c| c.to_uppercase());
188 println!(" Uppercased city: {}", order_city.get(&uppercased));
189
190 // 3. Lens::transform — reusable update function
191 println!("\n--- Lens::transform — reusable update function ---");
192 let normalize_city: Box<dyn Fn(String) -> String> = Box::new(|c| c.trim().to_uppercase());
193 let normalize_order_city = city_lens().transform::<FnP>(normalize_city);
194 let addr = Address {
195 street: "456 Oak Ave".into(),
196 city: " new york ".into(),
197 zip: "10001".into(),
198 };
199 let normalized = normalize_order_city(addr);
200 println!(" Normalized city: {:?}", normalized.city);
201
202 // 4. Prism: sum type focus
203 println!("\n--- Prism: sum type focus ---");
204 let cc = credit_card_prism();
205 let wallet = wallet_prism();
206 let bank = bank_transfer_prism();
207
208 println!(" Credit card last4: {:?}", cc.preview(&order.payment));
209 println!(" Wallet preview: {:?}", wallet.preview(&order.payment));
210
211 // Prism::review — construct a variant
212 let new_payment = wallet.review(("PayPal".into(), 5000));
213 println!(" New wallet payment: {:?}", new_payment);
214
215 // Prism::over — modify only if matched
216 let updated_payment = cc.over(order.payment.clone(), |(last4, _exp)| {
217 (last4, "01/28".into())
218 });
219 println!(" Updated CC expiry: {:?}", updated_payment);
220
221 // Prism on non-matching variant: passes through unchanged
222 let unchanged = wallet.over(order.payment.clone(), |(prov, bal)| (prov, bal + 1000));
223 println!(" Wallet over on CC (unchanged): {:?}", unchanged);
224
225 // 5. Prism::transform — reusable pattern-matching function
226 println!("\n--- Prism::transform — reusable variant modifier ---");
227 let add_balance: Box<dyn Fn((String, i64)) -> (String, i64)> =
228 Box::new(|(prov, bal)| (prov, bal + 2500));
229 let add_wallet_balance = wallet.transform::<FnP>(add_balance);
230
231 let wallet_payment = Payment::Wallet {
232 provider: "PayPal".into(),
233 balance_cents: 10000,
234 };
235 let topped_up = add_wallet_balance(wallet_payment);
236 println!(" Topped up: {:?}", topped_up);
237
238 // Apply same transform to non-wallet — passes through
239 let still_cc = add_wallet_balance(order.payment.clone());
240 println!(" CC unchanged: {:?}", still_cc);
241
242 // 6. Summary: combine multiple optics
243 println!("\n--- Combining lenses and prisms ---");
244 let orders = vec![
245 sample_order(),
246 Order {
247 id: 1002,
248 customer: Customer {
249 name: "Bob Jones".into(),
250 address: Address {
251 street: "789 Elm St".into(),
252 city: "Capital City".into(),
253 zip: "12345".into(),
254 },
255 },
256 items: vec![Item {
257 name: "Mouse".into(),
258 price_cents: 2999,
259 quantity: 1,
260 }],
261 payment: Payment::Wallet {
262 provider: "Stripe".into(),
263 balance_cents: 50000,
264 },
265 },
266 Order {
267 id: 1003,
268 customer: Customer {
269 name: "Carol White".into(),
270 address: Address {
271 street: "321 Pine Rd".into(),
272 city: "Springfield".into(),
273 zip: "62702".into(),
274 },
275 },
276 items: vec![],
277 payment: Payment::BankTransfer {
278 iban: "DE89370400440532013000".into(),
279 },
280 },
281 ];
282
283 let order_city_lens = customer_lens().then(address_lens()).then(city_lens());
284 println!(" All cities:");
285 for o in &orders {
286 let payment_type = match &o.payment {
287 Payment::CreditCard { .. } => "CC",
288 Payment::BankTransfer { .. } => "Bank",
289 Payment::Wallet { .. } => "Wallet",
290 };
291 println!(
292 " Order #{}: {} ({}, pays via {})",
293 o.id,
294 order_city_lens.get(o),
295 name_lens().get(&o.customer),
296 payment_type
297 );
298 }
299
300 // Find all bank IBANs using Prism
301 println!("\n Bank IBANs:");
302 for o in &orders {
303 if let Some(iban) = bank.preview(&o.payment) {
304 println!(" Order #{}: {}", o.id, iban);
305 }
306 }
307}pub fn set(&self, s: S, b: B) -> T
Sourcepub fn then<X, Y>(self, inner: Lens<A, B, X, Y>) -> ComposedLens<S, T, X, Y>where
S: 'static,
T: 'static,
A: 'static,
B: 'static,
X: 'static,
Y: 'static,
pub fn then<X, Y>(self, inner: Lens<A, B, X, Y>) -> ComposedLens<S, T, X, Y>where
S: 'static,
T: 'static,
A: 'static,
B: 'static,
X: 'static,
Y: 'static,
Chain another lens to focus deeper, producing a ComposedLens.
Examples found in repository?
examples/domain_model_optics.rs (line 175)
159fn main() {
160 println!("=== Domain Model with Optics Example ===\n");
161
162 let order = sample_order();
163
164 // 1. Lens: get/set/over
165 println!("--- Lens: basic get/set/over ---");
166 let customer = customer_lens();
167 let address = address_lens();
168 let city = city_lens();
169
170 println!(" Customer name: {}", name_lens().get(&order.customer));
171 println!(" City: {}", city.get(&address.get(&customer.get(&order))));
172
173 // 2. Composed Lens: deep access with .then()
174 println!("\n--- ComposedLens: deep access with .then() ---");
175 let order_city = customer_lens().then(address_lens()).then(city_lens());
176 let order_zip = customer_lens().then(address_lens()).then(zip_lens());
177
178 println!(" Order city: {}", order_city.get(&order));
179 println!(" Order zip: {}", order_zip.get(&order));
180
181 // Deep set
182 let updated = order_city.set(order.clone(), "Shelbyville".into());
183 println!(" After city update: {}", order_city.get(&updated));
184 println!(" Original unchanged: {}", order_city.get(&order));
185
186 // Deep over (modify)
187 let uppercased = order_city.over(order.clone(), |c| c.to_uppercase());
188 println!(" Uppercased city: {}", order_city.get(&uppercased));
189
190 // 3. Lens::transform — reusable update function
191 println!("\n--- Lens::transform — reusable update function ---");
192 let normalize_city: Box<dyn Fn(String) -> String> = Box::new(|c| c.trim().to_uppercase());
193 let normalize_order_city = city_lens().transform::<FnP>(normalize_city);
194 let addr = Address {
195 street: "456 Oak Ave".into(),
196 city: " new york ".into(),
197 zip: "10001".into(),
198 };
199 let normalized = normalize_order_city(addr);
200 println!(" Normalized city: {:?}", normalized.city);
201
202 // 4. Prism: sum type focus
203 println!("\n--- Prism: sum type focus ---");
204 let cc = credit_card_prism();
205 let wallet = wallet_prism();
206 let bank = bank_transfer_prism();
207
208 println!(" Credit card last4: {:?}", cc.preview(&order.payment));
209 println!(" Wallet preview: {:?}", wallet.preview(&order.payment));
210
211 // Prism::review — construct a variant
212 let new_payment = wallet.review(("PayPal".into(), 5000));
213 println!(" New wallet payment: {:?}", new_payment);
214
215 // Prism::over — modify only if matched
216 let updated_payment = cc.over(order.payment.clone(), |(last4, _exp)| {
217 (last4, "01/28".into())
218 });
219 println!(" Updated CC expiry: {:?}", updated_payment);
220
221 // Prism on non-matching variant: passes through unchanged
222 let unchanged = wallet.over(order.payment.clone(), |(prov, bal)| (prov, bal + 1000));
223 println!(" Wallet over on CC (unchanged): {:?}", unchanged);
224
225 // 5. Prism::transform — reusable pattern-matching function
226 println!("\n--- Prism::transform — reusable variant modifier ---");
227 let add_balance: Box<dyn Fn((String, i64)) -> (String, i64)> =
228 Box::new(|(prov, bal)| (prov, bal + 2500));
229 let add_wallet_balance = wallet.transform::<FnP>(add_balance);
230
231 let wallet_payment = Payment::Wallet {
232 provider: "PayPal".into(),
233 balance_cents: 10000,
234 };
235 let topped_up = add_wallet_balance(wallet_payment);
236 println!(" Topped up: {:?}", topped_up);
237
238 // Apply same transform to non-wallet — passes through
239 let still_cc = add_wallet_balance(order.payment.clone());
240 println!(" CC unchanged: {:?}", still_cc);
241
242 // 6. Summary: combine multiple optics
243 println!("\n--- Combining lenses and prisms ---");
244 let orders = vec![
245 sample_order(),
246 Order {
247 id: 1002,
248 customer: Customer {
249 name: "Bob Jones".into(),
250 address: Address {
251 street: "789 Elm St".into(),
252 city: "Capital City".into(),
253 zip: "12345".into(),
254 },
255 },
256 items: vec![Item {
257 name: "Mouse".into(),
258 price_cents: 2999,
259 quantity: 1,
260 }],
261 payment: Payment::Wallet {
262 provider: "Stripe".into(),
263 balance_cents: 50000,
264 },
265 },
266 Order {
267 id: 1003,
268 customer: Customer {
269 name: "Carol White".into(),
270 address: Address {
271 street: "321 Pine Rd".into(),
272 city: "Springfield".into(),
273 zip: "62702".into(),
274 },
275 },
276 items: vec![],
277 payment: Payment::BankTransfer {
278 iban: "DE89370400440532013000".into(),
279 },
280 },
281 ];
282
283 let order_city_lens = customer_lens().then(address_lens()).then(city_lens());
284 println!(" All cities:");
285 for o in &orders {
286 let payment_type = match &o.payment {
287 Payment::CreditCard { .. } => "CC",
288 Payment::BankTransfer { .. } => "Bank",
289 Payment::Wallet { .. } => "Wallet",
290 };
291 println!(
292 " Order #{}: {} ({}, pays via {})",
293 o.id,
294 order_city_lens.get(o),
295 name_lens().get(&o.customer),
296 payment_type
297 );
298 }
299
300 // Find all bank IBANs using Prism
301 println!("\n Bank IBANs:");
302 for o in &orders {
303 if let Some(iban) = bank.preview(&o.payment) {
304 println!(" Order #{}: {}", o.id, iban);
305 }
306 }
307}Source§impl<S, T, A, B> Lens<S, T, A, B>
impl<S, T, A, B> Lens<S, T, A, B>
Source§impl<S, T, A, B> Lens<S, T, A, B>where
S: Clone,
impl<S, T, A, B> Lens<S, T, A, B>where
S: Clone,
Sourcepub fn over(&self, s: S, f: impl FnOnce(A) -> B) -> T
pub fn over(&self, s: S, f: impl FnOnce(A) -> B) -> T
Examples found in repository?
examples/data_transformation.rs (line 92)
89fn apply_discount(transactions: Vec<Transaction>, pct: f64) -> Vec<Transaction> {
90 let lens = amount_lens();
91 VecF::fmap(transactions, |t| {
92 lens.over(t, |a| (a as f64 * (1.0 - pct / 100.0)) as i64)
93 })
94}
95
96/// Normalize names to uppercase.
97fn normalize_names(transactions: Vec<Transaction>) -> Vec<Transaction> {
98 let lens = name_lens();
99 VecF::fmap(transactions, |t| lens.over(t, |n| n.to_uppercase()))
100}Sourcepub fn transform<P>(&self, pab: <P as HKT2>::P<A, B>) -> <P as HKT2>::P<S, T>where
P: Strong,
S: 'static,
T: 'static,
A: 'static,
B: 'static,
pub fn transform<P>(&self, pab: <P as HKT2>::P<A, B>) -> <P as HKT2>::P<S, T>where
P: Strong,
S: 'static,
T: 'static,
A: 'static,
B: 'static,
Profunctor encoding: transform a P<A, B> into a P<S, T> via this lens.
This is the key operation that connects concrete lenses to the profunctor
hierarchy. Given any Strong profunctor P and a value pab: P<A, B>,
transform produces P<S, T> by:
first(pab)lifts toP<(A, S), (B, S)>dimappre-composes withs -> (get(s), s)and post-composes with(b, s) -> set(s, b)
Examples found in repository?
examples/domain_model_optics.rs (line 193)
159fn main() {
160 println!("=== Domain Model with Optics Example ===\n");
161
162 let order = sample_order();
163
164 // 1. Lens: get/set/over
165 println!("--- Lens: basic get/set/over ---");
166 let customer = customer_lens();
167 let address = address_lens();
168 let city = city_lens();
169
170 println!(" Customer name: {}", name_lens().get(&order.customer));
171 println!(" City: {}", city.get(&address.get(&customer.get(&order))));
172
173 // 2. Composed Lens: deep access with .then()
174 println!("\n--- ComposedLens: deep access with .then() ---");
175 let order_city = customer_lens().then(address_lens()).then(city_lens());
176 let order_zip = customer_lens().then(address_lens()).then(zip_lens());
177
178 println!(" Order city: {}", order_city.get(&order));
179 println!(" Order zip: {}", order_zip.get(&order));
180
181 // Deep set
182 let updated = order_city.set(order.clone(), "Shelbyville".into());
183 println!(" After city update: {}", order_city.get(&updated));
184 println!(" Original unchanged: {}", order_city.get(&order));
185
186 // Deep over (modify)
187 let uppercased = order_city.over(order.clone(), |c| c.to_uppercase());
188 println!(" Uppercased city: {}", order_city.get(&uppercased));
189
190 // 3. Lens::transform — reusable update function
191 println!("\n--- Lens::transform — reusable update function ---");
192 let normalize_city: Box<dyn Fn(String) -> String> = Box::new(|c| c.trim().to_uppercase());
193 let normalize_order_city = city_lens().transform::<FnP>(normalize_city);
194 let addr = Address {
195 street: "456 Oak Ave".into(),
196 city: " new york ".into(),
197 zip: "10001".into(),
198 };
199 let normalized = normalize_order_city(addr);
200 println!(" Normalized city: {:?}", normalized.city);
201
202 // 4. Prism: sum type focus
203 println!("\n--- Prism: sum type focus ---");
204 let cc = credit_card_prism();
205 let wallet = wallet_prism();
206 let bank = bank_transfer_prism();
207
208 println!(" Credit card last4: {:?}", cc.preview(&order.payment));
209 println!(" Wallet preview: {:?}", wallet.preview(&order.payment));
210
211 // Prism::review — construct a variant
212 let new_payment = wallet.review(("PayPal".into(), 5000));
213 println!(" New wallet payment: {:?}", new_payment);
214
215 // Prism::over — modify only if matched
216 let updated_payment = cc.over(order.payment.clone(), |(last4, _exp)| {
217 (last4, "01/28".into())
218 });
219 println!(" Updated CC expiry: {:?}", updated_payment);
220
221 // Prism on non-matching variant: passes through unchanged
222 let unchanged = wallet.over(order.payment.clone(), |(prov, bal)| (prov, bal + 1000));
223 println!(" Wallet over on CC (unchanged): {:?}", unchanged);
224
225 // 5. Prism::transform — reusable pattern-matching function
226 println!("\n--- Prism::transform — reusable variant modifier ---");
227 let add_balance: Box<dyn Fn((String, i64)) -> (String, i64)> =
228 Box::new(|(prov, bal)| (prov, bal + 2500));
229 let add_wallet_balance = wallet.transform::<FnP>(add_balance);
230
231 let wallet_payment = Payment::Wallet {
232 provider: "PayPal".into(),
233 balance_cents: 10000,
234 };
235 let topped_up = add_wallet_balance(wallet_payment);
236 println!(" Topped up: {:?}", topped_up);
237
238 // Apply same transform to non-wallet — passes through
239 let still_cc = add_wallet_balance(order.payment.clone());
240 println!(" CC unchanged: {:?}", still_cc);
241
242 // 6. Summary: combine multiple optics
243 println!("\n--- Combining lenses and prisms ---");
244 let orders = vec![
245 sample_order(),
246 Order {
247 id: 1002,
248 customer: Customer {
249 name: "Bob Jones".into(),
250 address: Address {
251 street: "789 Elm St".into(),
252 city: "Capital City".into(),
253 zip: "12345".into(),
254 },
255 },
256 items: vec![Item {
257 name: "Mouse".into(),
258 price_cents: 2999,
259 quantity: 1,
260 }],
261 payment: Payment::Wallet {
262 provider: "Stripe".into(),
263 balance_cents: 50000,
264 },
265 },
266 Order {
267 id: 1003,
268 customer: Customer {
269 name: "Carol White".into(),
270 address: Address {
271 street: "321 Pine Rd".into(),
272 city: "Springfield".into(),
273 zip: "62702".into(),
274 },
275 },
276 items: vec![],
277 payment: Payment::BankTransfer {
278 iban: "DE89370400440532013000".into(),
279 },
280 },
281 ];
282
283 let order_city_lens = customer_lens().then(address_lens()).then(city_lens());
284 println!(" All cities:");
285 for o in &orders {
286 let payment_type = match &o.payment {
287 Payment::CreditCard { .. } => "CC",
288 Payment::BankTransfer { .. } => "Bank",
289 Payment::Wallet { .. } => "Wallet",
290 };
291 println!(
292 " Order #{}: {} ({}, pays via {})",
293 o.id,
294 order_city_lens.get(o),
295 name_lens().get(&o.customer),
296 payment_type
297 );
298 }
299
300 // Find all bank IBANs using Prism
301 println!("\n Bank IBANs:");
302 for o in &orders {
303 if let Some(iban) = bank.preview(&o.payment) {
304 println!(" Order #{}: {}", o.id, iban);
305 }
306 }
307}Trait Implementations§
Auto Trait Implementations§
impl<S, T, A, B> Freeze for Lens<S, T, A, B>
impl<S, T, A, B> RefUnwindSafe for Lens<S, T, A, B>
impl<S, T, A, B> Send for Lens<S, T, A, B>
impl<S, T, A, B> Sync for Lens<S, T, A, B>
impl<S, T, A, B> Unpin for Lens<S, T, A, B>
impl<S, T, A, B> UnsafeUnpin for Lens<S, T, A, B>
impl<S, T, A, B> UnwindSafe for Lens<S, T, A, B>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more