6

Was playing around with lenses in Prolog. Lenses are a kind of microscope that allow to zoom into a structure and do some reads or writes in a functional fashion. Basically my point of departure was the following modelling of setters and declarative getters in Prolog:

Getter: Just a <closure>,
called as call(<closure>, X, Y), this will retrieve the value Y from X.

Declarative Setter: The same <closure> but used with a different arity,
called as call(<closure>, X, Y, Z), this will update the X by a new value Y giving a new Z.

I quickly arrived at a definition of a lens composition operator @, which can be used to combine two lenses into a new one, just based on their closures. An example and a definition is found in the appendix. But according to this article lenses can be made to be simply compositional.

In my opinion, when something is compositional it can be easily modelled via DCG. I can do this for the getter as follows, but I did not yet figure out a way to do it for the declarative setter as well:

/* Getter composition as DCG */
@(C1, C2) --> 
     call(C1),
     call(C2).

How would I model the setter composition in DCG? Is this possible, maybe altering the intial assumptions of how the getters and declarative setters are modelled, so that the result is simply compositional?

Best Regards

Appendix: Here is an example of some setters and getters:

/* getter */
back(bicycle(X, _), X).
front(bicycle(_, Y), Y).
circumference(wheel(X, _), X).
spokes(wheel(_, Y), Y).

/* setter */
back(bicycle(_, Y), X, bicycle(X, Y)).
front(bicycle(X, _), Y, bicycle(X, Y)).
circumference(wheel(_, Y), X, wheel(X, Y)).
spokes(wheel(X, _), Y, wheel(X, Y)).

Here is the modelling of the lens composition:

:- op(600, xfy, @).

/* getter composition */
@(C1, C2, X, Y) :-
    call(C1, X, H),
    call(C2, H, Y).

/* setter composition */
@(C1, C2, X, Y, Z) :-
    call(C1, X, H),
    call(C2, H, Y, J),
    call(C1, X, J, Z).

Here is are some example runs:

Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.16)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam

?- call(front@spokes, bicycle(wheel(1330, 12), wheel(1440, 16)), X).
X = 16.

6 ?- call(back@circumference, bicycle(wheel(1330, 12), wheel(1440, 16)), X).
X = 1330.

7 ?- call(front@circumference, bicycle(wheel(1330, 12), wheel(1440, 16)), 1420, X).
X = bicycle(wheel(1330, 12), wheel(1420, 16)).
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111

1 Answers1

2

There is a solution which has a further indirection, but which has also the potential to somehow precompile access paths. We view access paths again as closures, but this time they modify continuation functions. There are two types of continuation:

Getter Continuation:
- Transforms the value before returning it.
Declarative Setter Continuation:
- Transforms the value by an additional parameter before updating it.

An access path element now transforms the two continuations, to give a new continuation. The access path element transformation is compositional, whereby the inner access elements are first applied.

Some example continuations are given in the appendix, we can then model composition of access elements via DCG as follows. Note the order in the body of the DCG, this reflects aforementioned access elements order.

/* composition */
@(C1, C2) :-
   call(C2),
   call(C1).

Bye

Appendix: Here are some transformer definitions:

/* transformer construction */
back(F, back(F)).
front(F, front(F)).
circumference(F, circumference(F)).
spokes(F, spokes(F)).

/* getter transformer */
back(F, bicycle(X, _), Y) :- call(F,X,Y).
front(F, bicycle(_, X), Y) :- call(F,X,Y).
circumference(F, wheel(X, _), Y) :- call(F,X,Y).
spokes(F, wheel(_, X), Y) :- call(F,X,Y).

/* setter transformer */
back(F, bicycle(X, Y), Z, bicycle(T, Y)) :- call(F,X,Z,T).
front(F, bicycle(X, Y), Z, bicycle(X, T)) :- call(F,Y,Z,T).
circumference(F, wheel(X, Y), Z, wheel(T, Y)) :- call(F,X,Z,T).
spokes(F, wheel(X, Y), Z, wheel(X, T)) :- call(F,Y,Z,T).

Here is the lens composition and some stop transformer:

:- op(600, xfy, @).

/* composition */
@(C1, C2, F, G) :-
   call(C2, F, H),
   call(C1, H, G).

/* stop getter */
id(X,X).

/* stop setter */
id(_,X,X).

here are some example runs:

Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.14-1-ga20f192)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam

?- ['lens.pro'].

?- call(front@spokes, id, F), call(F, bicycle(wheel(1330, 12), wheel(1440, 16)), X).
F = front(spokes(id)),
X = 16.

?- call(back@circumference, id, F), call(F, bicycle(wheel(1330, 12), wheel(1440, 16)), X).
F = back(circumference(id)),
X = 1330.

?- call(front@circumference, id, F), call(F, bicycle(wheel(1330, 12), wheel(1440, 16)), 1420, X).
F = front(circumference(id)),
X = bicycle(wheel(1330, 12), wheel(1420, 16)).