Re: "dict"ionary typing?

Shon Feder <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <CAG+hd-i0tQOm7cLk6pZ8Oh2cBFxZ0aA+8V6KC_9Wu3cR_huNjw@mail.gmail.com>
I believe that what you are describing, Michael, is *exactly* what Carlo
Capelli announced with his `lifter` package, in the "Functional Syntax"
thread yesterday:

> I crafted my own tiny interface, I'll show it since I got a positive
review
> from an user...
>
>
>
> 1 ?- [lifter].
>
> % lifter compiled into lifter 0.01 sec, 13 clauses
>
> true.
>
>
> 2 ?- length(List, ° is 1+1).
>
> List = [_G1367, _G1370].
>
>
> We can name lifted variables, if are worth reuse
>
>
> 3 ?- length(List, °L is 1+1), length(L2, ° is L+2).
>
> List = [_G1740, _G1743],
>
> L = 2,
>
> L2 = [_G1746, _G1749, _G1752, _G1755].

I have used the module (https://github.com/CapelliC/prolog-snippets) and I
find it quite interesting. Carlo's approach is interesting for exactly the
reasons you note.

> I dislike declarations when they can be inferred from usage.  I
> particularly dislike function declarations in Prolog because they imply
> that a predicate can only be used as a function in a single "mode". One
might reasonably use append/3 as a function in three different modes.


`lifter` implements the same goal_expansion pattern as Richard describes:

> If a name/arity pair was declared as a function or if
> there was a :- func [mod:]name/arity declaration, then
> in a clause body
> p(...,name(T1,...,Tn),...)
> =>
> name(T1, ..., Tn, T0),
> p(..., T0, ...)

With `lifter`, the clause

    longer(A,B) :-
        length(A,º) > length(B,º).

is expanded to

    longer(A, B) :-
        length(A, C),
        length(B, D),
        C > D.

I like that this approach satisfies the desire for expressions that
"evaluate in place" but keeps the flexibility and potent, multi-directional
functionality of prolog relations. Also, as with Richards, Playing with
Carlo's module has had me thinking about different ways of understanding a
function within the scope of my limited knowledge of Prolog, maths, and
logic generally. (I apologize, by the way, if the following is too naive to
contribute meaningfully to this discussion).

Insofar as functions are just ordered pairs of elements belonging to
certain domains, that is, insofar as they are thought of extensionally the
common representation, f(n1, ..., nm), expresses the ordering by omitting a
reference to the right-most element. This is a differently emphasized
articulation of the same relationship that Sterling and Shapiro describe in
'The Art of Prolog': "The predicate ackermann(M,N,A) denotes that A =
ackerman(M,N)." The predicate describes a function by using the rightmost
argument place to connote the *output*. According to this view, it seems
like `ackermann/3` is thought of as describing a special case of equality.
It might be written more clearly as `equals(ackermann(M,N), A)`.

The function is governed by equality (which is, in one sense, just a
relation of substitutability: "If one substitutes equals for equals,
equality remains" [^1]). The function can evaluate to its output and
doesn't need to make reference to the ouput element, so we can write y =
f(x) + 2. Of course, in Prolog, we could add to our program the fact
`ackermann(_,_,1).`, and then we're not describing a mathematical function
any more, since every set of inputs returns 1 as well as it's "proper"
value. Because of the important differences between the usual
representation of functions and the common meanings of Prolog predicates, I
am inclined to towards Michael's view on function declarations in Prolog. I
am certain, however, that I'd like any implementation of functions to give
clear syntactic cues of what is going to be substituted for an implicit
value.

'lifter' does an excellent job of addressing these issues. I was trying to
find a way of thinking about what 'lifter' does with `º` that made sense to
me, and I kept coming back to the idea of the relative pronoun: it lets us
string dependent clauses together without re-designating the subject each
time. Here's my sloppy translation of a sloppy definition of factorial
written with lifter:

    factorial(0,1).
    factorial(N,Fac) :- Fac is factorial(º is (ºN > 0) - 1, º) * N .

    The factorial of N is Fac, if Fac is the factorial of a number N,
    which, greater than zero, is subtracted by 1, that is multiplied by N .

My analogy with relative clauses might not be completely sound, but I do
think the complexity of, and easily convoluted nature of this kind of
pattern of expression is apparent (granted, this is a straw example).

In any case, I really like what `lifter` suggests about the relationship
between Prolog predicates and functions: the latter is a kind of
proposition is which one argument is emphasized to the point of eclipsing
the others. In fact, however, the function merely hides its extra arguments
and clauses within the internalized conventions of mathematical discourse.
There's a sort of goal expansion going on in our normal use of functions,
and by adopting a some slightly unusual syntax, we can interact with
structure of the expansion procedure in interesting ways (the difference
between Richard's and Carlo's use of goal_expansion).

I believe that it is in relation to dynamics of this sort that Russell
described functions in preceding sense to be a special case of
*propositional functions*:


... "the father of x" is just as legitimately a function of which x is the
argument as is "the logarithm of X." Functions in this sense are
*descriptive* functions. As we shall see later, there are functions of a
still more general and more fundamental sort, namely, *propositional*
functions; but for the present we shall confine our attention to
descriptive functions, *i.e.* "the term having the relation R to x," or,
for short, "the R of x" ...

I suspect that Carlo's `º` means most accurately "the term having the
relation ... to ...". It says that the predicate in which it occurs is
really about this one particular thing, such that you can just replace the
predicate in this context with the thing. I think a little more sugar could
maybe be sprinkled onto the `lifter` with good results. For instance, I'd
like to see it implemented through `term_expansion/2` so that it can lift
variables from the head of a clause as well--it could be used to visually
organize conditions on the arguments. Also, I find it awkward to use
arithmetical propositions in this way: e.g. plus(1, º is 4 * 5, X). It
might be interesting to try adding another pattern like º{<Expression>}.
These two additions would let factorial be written like,

    factorial(0, 1).
    factorial(ºN > 0, Fac) :- Fac is factorial( º{N - 1}, º) * N

Actually, I think you could write the second clause thus

    factorial(ºN > 0, º{ factorial( º{N - 1}, º) * N) }).

In any case, thinking about a function as a description, as Russell
suggests, it becomes quite evident what we would want descriptive functions
for in our language, since they perform the very same service as definite
descriptions of natural language: to express complex relations amongst
objects without needing relative clauses or redundant expression: I can say

    "the father of my friend knows the ceo of the world's richest
corporation"

rather then

    "father(Father,Friend), friend(Me,Friend), knows(Father, CEO), ceo(CEO,
Corp), largest_in(world, Corporation)."

I have experimented with using the '.' in atoms to designate a suffix
which, when appended to predicates, replaces the predicate with the
variable in its hidden last argument. This is pretty much the same as
Ciao's tilde operator, I think, only it might look a bit nicer and
contribute to more expressive statements:

    longer(A, B) :-
      length.of(A) > length.of(B).


In this case, `.of` is simply a suffix that says a term is expanded to be
replaced by the value of it's third argument. The suffixed preposition says
something is being described. So `reverse/2` can be called `reverse.of(L)`.
You don't need a function declaration for these, because they are just
normal predicates hiding their third argument. However, it is sometimes
desirable to have a visual cue as to which predicates are likely to called
on as descriptions, so a simple operator for making the distinction might
be in order:

    v(Expression) => Value :- Value is Expression.

Where `v` stands for "value" and `=>` is just sugar for expression's sake:
`?- listing(v). v(A, B) :- B is A.`

    ?- X = v.of(length.of([1,2,3,4,5])) *  5 .

This seems to fit well with the syntax for maps/dics. This could be used in
conjunction with lifter, perhaps?

    factorial(0,1).
    factorial(ºN > 0) => factorial.of(º{N - 1}) * N.

(Which should expand into a totally vanilla Prolog clause).

Perhaps there could also be a simple, dynamic, multi-file predicate for
adding in new suffixes for kicks:

    descriptive_suffix(.to).
    appended(A, B)  => C :- append(A,B,C).

    ?- X = appended.to([1,2], [3,4]).
    X = [1,2,3,4].

---

In any case, I have really enjoyed reading these discussions recently and
I'm excited about the developments in swi7, not because I have enough
knowledge to have an opinion on how things should develop, but because I
have benefited greatly from -- and invigorated by -- the discussions that
have emerged in the process.

I'm sorry this is so long.


*/
-------------- next part --------------
HTML attachment scrubbed and removed
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.