mndrix / list_util

Prolog list utility predicates
The Unlicense
11 stars 5 forks source link

2-arity Goal argument for iterate/3? #16

Open mndrix opened 9 years ago

mndrix commented 9 years ago

During discussion with @eazar001 on #15, I realized that our current API for iterate/3 is perhaps more complicated than it should be. The Goal argument has arity 3. That allows one to make a distinction between state values and returned values. However, in most use cases the state value and the returned value are identical.

If Goal had arity 2, we'd get simpler code. For example:

integers(Z) :- iterate(succ,1,Z). % Z = [1,2,3,...]
repeat(X,Xs) :- iterate(=,X,Xs). % Xs = [X,X,X,...]

This seems more natural than our current API which requires an auxiliary predicate in each case.

For what it's worth, using Goal with arity 2 more closely emulates the successful iterate :: (a -> a) -> a -> [a] function from Haskell's Prelude.

eazar001 commented 9 years ago

I agree that having :Goal carry arity 2 makes things visually a lot easier. Perhaps making arity 2 the default for iterate/3, and having another version that works with a Goal/3, if the need arises to distinguish state from return value would be a good option.

mndrix commented 9 years ago

I'm having second thoughts about this. We now have three examples of a 3-arity Goal being very useful:

In all three cases, the state that's being iterated is different than the values being produced. I'd hate to lose this powerful ability unless we have to.

Maybe we could use reflection to get the 2-arity behavior when it's wanted. Something like:

iterate(Module:Goal,State,List) :-
    functor(Goal,Name,Arity),
    ( N is Arity+3, current_predicate(Module:Name/N) ->
        iterate_(Goal,State,List)
    ; N is Arity+2, current_predicate(Module:Name/N) ->
        iterate_(iterate_wrapper(Goal),State,List)
    ; otherwise ->
        throw(iterate_goal_bad_arity)
    ).

iterate_wrapper(Goal,X0,X,X) :-
    call(Goal,X0,X).
eazar001 commented 9 years ago

Incidentally, as I was working a little bit more with your iterate/3, I also started noticing just how powerful it was as well. Although your proposition seems like a good compromise, nonetheless.