Skip to content

Instantly share code, notes, and snippets.

@timyates
Last active January 3, 2016 05:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timyates/8416286 to your computer and use it in GitHub Desktop.
Save timyates/8416286 to your computer and use it in GitHub Desktop.
Functional Lenses and Groovy
import groovy.transform.*
// Immutable test object
def tim = new Person( 'tim', new Address( 'Woo', 123 ) )
// Lens for setting address for a person
Lens personAddress = new Lens( { p -> p.address }, { p, a -> new Person( p.name, a ) } )
// And one for setting zipcode for an address
Lens addressZipcode = new Lens( { a -> a.zip }, { a, z -> new Address( a.street, z ) } )
// Compose them into one for setting zipcode for a person
Lens personZipcode = addressZipcode << personAddress
// Simplistic test
assert personZipcode( tim, 100 ) == new Person( 'tim', new Address( 'Woo', 100 ) )
//////////////////////////////////
// Test classes
@Immutable
class Address {
String street
Integer zip
}
@Immutable
class Person {
String name
Address address
}
//////////////////////////////////
// Lens implementation
@Immutable
class Lens {
Closure getter
Closure setter
def call( a ) { getter( a ) }
def call( a, b ) { setter( a, b ) }
Lens leftShift( Lens other ) {
new Lens( { a -> get( other.getter( a ) ) },
{ a, b -> other.setter( a, setter( other.getter( a ), b ) ) } )
}
}
@ronanM
Copy link

ronanM commented Jan 16, 2014

Maybe simpler with copyWith() ?

http://mrhaki.blogspot.fr/2013/11/groovy-goodness-create-copywith-method.html

// Lens for setting address for a person
Lens personAddress = new Lens( { p -> p.address }, { p, a -> p.copyWith(address: a) } )

// And one for setting zipcode for an address
Lens addressZipcode = new Lens( { a -> a.zip }, { a, z -> a.copyWith(zip: z) } )

@Immutable(copyWith = true)
class Person {
...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment