Overriding/shadowing built-in predicates - when is it (not) allowed?

I’m trying to override some of SWI’s built-in predicates (in the context of SICStus emulation, as usual). This seems to work for some predicates, but not for others. For example, I’m able to override statistics/2, but not at_end_of_stream/1:

?- [user].
|: statistics(answer, 42).
|: at_end_of_stream(this_is_not_a_stream).

ERROR: user://2:33:
ERROR:    No permission to modify static procedure `at_end_of_stream/1'
|: ^D% user://2 compiled 0.00 sec, 1 clauses
true.

?- statistics(answer, X).
X = 42.

?- at_end_of_stream(this_is_not_a_stream).
ERROR: stream `this_is_not_a_stream' does not exist
ERROR: In:
ERROR:   [10] at_end_of_stream(this_is_not_a_stream)
ERROR:    [9] toplevel_call(user:user: ...) at <builddir>/home/boot/toplevel.pl:1113

This is odd because both are built-in/foreign predicates from the system module. predicate_property/2 also returns nearly identical information for both predicates:

?- predicate_property(statistics(_, _), Prop).
Prop = visible ;
Prop = built_in ;
Prop = foreign ;
Prop = static ;
Prop = imported_from(system) ;
Prop = nodebug ;
Prop = defined ;
Prop = size(144).

?- predicate_property(at_end_of_stream(_), Prop).
Prop = visible ;
Prop = built_in ;
Prop = foreign ;
Prop = static ;
Prop = imported_from(system) ;
Prop = nodebug ;
Prop = iso ;
Prop = defined ;
Prop = size(144).

The only difference seems to be that at_end_of_stream/1 is ISO and statistics/2 isn’t. Does SWI-Prolog not allow overriding/shadowing ISO predicates? Is there any way to work around that restriction and do it anyway? :slight_smile:

To clarify - I don’t want to redefine/modify the actual system predicates. I only want to define a predicate with the same name in another module, and (in the scope of that module) have my predicate shadow the built-in predicate of the same name.

What does “to shadow” mean in this context?

In general, it means “hiding a declaration from an outer scope by declaring something with the same name in an inner scope”. I’m trying to declare my own version of at_end_of_stream/1 locally in a module, so that when code inside my module (or code that imports the module) calls at_end_of_stream/1, it uses my module’s definition, but all other code still uses the standard SWI-Prolog definition.

I know the term “shadowing” mostly from the Python world. Maybe it’s not used as much in the context of Prolog.

Thank you for clarifying. I didn’t know the term, but that doesn’t mean much. Perhaps it’s used widely and I’ve not come out from under my rock often enough to hear it.

See redefine_system_predicate/1. Handle with care. Also be aware that some of the low-level stuff is handled by the compiler and VM and although you can redefine the predicate, it won’t have much effect as most calls are inlined as VM code. This notably holds for unification, comparison, control structures, arithmetic and type tests.

Thank you, that’s exactly what I was looking for. Sadly it seems that such redefined system predicates cannot be exported, which makes it not very useful for expects_dialect(sicstus).

For now I’ll just rewrite the “is this stream valid and readable” check in our application so that it can also handle SWI’s possibly blocking at_end_of_stream/1.

Typically the way to deal with different semantics in dialects is to use goal_expansion/2 to map predicate X into sisctus_X and export sicstus_X from sicstus.pl. Is there a reason why that doesn’t work?

1 Like

Good point - rewriting the calls using goal_expansion/2 seems to work fine with my code. Not sure why I didn’t try that before. I suppose I thought that simply overriding/hiding the built-in definition would be much simpler, but clearly it isn’t. :smiley: