Beginner's question: why is this happening?

I’m using: SWI-Prolog version 8.2.4 under Windows 7 (64-bit)

I want the code to: Code a generative music system

My code looks like this:

pitch-name(c).
pitch-name(cf).
pitch-name(cs).
pitch-name(d).
pitch-name(df).
pitch-name(ds).
pitch-name(e).
pitch-name(ef).
pitch-name(es).
pitch-name(f).
pitch-name(ff).
pitch-name(fs).
pitch-name(g).
pitch-name(gf).
pitch-name(gs).
pitch-name(a).
pitch-name(af).
pitch-name(as).
pitch-name(b).
pitch-name(bf).
pitch-name(bs).

But what I’m getting is:

?- pitch-name(X).
X = c ;
X = cf ;
X = cs ;
X = d ;
X = df ;
X = ds ;
X = e ;
X = ef ;
X = es ;
X = f ;
X = ff ;
X = fs ;
X = g ;
X = gf ;
X = gs ;
X = a ;
X = af ;
X =  (as) ;
X = b ;
X = bf ;
X = bs.

?- 

Why is the line with as formatted the way it is, instead of X = as ; like all the other lines?

Thanks.

as is a defined operator.

?- current_op(P,T,as).
P = 700,
T = xfx.

?- current_op(P,T,a).
false.

Thanks! I thought it might be defined already, but I couldn’t find any reference when I searched for ‘as’ in the online documentation. Where can I find a description of it? Is this liable to cause a problem, or can I just ignore it?

It still displays ’ (as) ’ when I run the query. But this seems to work, so I guess I won’t worry about it, unless I run into a problem later.

?- pitch-name(as).
true.

?-

Thanks again.

as is an operator for two syntactic constructs. One is importing predicates under a different name. This allows for example:

:- use_module(library(lists), [member/2 as element_of]).

The other is to define some property combinations, notably inherited from XSB Prolog. For example:

:- table p/1 as incremental.

It is documented in the table at SWI-Prolog -- Manual

2 Likes

You have chosen a peculiar name for your predicate. When you write it as it is, you have defined a predicate -/2 with pitch in the first argument and name(X) in the second. So, you could do like this:

?- X-name(c).
X = pitch.

Maybe that was your intention all along.

Thanks. Is it considered bad practice to define something with a name that’s already in use, as long as you don’t need to use the pre-defined operator? I’m guessing that it would “mask” the pre-defined operator, but at the moment I don’t see any need to use it.

I can’t answer for Jan but I wouldn’t be bothered by this overlap between your atom as and the predefined operator.

It is completely harmless. as only has a special meaning in the described context. In any other context it is just the word as without any meaning. That is how Prolog works. For example, 1+2 is to Prolog just the term +(1,2). It is in no way related to arithmetic. Only when you write e.g. A is 1+2 as a goal it gets a meaning. Used as a goal (query), is/2 becomes a predicate and is/2 gives a meaning to 1+2, binding A to 3.

The Prolog REPL (toplevel) prints answers such that they are valid Prolog terms and the Prolog standard requires A = as to be written as A = (as) because as is an operator. That is why you see what you see.

2 Likes

You probably meant pitch_name(c). (with an underscore). It’ll work with -, but it’s probably not what you meant.

?- write_canonical(pitch-name(c)).
-(pitch,name(c))

?- write_canonical(pitch_name(c)).
pitch_name(c)
1 Like

You’re correct, I thought the hyphen would be treated as part of the name, not as an operator. But when I test it, it seems to work anyway. Why don’t I get an error like the following?

?- name(c).
ERROR: Unknown procedure: name/1
ERROR:     However, there are definitions for:
ERROR:         name/2
false.

My question was about something else, but thanks for pointing this out.

Another variation of as/2 as an operator.

Composing modules from other modules

:- reexport(module_6, except([pred/1 as mypred])).

In Prolog, an atom has some peculiar handling of the character class used routinely for arithmetic operators. And since we can use any atom as a functor, you are defining facts for ‘-’/2, as @Boris explained very well.
Arithmetic expressions are a DSL (Domain Specific Language), with specific rules enforced by is/2, and there ‘-’/2, or ‘-’/1, find their use (are interpreted) . But there are other domains where expression are useful, as you just found out :slight_smile:

