Hello @sgodoyc,
I know I’m posting this a bit after the battle ^^ but I did spent a long time thinking about the same subjects. So, here is my take of the problem:
First, I believe your basic exercise contains not one but two rabbit hole ^^:
- the declarative aspect of manipulating a list, which is very well spelled out in your original post with the 4 different case.
- the declarative aspect of what an even numeric element is.
- your b1 and b2 cases hints that an even number is a binary mutually excusive test but no more information is given.
So let’s start the thought experiment by spelling out your four cases on the declarative use of list for filtering elements. Let’s admit that we have a even/1
predicate that is a binary mutually exclusive test of an even number:
filter_evens([], []).
filter_evens([X | Rest], Answer) :-
even(X),
filter_evens(Rest, Answer).
filter_evens([X | Rest], [X | Answer]) :-
not(even(X)),
filter_evens(Rest, Answer).
Next, how should even/1
be coded ? Well, let’s start with the most obvious:
even(X) :-
(X mod 2) =:= 0.
However, you can’t stop here. You need to ask yourself, what are the different possiblity of X
?
Traditionnaly, in programming, you would want to know the type of a variable.
In our case, it would be relevant to ask what this even/1
test would do if:
X
is a number: well, that’s easy, just compute the arithmetic and conditionalX
is not a number: there is two possibility here
i. fail: which mean thatfilter_evens/2
accepts non numeric entities inList
.
ii. raise a type error: meaning “I don’t know how to handle this, deal with it ^^”
The behavior of prolog arithmetic choose the 2.ii options and raise on non numeric values.
For option 2.i, you need to test the type of X first, which number/1
could do.
But you have another problem in prolog, which is groundness
:
X
is ground: well, you can just proceed with the type tests.X
is a variable: this means that X could be later instantiated to anything, a number or something else, there’s multiple solutions here:
i. raise an instantiation error: same as the type, refuse to proceed.
ii. use constraints: heh, I lied, there is 3 rabbit holes in this problem ^^
You will often find prolog code that treats variables as another type. For example, number/1
fails when given a variable. But from prolog point of view, this is not a pure declarative stance.
So I believe that without using constraints, you should either keep the previous definition of even/1
or if you want to be able to handle non-numeric ground elements, modifiy to this:
even(X) :-
( var(X)
-> instantiation_error(X)
; true
),
number(X),
(X mod 2) =:= 0.