Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What about a with_input_from/2 predicate?

53 views
Skip to first unread message

mauro....@neuroenergetics.org

unread,
Nov 17, 2014, 5:58:50 AM11/17/14
to
SWI Prolog provides the with_output_to/2 predicate, which is very useful for sending output to specific data structure (e.g., atom). To my knowledge, a predicate that conversely take input from a data structure does not exist.

Thus, it would be nice to have a sort of with_input_from/2 predicate. As an illustration, the behavior of such a predicate could be as follows:

?- with_input_from(atom('First line\nSecond line\n', Stream), read_line_to_codes(Stream, Line)), atom_codes(Atom, Codes).

Codes = [70, 105, 114 ... ]
Atom = 'First line'

I believe with_input_from/2 could be used instead of the annoying procedure of creating a temporary files.

Another example that I find relevant is the following (but there are many others):

?- Term = ':- write(\'Hello, world!\'), nl.', with_input_from(atom(Term, Stream), load_files(hello, [stream(Stream)]).

Comments are welcome.
M

P.S.: Do you know why qcompile/2 does not behave like load_files/2 when the option stream(Stream) is used? In particular, qcompile still looks for file "hello", while load_files/2 uses this atom only to reference the file.


Jan Burse

unread,
Nov 17, 2014, 6:37:05 AM11/17/14
to
Hi,

You can implement a non-concurrent solution by youself:

http://www.jekejeke.ch/idatab/doclet/prod/en/docs/05_run/15_stdy/07_compliance/07_diagnosis/01_runner/02_helper.html

Requires at least setup_call_clean/3 and the ISO standard.

But it writes and reads from predefined files. So it wouldn't
work multi-threaded. For a multi-threaded solution would need
temporary files or memory files.

But temporary files or memory files are not part of ISO standard.

Bye


mauro....@neuroenergetics.org schrieb:

cc.car...@gmail.com

unread,
Nov 17, 2014, 10:25:55 AM11/17/14
to
Il giorno lunedì 17 novembre 2014 11:58:50 UTC+1, mauro....@neuroenergetics.org ha scritto:
> SWI Prolog provides the with_output_to/2 predicate, which is very useful for sending output to specific data structure (e.g., atom). To my knowledge, a predicate that conversely take input from a data structure does not exist.
>
> Thus, it would be nice to have a sort of with_input_from/2 predicate. As an illustration, the behavior of such a predicate could be as follows:
>
> ?- with_input_from(atom('First line\nSecond line\n', Stream), read_line_to_codes(Stream, Line)), atom_codes(Atom, Codes).
>
> Codes = [70, 105, 114 ... ]
> Atom = 'First line'
>
> I believe with_input_from/2 could be used instead of the annoying procedure of creating a temporary files.

it's fun, I wrote the very same some years ago...

%% with_input_from(+Atom, +Reader)
%
% apply Reader to streamed Atom
%
with_input_from(Atom, Reader) :-
atom_to_memory_file(Atom, Handle),
open_memory_file(Handle, read, RMem),
call(Reader, RMem),
close(RMem),
free_memory_file(Handle).

mauro....@neuroenergetics.org

unread,
Nov 17, 2014, 5:00:40 PM11/17/14
to

The memory file approach is interesting, though a memory file handle (under SWI) has '$memory_file' as functor, and as such cannot be used where predicates require a stream (e.g., when using atom_to_memory_file/2 instead of open_memory_file/[3,4]).

However, while looking at the library(memfile) documentation, I noticed the predicate open_chars_stream/2 in library(charsio), which does not suffer from the above-mentioned limitation (btw, it is also already implemented).

Many thanks.
M

Ulrich Neumerkel

unread,
Nov 18, 2014, 12:10:35 PM11/18/14
to
mauro....@neuroenergetics.org writes:
>SWI Prolog provides the with_output_to/2 predicate, which is very useful for sending output to specific data structure (e.g., atom). To my knowledge, a predicate that conversely take input from a data structure does not exist.
>
>Thus, it would be nice to have a sort of with_input_from/2 predicate. As an illustration, the behavior of such a predicate could be as follows:
>
>?- with_input_from(atom('First line\nSecond line\n', Stream), read_line_to_codes(Stream, Line)), atom_codes(Atom, Codes).
>
>Codes = [70, 105, 114 ... ]
>Atom = 'First line'
>
>I believe with_input_from/2 could be used instead of the annoying procedure of creating a temporary files.
>
>Another example that I find relevant is the following (but there are many others):
>
>?- Term = ':- write(\'Hello, world!\'), nl.', with_input_from(atom(Term, Stream), load_files(hello, [stream(Stream)]).

Having a general with_input_from/2 encourages to rely even
more on I/O which is alien to Prolog's pure core.

Some way to load/compile programs more flexibly does make
some sense, probably everyone has done it in one way or
another.

mauro....@neuroenergetics.org

unread,
Nov 21, 2014, 6:56:22 AM11/21/14
to

>
> Having a general with_input_from/2 encourages to rely even
> more on I/O which is alien to Prolog's pure core.
>

I see.

Dealing with temporary data, the use of predicates requiring streams without actually creating a physical file is reminiscent of the use of lambda expressions to avoid defining a function. Isn't it?

Are lambda expressions also "alien to Prolog's pure core"?

Thanks.
M

Ulrich Neumerkel

unread,
Nov 21, 2014, 11:40:18 AM11/21/14
to
mauro....@neuroenergetics.org writes:
>> Having a general with_input_from/2 encourages to rely even
>> more on I/O which is alien to Prolog's pure core.
>>
>
>I see.
>
>Dealing with temporary data, the use of predicates requiring
>streams without actually creating a physical file is
>reminiscent of the use of lambda expressions to avoid
>defining a function. Isn't it?

That is a very far stretch.

>Are lambda expressions also "alien to Prolog's pure core"?

That depends whether or not they interfere with pure Prolog's
properties as monotonicity.



Jan Burse

unread,
Nov 21, 2014, 12:42:02 PM11/21/14
to
ulr...@mips.complang.tuwien.ac.at (Ulrich Neumerkel) schrieb:
> ?- Term = ':- write(\'Hello, world!\'), nl.',
> with_input_from(atom(Term, Stream), load_files(hello,
> [stream(Stream)]).

> Some way to load/compile programs more flexibly does make
> some sense, probably everyone has done it in one way or
> another.

I have the feeling that somebody is trolling here somebody
else, and he doesn't recognize it.

I guess it is not too much of a stretch to claim that it
is too far fetching to see the need of non-pure elements
to solve the problem of the op.

I feel the following predicates are 100% pure, they don't
have any side effects:

read_from_codes/2 (*)
call/1

So what the op tries above can be done without any load
files, it can be directly done as follows:

?- read_from_codes("write(\'Hello, world!\'), nl", X),
call(X).

Bye

(*)
http://prolog-commons.org/PrologCommons.html/codesio.html

Jan Burse

unread,
Nov 21, 2014, 12:43:44 PM11/21/14
to
Jan Burse schrieb:
> ?- read_from_codes("write(\'Hello, world!\'), nl", X),
> call(X).
>
> Bye
>
> (*)
> http://prolog-commons.org/PrologCommons.html/codesio.html

Even available in SWI-Prolog:
http://www.swi-prolog.org/pldoc/doc_for?object=codesio:read_from_codes/2

Jan Burse

unread,
Nov 21, 2014, 12:46:53 PM11/21/14
to
Hi,

Jan Burse schrieb:
> I feel the following predicates are 100% pure, they don't
> have any side effects:

Also reading a file is less a side-effect than writing a file.
Especially when the file handle is only used by one thread.

The only side-effect of reading a file is the advancement
of the file position. As a result reading a file is non-
communitative:

read(Stream,X),
read(Stream,Y).

Is not the same as:

read(Stream,X),
read(Stream,Y).

But the same also happens in DCG, the DCG (,)/2 is not commutation.
Yes non-pure Prolog programs are not commutative, but this
doesn't mean they don't have a semantics.

Bye

Jan Burse

unread,
Nov 21, 2014, 12:56:05 PM11/21/14
to
Jan Burse schrieb:
>
> But the same also happens in DCG, the DCG (,)/2 is not commutation.
> Yes non-pure Prolog programs are not commutative, but this
> doesn't mean they don't have a semantics.

Bottom line is, the predicates with_input_from/2 or
with_output_to/2 could be made pure as well. They
could also produce or consume strins, atoms, bytes
from arguments that are pure IO.

http://www.swi-prolog.org/pldoc/man?section=pureinput

mauro....@neuroenergetics.org

unread,
Nov 21, 2014, 1:03:17 PM11/21/14
to

Mine was nothing but an example.

I am putting aside the argument that anything can be done in many different ways (perhaps one more "pure" than another).

I think I have partly solved with open_chars_stream/2 (based on memory files, as stated above). Let's look at the following example (I am using SWI Prolog):

?- open_chars_stream("<clause><head>Head</head><body>Body</body></clause>", S), load_xml(stream(S), Contents, []).
S = <stream>(03139840),
Contents = [element(clause, [], [element(head, [], ['Head']), element(body, [], ['Body'])])].

What's wrong with it?

Ulrich Neumerkel

unread,
Nov 22, 2014, 4:18:04 PM11/22/14
to
You are allocating here a global resource, but you are not caring about
its proper deallocation. Try

repeat, open_chars_stream("x",S), fail.

and observe how resources get consumed. Particularly undesirable for
server processes.

Would load_xml be rather implemented as a DCG, you could use it directly, and in
case you want to read from a file there is phrase_from_file/2 that maintains
all stream-management.

Doing this yourself is possible, but very tricky. That's why it is
a good idea to delegate this as much as possible.

mauro....@neuroenergetics.org

unread,
Nov 22, 2014, 5:57:18 PM11/22/14
to

Yes, I forgot to close the stream in my example. Try the following and see nothing happens to system resources:

?- repeat, open_chars_stream("x", S), close(S), fail.

Again, the example above was, indeed, just an example. Of course, within the SGML/XML parser library there is a predicate that actually does what you are referring to. However, there are cases where the developers export only a subset of predicates and one is obliged to hack the code to use hidden features. In the best of cases this is equivalent to use non-public predicates (i.e., with explicit module qualification, which is not what the developers probably would agree with), whereas the solution making use of open_chars_stream/2 (for example) doesn't appear to suffer from this limitation.



mauro....@neuroenergetics.org

unread,
Nov 22, 2014, 6:03:26 PM11/22/14
to

PS: Well, I guess the correct way to use open_chars_stream/2 is as follows (Goal should be instantiated, of course):

setup_call_cleanup(open_chars_stream("x", S), Goal, close(S))

This would guarantee deallocation of resources even upon error/failure.

Jan Burse

unread,
Nov 23, 2014, 5:43:51 AM11/23/14
to
mauro....@neuroenergetics.org schrieb:
>
> PS: Well, I guess the correct way to use open_chars_stream/2 is as follows (Goal should be instantiated, of course):
>
> setup_call_cleanup(open_chars_stream("x", S), Goal, close(S))
>
> This would guarantee deallocation of resources even upon error/failure.
>

setup_call_cleanup/3 is only needed if you have
cuts or exceptions in the continuation. Otherwise
you could also use:

open_chars_stream("x", S),
catch((Goal, close(S)), _, close(S)).


The above would catch exceptions inside Goal. But
usually setup_call_cleanup/3 is the better idea
since there are often cuts or execptions in
the continuation.

SWI-Prolog also offers a combination of catch/3
and setup_call_cleanup/3, which allows you to
analyse the event causes the calling of the
cleanup goal.

It is called setup_call_catcher_cleanup/4.
http://www.swi-prolog.org/pldoc/doc_for?object=setup_call_catcher_cleanup/4

Bye

Ulrich Neumerkel

unread,
Nov 23, 2014, 6:02:25 AM11/23/14
to
Jan Burse <janb...@fastmail.fm> writes:
>setup_call_cleanup/3 is only needed if you have
>cuts or exceptions in the continuation. Otherwise
>you could also use:
>
> open_chars_stream("x", S),
> catch((Goal, close(S)), _, close(S)).

How can you tell whether or not you have exceptions? There
might be a resource error or an interrupt in between.
Further, catching with _ means you are masking this exception.

Managing resources is tricky.

Jan Burse

unread,
Nov 23, 2014, 8:41:21 AM11/23/14
to
ulr...@mips.complang.tuwien.ac.at (Ulrich Neumerkel) schrieb:
Of course don't use the catch/3, the setup_call_cleanup/3
is much better for many reasons. For example it will not
accept interrupts when in transit from the open statement
and to the chatch envelope.

Also the catch formulation above assumes that Goal is
deterministic, whereas setup_call_cleanup/3 can also
deal with non-deterministic goals. I think Prolog
can't formulate easily what setup_call_cleanup/3 does.

Bye

0 new messages