When you defined pitch-name(c), you actually defined the fact '-'(pitch,name(c)). So, everything “works” because of the way term unification is done; but under the covers it’s slightly different from what you expect – it looks for the fact '-' with two arguments: pitch and name(c). This also has an effect on efficiency, because term indexing doesn’t work as well with such terms. So, it’s not “wrong”, but it could be confusing (e.g., if you wanted to put a spy point, you’d use the '-' predicate, not pitch-name).

If you want to see how any expression is processed by Prolog, use write_canonical/1.

1 Like

He may have explained it very well, but unfortunately, I didn’t understand the explanation.

Did you understand my explanation? :wink:

If you have any background in Lisp, you can think of Prolog “operators” as syntactic sugar for terms (e.g., 1+2 is just another way of writing '+'(1,2)) and everything is quoted by default, except for top-level goals in predicates. So, 1+2 doesn’t evaluate – it’s just a term; if you want it evaluated, you do something like X is 1+2 (which is the same as 'is'('+'(1,2)).
Here’s another example:

?- X = '+'(1,2).  % Nothing is evaluated
X = 1+2.

?- X is '+'(1,2).  % is/2 evaluates the term
X = 3.

?- Z = '+'(1,2), X is Z.
Z = 1+2,
X = 3.
1 Like

I didn’t explain it well at all :wink: I tried to give you something so that you can start figuring it out on your own.

As for predicate names, those must be valid atoms. You can always check if something is a valid atom using atom/1, like this:

?- atom(pitch-name).
?- atom('Pitch name').
?- atom(pitch_name).
?- atom(Pitch_name).

(try those out on your toplevel!)

So you can, in fact, use quotes to put any text inside an atom.

Yet another issue is using predicate names that happen to be defined already. name/2 happens to be a built-in predicate. I try to avoid such name clashes so that I don’t have to look too far to know which predicate definition I will get. Note that a predicate is identified by its “predicate indicator”, so Name/Arity (with the forward slash). I prefer not to define predicates with the same name but different arity only, but it definitely has its uses. You will find plenty of examples of same name with different arities in the standard library, usually in the interface to a module.

To avoid any confusion here:

  • Atoms defined as operators: feel free to reuse them. As long as they are just a part of a Prolog term, it depends on how you interpret that.
  • Predicate indicators that are already used by built-in predicates (like name/2, atom/1, and so on): avoid redefining those predicates unless you know what you are doing. It is however perfectly fine to use the atom from the name inside your Prolog terms, so something like this is fine:
my_pred(atom, foo).
my_pred(name, bar).

or even

my_other_pred(atom/1, foo+2).
my_other_pred(name/2, bar+3).
1 Like

Yes, thanks.

My mistake was in thinking that “pitch-name” would be parsed as a single token, as it would be in other languages I’m more familiar with, like C/C++, Java or Python. It was never my intention to redefine “name” or anything other predicate or operator. From now on, I’ll avoid using hyphens within names and use underscores instead. Knowing about write_canonical/1 and atom/1 will also come in handy.

I’ve rewritten my code as follows (I added a second argument to print out more user-friendly names).

pitch_name(c,'C').
pitch_name(cf,'Cb').
pitch_name(cs,'C#').
pitch_name(d,'D').
pitch_name(df,'Db').
pitch_name(ds,'D#').
pitch_name(e,'E').
pitch_name(ef,'Eb').
pitch_name(es,'E#').
pitch_name(f,'F').
pitch_name(ff,'Fb').
pitch_name(fs,'F#').
pitch_name(g,'G').
pitch_name(gf,'Gb').
pitch_name(gs,'G#').
pitch_name(a,'A').
pitch_name(af,'Ab').
pitch_name(as,'A#').
pitch_name(b,'B').
pitch_name(bf,'Bb').
pitch_name(bs,'B#').

Thanks.

If it’s inside quotes, then it’s a single name. You could, for example, write
'pitch-name'(c,'C'), but that’s a bit clumsy, so people tend to not do it.

The only major programming language I know that allows hyphens inside names (as opposed to underscores) is COBOL. :wink: