Skip to content

Instantly share code, notes, and snippets.

@ekmett
Created July 24, 2022 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ekmett/ce5cc1edce154248fac9ea0f2c3ede9b to your computer and use it in GitHub Desktop.
Save ekmett/ce5cc1edce154248fac9ea0f2c3ede9b to your computer and use it in GitHub Desktop.
lenses for rust don't have to be an inference nightmare
#![allow(non_camel_case_types)]
// type-inferrable lenses for rust can exist
// the bokeh of a lens is the stuff that is out of focus.
// this trait describes how given the lens as context, and a bokeh
// you can reassemble the result.
pub trait Bokeh<C,B> {
type T;
fn blur(&self,bokeh:C,b:B) -> Self::T;
}
// isomorphism lenses with constant complement, and type changing assignment
// and, unlike every other encoding I've seen for lenses in rust, usable type inference
pub trait Lens<S> : Bokeh<Self::C,Self::A,T=S> {
type A; // what you get if you feed this lens a value of type S
type C; // everything else that is not the particular A you are interested in
fn focus(&self,s:S) -> (Self::C, Self::A);
fn map<B,F>(&self,s:S,f:F) -> <Self as Bokeh<Self::C,B>>::T where
Self: Bokeh<Self::C,B>,
F: FnOnce (&Self::A) -> B
{
let (c, a) = self.focus(s);
self.blur(c, f(&a))
}
fn get(&self,s:S) -> Self::A { self.focus(s).1 }
fn set<B>(&self,s:S,b:B) -> <Self as Bokeh<Self::C,B>>::T where
Self: Bokeh<Self::C,B>,
{
self.blur(self.focus(s).0,b)
}
}
pub struct fst;
impl <Y,Z> Bokeh<Z,Y> for fst {
type T=(Y,Z);
fn blur(&self,c:Z,b:Y) -> (Y,Z) { (b,c) }
}
impl <X,Z> Lens<(X,Z)> for fst {
type A=X; type C=Z;
fn focus(&self,s:(X,Z)) -> (Z,X) { (s.1,s.0) }
}
pub struct snd;
impl <Z,Y> Bokeh<Z,Y> for snd {
type T = (Z,Y);
fn blur(&self,c:Z,b:Y) -> (Z,Y) { (c,b) }
}
impl <Z,X> Lens<(Z,X)> for snd {
type A=X; type C=Z;
fn focus(&self,p:(Z,X)) -> (Z,X) { p }
}
pub struct comp<P,Q>(P,Q);
//impl <S,P,Q:
impl <C,D,B,P,Q> Bokeh<(C,D),B> for comp<P,Q> where
P : Bokeh<C,B>,
Q : Bokeh<D,<P as Bokeh<C,B>>::T>,
{
type T = <Q as Bokeh<D,<P as Bokeh<C,B>>::T>>::T;
fn blur(&self,bokeh:(C,D),b:B) -> Self::T {
self.1.blur(bokeh.1,self.0.blur(bokeh.0,b))
}
}
impl <S,P,Q> Lens<S> for comp<P,Q> where
Q: Lens<S>,
P: Lens<<Q as Lens<S>>::A>,
{
type A = <P as Lens<<Q as Lens<S>>::A>>::A;
type C = (<P as Lens<<Q as Lens<S>>::A>>::C, <Q as Lens<S>>::C);
fn focus(&self,s:S) -> (Self::C, Self::A) {
let (d,a) = self.1.focus(s);
let (c,b) = self.0.focus(a);
((c,d),b)
}
}
pub trait Iso<S> : Lens<S,C=()> {}
impl <S,T: Lens<S,C=()>> Iso<S> for T {}
pub struct swap;
impl <X,Y> Bokeh<(),(X,Y)> for swap {
type T = (Y,X);
fn blur(&self,_:(),b:(X,Y)) -> (Y,X) { (b.1,b.0) }
}
impl <X,Y> Lens<(X,Y)> for swap {
type A = (Y,X);
type C = ();
fn focus(&self,s:(X,Y)) -> ((),(Y,X)) { ((),(s.1,s.0)) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(fst.map((1,2),|x| x + 1),(2,2));
assert_eq!(snd.map((1,2),|x| x + 1),(1,3));
assert_eq!(snd.set((1,2),"hello"),(1,"hello"));
assert_eq!(fst.get((1,2)),1);
assert_eq!(comp(fst,fst).get(((1,2),3)),1);
assert_eq!(comp(snd,fst).get(((1,2),3)),2);
assert_eq!(comp(snd,comp(snd,fst)).get(((1,(2,3)),4)),3);
assert_eq!(comp(snd,comp(snd,fst)).set(((1,(2,3)),4),"hello"),((1,(2,"hello")),4));
assert_eq!(swap.get((1,2)),(2,1));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment