5

For languages such as Java and C which allow syntactic sugar with operators, i.e. infix, they use precedence and associativity.

Prolog also uses associativity:
left-associative - yfx
right-associative - xfy

but why is there xfx?

The only thing I have found on the internet giving any more clarity is

xfx means that the operator 'dominates' upon its branches, while xfy or yfx stand for 'list construction', in sense that allow chaining expressions of same priority.

from: Interpretation of Prolog's operator in an exercise by CapelliC

If the answer can include examples where xfx is needed because xfy and yfx fail/don't have meaning, that would be appreciated.

For Prolog reference: The Prolog Built-in Directive op

As false notes:

Please note that the operators you give in above link differ from both the standard and SWI! They are outdated by at least 10 years. As an example, it should be current_op(200,fy,-).

After reading the two current answers by CapelliC and false it became clear that non-associative was the key word to locating more relevant information. So Operator associativity now make more sense with regards to xfx when paying attention to non-associative section.

Community
  • 1
  • 1
Guy Coder
  • 24,501
  • 8
  • 71
  • 136
  • "associativity" describes only how the operator relates to other operators , it describes nothing about the difference in the nature of the operands . – Kintalken May 21 '17 at 20:33

3 Answers3

6

The specifier of an operator denotes the class (prefix, infix, postfix) and associativity of an operator.

xfx means that the operator does not permit any form of associativity. In other words: it does not nest with operators of the same priority. However, it does still nest with operators of a lower priority.

The largest part of such operators are predicates of arity two that are used for comparison-like meaning and where nesting would have - at least - an entirely different meaning than what the predicate is about. They are all located at priority 700: High enough to allow all arithmetic operators to work without bracketing ; low enough to work with control constructs and related meta-predicates that expect goals as arguments. Think of (\+)/1, (;)/2, (',')/2.

Consider =<: If you want to state that X is between 1 and 3 you cannot write 1 =< X =< 3 because neither (1 =< X) =< 3 nor 1 =< (X =< 3) make sense in Prolog. In fact, both would lead to an evaluation error. You have to write 1 =< X, X =< 3. which means =<(1, X), =<(X, 3).

For some cases it might make sense to nest, think of (A = B) = P. Here, the first = is just a functor like any other of arity 2, while the second = is the built-in predicate (=)/2 for equality/unification. But such cases are rare and it is worth highlighting them with an extra pair of round brackets.

Note also that :- :- a. is invalid syntax, because (:-)/1 again does not nest.

Arithmetic operators are typically left associative since you process them left-to-right like 1+2+4, first computing 1+2 and only then adding 4. The notable exception is (^)/2: This is so, because (X^Y)^Z =:= X^(Y*Z) whereas (X^(Y^Z)) denotes a term that cannot be expressed with simpler operators.

