?- 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! 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?
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:
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.
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+2as 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.
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.
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
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.
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.
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 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).
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.