Modules are one of the most controversial issues in Prolog standardization. Then it's hard to find coherent information on the topic and examples.
IMHO your problem derives from too much expectation about the construct, that does something simple, but has complicated consequences, because it changes one fundamental property of the language element: atoms, when used as predicate identifiers, become pairs Module:Name, where Module is called the context (well, kind of... I hope someone will contribute to understand this important topic with more precise wording and references).
To simplify a bit your problem, let's rename (<-)/2 as eval/2. Then
:- module(ch20_Ires, []).
:- reexport([ch20_utils, ch20_examples]).
ch20_utils:eval(R, residual_info(Attribute)) :-
findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
list_to_set(Values, Values_set),
setof(Class, Class^Attr^example(Class,Attr), Classes_set),
eval(Values_prob_sum, sum_list(Values_set, value_prob(Values, Classes_set, Attribute))),
eval(R, -Values_prob_sum), !.
...
% note: moved here from ch20_utils
ch20_utils:eval(R, X) :- R is X, !.
and
:- module(ch20_utils, [ eval/2 ]).
:- multifile eval/2.
eval(R, log2(X)) :- eval(R, log(X) / log(2)), !.
...
we get the expected result from topline
17 ?- eval(X, residual_info(size)).
X = 1.5421864522230475.
Note the declaration in ch20_utils (where eval is first seen):
:- multifile eval/2.
that allows to continue the definitions in some other module, but requires to qualify further definitions with ch20_utils identifier. Then the recursive calls will have chances find those definitions.
Note I moved the last clause from ch20_utils to ch20_Ires. This is required because that clause is a 'catchall' rule, thus inhibited inspection of ch20_Ires clauses: actually, I got
12 ?- eval(X, residual_info(size)).
ERROR: is/2: Arithmetic: `size/0' is not a function
before moving the clause...
Now, back to original problem, as you know operators are just syntax sugar:
:- module(ch20_Ires, []).
:- reexport([ch20_utils, ch20_examples]).
ch20_utils:(R <- residual_info(Attribute)) :-
findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
list_to_set(Values, Values_set),
setof(Class, Class^Attr^example(Class,Attr), Classes_set),
Values_prob_sum <- sum_list(Values_set, value_prob(Values, Classes_set, Attribute)),
R <- -Values_prob_sum, !.
...
% note: moved here from ch20_utils
ch20_utils:(R <- X) :- R is X, !.
and
:- module(ch20_utils,
[ op(900, xfy, <-),
(<-)/2
]).
:- multifile (<-)/2.
R <- log2(X) :- R <- log(X) / log(2), !.
R <- len(L) :- length(L, R), !.
Sum <- sum_list([], _) :- Sum <- 0.
Sum <- sum_list([H|T], Func) :-
New_sum <- sum_list(T, Func),
!, Func =..Func_list,
append(Func_list,[H],Apply_func_list),
Apply_func =..Apply_func_list,
Sum <- New_sum + Apply_func.
R <- X :- compound(X), X =..[OP,X2,X3], R2 <- X2, R3 <- X3, Expr =.. [OP,R2,R3], R is Expr, !.
% R <- X :- R is X, !.
seem to solve the syntax issue:
20 ?- X <- residual_info(size).
X = 1.5421864522230475.