Foo vs foo()

What exactly is foo() when compared to foo? I know the latter is simply an atom. But how should I understand foo()?

What I found in swipl. Interesting that Tau and Scryer throw syntax errors.

?- X = a().
X = a().

?- a = a().
false.

?- write_canonical(a()).
a()
true.

This form seems to behave differently from other compounds:

?- a =.. Out.
Out = [a].

?- a(10) =.. Out.
Out = [a, 10].

?- a() =.. Out.
ERROR: Domain error: `compound_non_zero_arity' expected, found `a()'
ERROR: In:
ERROR:   [10] a()=.._7552
ERROR:    [9] toplevel_call(user:user: ...) at /usr/local/Cellar/swi-prolog/8.4.0/libexec/lib/swipl/boot/toplevel.pl:1117

The main reason I want to understand foo() is because I’m going to be exposing Prolog to end-developers, and writing foo() over foo makes sense in my project’s context, but I wonder if I should try to disallow it since their terms will be fed into =../2

1 Like

Check out SWI-Prolog datatypes and Verify Type of a Term

?- var(foo).
false.

?- nonvar(foo).
true.

?- number(foo).
false.

?- atom(foo).
true.

?- string(foo).
false.

?- atomic(foo).
true.

?- compound(foo).
false.

?- callable(foo).
true.

?- ground(foo).
true.
?- var(foo()).
false.

?- nonvar(foo()).
true.

?- number(foo()).
false.

?- atom(foo()).
false.

?- string(foo()).
false.

?- atomic(foo()).
false.

?- compound(foo()).
true.

?- callable(foo()).
true.

?- ground(foo()).
true.

So now you have some useful ideas but they really are not of much use for a beginner. Latter when you get more advanced you start to eat these predicates that check types like candy. Then when you get really advanced you use them only when absolutely necessary because you can keep track of all of the details in your head.

Also note the note for callable/2

This was intended as a type-test for arguments to call/1, call/2 etc.

In other words don’t rely on callable/2 for just about anything.


But how should I understand foo() ?

Your knowledge of imperative programming will do you good when learning Prolog. Do not focus on the (). What you should understand is

  1. Prolog is homoiconic so data is code and code is data.

As code foo() is a predicate that has no arguments but then that would be more commonly written as just foo. If it had arguments it could be foo(A,B).
foo() could be used as data but more likely it would have one or more values associated with it, E.g, foo(bar) or foo(bar,baa).

  1. Prolog for beginner should not think about functions. Latter on the use of the word function will slip in for certain needs but for the most part early on learn that Prolog has predicates.

Please explain. Examples would be good.


See this reply


HTH

1 Like

Here’s the relevant section of the SWI-Prolog manual: Compound terms with zero arguments

So, in short, foo() is a compound term like foo(1) or foo(a, b), except it has zero arguments. As you already found out, this is only supported in SWI-Prolog. In all other Prolog systems (that I know of), a compound term has to have at least one argument - if you want no arguments, you instead use a plain atom (with no parentheses after it).

Whether it makes sense to use zero-argument compound terms depends on your use case. As mentioned in the manual, the intended use is in domain-specific languages based on (SWI-)Prolog syntax, so that the user input can have terms that look like a zero-argument function call in an imperative language. That sounds similar to what you’re describing.

On the other hand, it probably makes less sense to use zero-argument compounds in an internal data representation. As you noticed, they are not compatible with the usual term manipulation predicates like (=..)/2 and functor/3 - you have to use the special-purpose compound_name_arity/3 and compound_name_arguments/3 instead.

So I would recommend using plain atoms internally, because they are a standard Prolog data type that you know will be handled correctly by all code. Also, if you ever need to exchange Prolog data with another Prolog implementation, you’ll have fewer issues with plain atoms. But if you know that your data will always stay inside SWI-Prolog, and you write your code using the right predicates, then zero-argument terms should work fine too.

2 Likes

Thanks for the great replies!!

I would consider my knowledge of Prolog to be intermediate. I understand homoiconicity, predicates over functions, and so on. It’s the fine details that I often struggle to get a grasp on.

As mentioned in the manual, the intended use is in domain-specific languages based on (SWI-)Prolog syntax, so that the user input can have terms that look like a zero-argument function call in an imperative language. That sounds similar to what you’re describing.

Indeed it is! My context is allowing the end-developer to define function interfaces for an existing, external domain. For example:

interface(myThing, [
  foo: [bool],
  bar(integer): [integer, integer],
]).

It’s a bit unfortunate that other Prologs don’t support zero-argument compounds, because it’s completely natural to write foo(): bool in this domain. Considering this code will run in the browser via Tau, I think I’ll just have to document it under a “common pitfalls” section.

In the long run I might have to write a string parser instead of relying on terms, purely for the sake of excellent developer experience. It’s nice that Prolog is good at this too.

You should at least take a look at the Aquarius compiler info (ref) and EDCGs.

While it also has a bit of learning curve, it might be what you need/seek.

1 Like

To a first approximation, yes; but technically, code and data aren’t the same - their representation is the same. The theorems for fixed-point recursion and combinator mean that working programmers don’t normally have to worry about this. However, you do need to use call/1 to actually invoke a predicate using its representation – and clause/2 goes the other way.

I’ve noticed that beginning Prolog programmers get confused with this; I’m not sure if my explanation makes things clearer or murkier.