false
  • 10,264
  • 13
  • 101
  • 209
  • 2
    @GuyCoder: Another way to remember: `x` means "no", and `y` means "yes I like to have an operator of my priority on my side". – false Nov 12 '13 at 18:52
  • "Arithmetic operators are typically left associative since you process them left-to-right" - believing that math is processed left-to-right is a consistent mistake on the part of computer people . I recently asked a number of advanced mathematicians "do you solve your arithmetic from left-to-right or right-toleft ?" All of them said right-to-left , 2 of them volunteered the opinion that processing arithmetic right-to-left was a characteristic of the advanced practicioner of mathematics . – Kintalken May 21 '17 at 19:15
  • Consider this : ``f : X → Y`` (which is declarativ , xfy) .v.s. this ``h ∘ (g ∘ f)`` (which is procedural , yfx) from here https://en.wikipedia.org/wiki/Morphism#Definition . You can go far with ``op`` if you abandon the notion of "associativity" and instead consider the ``x`` to be the ``domain``/\``source`` and consider the ``y`` to be the ``codomain/\target`` ; also sometimes a useful distinction is ``xfy`` is declarativ vs ``yfx`` is procedural . – Kintalken May 21 '17 at 19:16
  • "Arithmetic operators are typically left associative since you process them left-to-right" was a quote of an earlier comment ; the earlier comment was deleted subsequent to the appearance of my comment . The trail we leave behind might be valuable for others to follow . Also , it's okay to be "wrong" . What message to prolog "Students" , if the "Teachers" are ashamed of being "wrong" ? It's only sycophants and jerks that jump all over you and judge you for being "wrong" , i.e. they like to point their finger and make everyone else feel stupid because then they seem smarter . – Kintalken May 22 '17 at 15:07
  • ? This "Arithmetic operators are typically ..." is a quote from my answer above. Do you have any source to substantiate your claim that this does not hold - except anecdotal one? If you are adding a long list of numbers, it seems the natural way to start with the first two. – false May 22 '17 at 17:36
  • sorry false , I (idiot) thought that quote was in a comment that was subsequently deleted in response to my (apparent) ciriticism , my apologies . Other than the reference above ``h ∘ (g ∘ f)`` I have only the anecdotal . Not sure from where to obtain something canonical on this topic. I was taught to simplify from right to left, i.e "let x = 2 , let y = 3 , let z = 4 | w / z = x * y | w = x * y * z | w = 2 * 3 * 4 | w = 2 * 12 | w = 24" .... is that unusual ? i.e. is it also (or more commonly? ) taught as "x * y = w / z | x * y * z = w | 2 * 3 * 4 = w | 6 * 4 = w | 24 = w" ? – Kintalken May 23 '17 at 00:50
  • Now that I think about it , I was explicitly taught right-to-left evaluation in mathematics. For example, "given y = 3 , x = y - 4 , solve for x" the steps were |1| x = y - 4 |2| x = (-4) + y |3| x = (-4) + 3 |4| x = -1" . i.e. step |2| is a re-ordering of the terms such that the target of the substitution becomes right-most . In early grades , failure to demonstrate step 2 was reason for loss of marks . This is all antecdotal yes . Perhaps an odditity of my school region or whatever . For me it "seems natural" to do mathematics from right to left, maybe a inevitable result of my training . – Kintalken May 23 '17 at 01:21
  • "If you are adding a long list of numbers, it seems the natural way to start with the first two" --- as a computer programming task [*36'footnote] , I would naturally start with the first 2 as suggested . In constrast , as a list of numbers on paper , 1 number to each line , I would start with the rightmost digit , iterate the digits from bottom to top , note the carry at the top and repeat from the bottom . As a list of numbers all on one line , with the task of solving one operation per line , I would solve the rightmost term thus gradually reducing from right to left . – Kintalken May 23 '17 at 01:35
  • [*36'footnote] as a computer programmer before I met prolog . Consider the following from Clocksin & Mellish pg . 77 ``is_integer(0) . is_integer(X) :- is_integer(Y) , X #= Y + 1 .`` --- the recursive call cause's the Y to be solved first . I would rewrite the Clocksin & Mellish (not fully logical) as ``(is_integer(X):-(X #= 0 | X #= 1 + Y , is_integer(Y)))`` --- as a set of declarativ statments it reads from left-to-right but as a set of procedural outcomes it reads from right to left . – Kintalken May 23 '17 at 01:47
  • sorry for the comment spam but I do find this topic intriguing --- the division operator perhaps provides a definitiv case , division is ``numerator / denominator`` but also ``ordinal / cardinal`` i.e. ``36 apples / 2 bushels`` but not ``2 bushels / 36 apples`` and you need the cardinal (right) before you can have the ordinal (left) (similar to needing a type before having an instance of the type) ... you might state ``18 apples / 1 bushel`` but wouldn't state ``1 apple / 0.055555 bushels`` ... demonstrating that with division the right (bottom) is solved first . – Kintalken May 23 '17 at 06:12
  • 1
    So effectively you are adding digit-wise right-to-left on paper. Interesting. I learned it left-to-right with carries noted at the end (now I realize that the additional space for carries could be quite large). – false May 23 '17 at 09:31
2

For instance, (:-)/2 it's clearly an operator that can used only once, because it separates the head from the body.

The separation is 'relative', you could have in a rule, for instance

my_rule :- assertz((a :- b, c)).

Here is the full list of (default) xfx operators from SWI-Prolog

?- setof((X,Y), current_op(X,xfx,Y), L), maplist(writeln, L).
200, (**)
700, (<)
700, (=)
700, (=..)
700, (=:=)
700, (=<)
700, (==)
700, (=@=)
700, (=\=)
700, (>)
700, (>=)
700, (@<)
700, (@=<)
700, (@>)
700, (@>=)
700, (\=)
700, (\==)
700, (\=@=)
700, (as)
700, (is)
1200, (-->)
1200, (:-)
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • Can you explain what you mean by 'dominates' and 'relative'? – Guy Coder Nov 12 '13 at 17:39
  • Were purely descriptive words. I meant 'dominates' because it stays at top of its relative syntax tree, 'relative' was meant to introduce a scope. Parenthesis reset the scope. It's a simple construct, after all. – CapelliC Nov 12 '13 at 18:32
  • @CapelliC "For instance, (:-)/2 it's clearly an operator that can used only once, because it separates the head from the body." . The interpretation of ':-' as being a function definition such that ``FunctionHead :- FunctionBody`` is certainly the norm but perhaps too limiting for prolog . An alternate interpretation is that ``:-`` means *syntactic consequence* such that ``AntecedentExpression :- ConsequentExpression`` ... for example ``((foo - X + Y):-(throw(nonimplemented))) . ((bar):-(foo - 3 + chocolate)) . ?- (bar).`` . i.e. ``(foo - X + Y)`` is not a function head . – Kintalken May 23 '17 at 02:19
  • @kintalken ":- means **syntactic** consequence" ... or maybe it means **semantic** consequence , or perhaps ``SatisfactionModel :- SatisfactionSentences`` , there are (apparently) lots of potentially applicable interpretations from the broad field of logic .c.f. https://www.youtube.com/watch?v=S2coygw1HT0 – Kintalken May 23 '17 at 02:24
2

I don't think the current status of the prolog standard op scene is necessarily a definitive guide to how to use op. Reduction of each op to these type codes (xf, fx, yfx, etc.) was a brilliant invention, but such an invention is more a product of the zeitgeist than the product of any particular rationale (.i.e. the outcome is not necessarily defined by the intentions of the inventor).

xfy is an op that has a source on the left and a target on the right. xfx is an op that has a source on the left and a source on the right. yfx is an op that has a target on the left and a source on the right.

'xfy' is (generally) associated with :

  • left-to-right
  • top-to-bottom
  • domination
  • being first
  • being declarativ
  • being ignorant of the 'yfx'

'xfx' is (generally) associated with:

  • judgement of the operands
  • comparison of things of equal class
  • that's all I have for this weirdo

'yfx' is (generally) associated with:

  • right-to-left
  • bottom-to-top
  • egalitarian
  • being last (thus ultimately achieving true domination)
  • being procedural
  • being tolerant of the shenanigans of the 'xfy'

Note that the following works out because the 'x' and 'y' are partnered throughout :

  xfy xfx yfx

but in contrast this does not work out:

  yfx xfx xfy

Some examples (untested, just sketches)...

  (term_expansion((_a),(true)):-(asserta(_a))) .
  (term_expansion((_a | _b),(_b)):-(expand_term(_a,_c),asserta(_c))) .

  :-
  (op(10'1200,'xfy',':='))
  ,
  (op(10'1200,'xfx',':~'))
  ,
  (op(10'1200,'yfx',':-'))
  .

  (term_expansion((_a := _b :- _c),(term_expansion(_a,_b):-_c))) .
  (term_expansion((_a :~ _b :- _c),(goal_expansion(_a,_b):-_c))) .
  (term_expansion((_a := _b1 :~ _b2 :- _c),((_a := _b1 :- _c) | (_a :~ _b2 :- _c)))) .

In that example the :- is yfx. The syntax is ___consequent___ :- ___precedent___ ; the right is the source aka the domain (thus evaluated first), the left is the target aka the codomain (thus evaluated second).

In that example the :~ is xfx. The syntax is ___goal_1___ :~ ___goal_2__. It corresponds to a goal_expansion.

In that example the := is xfy. The syntax is ___precedent___ := ___consequence__. It corresponds to a term_expansion.

  :-
  (op(10'700,'xfy','@='))
  ,
  (op(10'700,'xfx','\\='))
  ,
  (op(10'700,'yfx','#='))
  .

  (_x @= _y) :- ('='(_x,_y)) . % .i.e. term equality
  % (_x1 \= _x2)   % prolog builtin, not equal
  % (_y #= _x)   % via clpfd, math is generally simplification/\answer is left

The \= is, in my opinion, the definitiv example of an xfx operator.

The terminology source | domain, target | codomain, is from here : https://en.wikipedia.org/wiki/Morphism#Definition.

I don't know much about category theory (yet), but I do wonder if the xfx has something to do with the identity morphism.

user229044
  • 232,980
  • 40
  • 330
  • 338
Kintalken
  • 763
  • 5
  • 9