chicken-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] Working with ports


From: Peter Bex
Subject: Re: [Chicken-users] Working with ports
Date: Mon, 21 Oct 2013 21:49:24 +0200
User-agent: Mutt/1.4.2.3i

On Mon, Oct 21, 2013 at 11:13:03PM +0400, Loïc Faure-Lacroix wrote:
> Being new to scheme, I hardly understand in what are ports superior than 
> having a file handle and accessing files using a file descriptor.

They are superior in that they are an abstraction over plain file
descriptors.  In some Schemes this is taken to an extreme: for
example Gambit offers a "directory port" from which you can use
Scheme's READ procedure to read the next file.  CHICKEN currently
does not go this far, but there are some examples of eggs which
wrap customised stuff in ports.  For example, the intarweb egg
has a way of encapsulating the logic of sending and receiving
chunked HTTP response bodies, and presenting the data as a
continuous stream of bytes, completely transparently to the user.
This allows you to use regular reading procedures regardless of
what the underlying data looks like.

I hope this gives you some inspiration and insight into why this
is a good idea :)

> In chicken, there is the posix unit that warps usual functions to work with 
> files. I would expect these functions to work very well. 

They're actually too low-level for my tastes.  They don't deal well with
error situations, and they do not integrate with the scheduler at all,
blocking all threads while reading/writing.

> While reading here and there the documentation, I found this:
> 
> with-output-to-port and with-input-from-port which rebind current-output-port 
> and current-input-port to the one passed to the function.
> 
> I then wrote this macro:
> 
> (define-syntax with-output-file
>   (syntax-rules ()
>     ((_ file body ...)
>      (call-with-output-file file (lambda (port)
>        (with-output-to-port port
>                             (lambda ()
>                               body ...)))))))

This is almost available as the standard procedure
with-output-to-file:

(with-output-to-file "hello.txt"
  (lambda ()
    (print "Hi, there!")))

The only difference is that this is more efficient and it's
not a macro, which makes it more flexible (as cool as macros
are, Schemers tend to avoid them like the plague, unless they
add a substantial amount of convenience or are required for
other reasons, like changing the evaluation rules)

> and wrote this function:
> 
> ; Dump changes to current-output-port
> (define (dump-changes files)
>   (for file in files
>     (display (string-append (number->string (file-change-time file)))
>     (display ":")
>     (display file)
>     (newline))))
> 
> As you can see, the function is using display to "current-output-port" which 
> lead me to this:
> 
> (define (save-changes file pattern)
>   (with-output-file file
>     (dump-changes (glob pattern)))
> 
> I'm not sure if I'm doing it right.

This is good, but it can be better if you avoid the macro and use
the standard procedure I mentioned before.

> Usually, I'd write a function that receive a port as a parameter explicitly 
> and read/write directly to the port. In this case, it is not clear that a 
> user would have to capture the output   by rebinding the current-output-port. 
> But the function that does the work is independent of the port that can be 
> used. I'm wondering if this is a good way to write code in scheme.

I'd say it's usually more convenient to pass the port, as it's slightly
more performant and convenient if you already have a port object (and
generally you do).  If it's a procedure that you'd expect the user to
use a LOT, and mostly on the current output port, then I'd say make
the port optional or hardcode it to use current-output-port.

Personally, I prefer both.  http-client for example provides both
with-input-from-request (changes current-input-port and
current-output-port implicitly) and call-with-input-request (which
passes the input/output ports to the reader/writer procedures).
This is consistent with R5RS, which also provides both
with-{input-from,output-to}-file and call-with-{input,output}-file.
It's trivial to implement, and leaves the choice to the user's
preferences and situational needs.

> Being from python, we often say that something is or isn't pythonic. 

In Scheme, we often say something is or isn't Schemely, but Schemers
tend to argue more about what that really means than Pythonistas ;)
There are a few distinct styles of writing Scheme code.  You'll know
what I mean when you've encountered a few existing codebases.  Until
then, just try to stick with the style that's constistent with what
you see in the R5RS document (and some SRFIs).  And try to isolate
your side-effects as much as possible.  Referential transparency is
the killer feature of functional programming, IMHO.

If in doubt, join us on IRC and we can discuss it in a lively
(but friendly!) debate :)

Cheers,
Peter
-- 
http://www.more-magic.net



reply via email to

[Prev in Thread] Current Thread [Next in Thread]