Defining facts in bulk ergonomically but (eventually) performantly

Hello Everyone,

I haven’t used Prolog seriously, but start to appreciate some of its expressiveness lately, and started toying around with it. So I might have a few questions every now and there. For start:

I would like to define many facts with repetitive arguments, for example:

mentions(foo, bar).
mentions(foo, baz).
mentions(foo, quux).

This is a bit repetitive, so I’m sure I could come up with a way to make this more compact. For example, two approaches I see based on docs is to

a) make mentions/2 defer to a mentions_all/2 predicate and doing some list lookup (or maybe even more efficient data structure lookup, as it was mentioned in an other thread). For example mentions_all(foo, [bar, baz, quux])

b) or, make mentions/2 a dynamic predicate, call an init predicate that assert-s all those facts (again based on some listy expansion), and finally use compile_predicates/1 to freeze mentions/2?

I’m torn between the base and these alternative approaches. Let’s forget about performance for now, as I don’t think I would ever hit a dataset size where this would be a concern. Let’s say my hesitation is between being simple and direct, even if a bit more tedious to type out (the base style) vs fostering a more ergonomic style.

What do you think? Is one more idiomatic in Prolong than the other?

(Btw, why do I want to map these a and b options back to the base? It seems it will be easier to work with facts once they are in that flattened format, rather than keep working with lists or other datastructures).

Can expand programmatically at startup:

:- dynamic mentions/2.
mentions(foo, [abc, def, gh]).
mentions(foo, bar).
mentions(foo, baz).
mentions(foo, quux).

expand_mention_lists :-
	forall(
		(	mentions(X, L),
			is_list(L)
		),
		(	forall(member(E, L), assertz(mentions(X, E))),
			retract(mentions(X, L))
		)
	).

:- expand_mention_lists.

Result:

?- listing(mentions).
:- dynamic mentions/2.

mentions(foo, bar).
mentions(foo, baz).
mentions(foo, quux).
mentions(foo, abc).
mentions(foo, def).
mentions(foo, gh).

… which is nice for SWI-Prolog -- Just-in-time clause indexing

@brebs already mentioned indexing, but
maybe it needs more explanation:

Comparing solution 1:

mentions(foo, bar).
mentions(foo, baz).

With solution 2:

mentions(foo, X) :- member(X, [bar, baz]).

Then solution 1 is more performant for call pattern (-, +) use
cases, if your prolog system has multi-argument indexing.

The problem is member/2 has O(N) lookup time complexity,
whereas a hash table index has only O(1) complexity.