A lens is a (1) functional (2) bi-directional (3) first-class and (4) composable generalization of (5) struct field names in most traditional languages.
1) "Functional" meaning we eschew mutation and implicit context. Mutation is a very powerful feature of a language, and much can be encoded using it. When we remove mutation as an option, many of the things we would have just used mutation for separate into distinct and rich solution spaces.
2) "Bi-directional" meaning we can use the same widget for both accessing a sub-part of a whole, and for replacing that sub-part within a whole. I personally call these operations "extract" and "replace", since "get" and "set" feel like they raise the specter of mutability.
3) "First-class" means that the lenses themselves are manipulable constructs within the language. In a language like C or Java, I can have "obj.a.b.c", but I cannot have ".a.b.c" itself on its own. Even if you consider method references (like "Obj::a" in Java), you've only obtained the ability to extract the "a" field from an Obj -- any particular method or function is only going to go in one direction.
4) "Composable" means that you can take two lenses and fit them together to get a new lens. Conceptually, I think this is understood -- we intuitively understand how to navigate an object graph when writing down an expression like "obj.a.b.c" -- but having a composable first-class abstraction lets us do the same thing programmatically. (It need not even be at "runtime" -- imagine a compile-time macro system that programmatically generates nested accesses from some base rules you define. I think you've got the rudiments of a dependency injection system here.)
5) "Struct field" means that for the given type, the field is always accessible. "List element" (or "tree leaf", ...) would give you traversals instead of lenses, and "tagged union variant" / "subclass" would give you prisms.
As for potential applications of the approach, there's the approach dependency injection graphs outlined above. I've also used ideas from lenses/prisms in serialization/deserialization and web service middleware. (Middleware compose really well as functional optics!)
With profunctor optics, you can even construct self-describing operations. For instance, imagine a JSON parser that can be directly interrogated to obtain the schema it accepts, or a middleware stack that can be interrogated to determine which headers it looks at, and which routes it accepts. Personally, this is the area I'm most excited about with functional optics.