# Is it possible to use channels as input/output ports?

**URL:** <https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038>\
**Category:** Questions & Answers\
**Created:** [May 27, 2022, 6:38pm UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038 "2022-05-27T18:38:49Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 27, 2022, 6:38pm UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/1 "2022-05-27T18:38:49Z")

</div>

I've been playing around with the idea of having channels that can be used like ports -- for example, (print ch) would become (channel-put ch) and (read ch) would become (channel-get ch).

I thought I might be able to get some traction via structs and custom ports:

```scheme
(define in (make-channel))
(define out (make-channel))
(define p1 (make-input-port 'in-port (lambda (b) (channel-get in))))
(define p2 (make-output-port 'in-port (lambda (b) (channel-put out))))
(struct in/out-ch (in out) #:prop:input-port p1 #:prop:output-port p2)

```

...sadly, that got me nowhere because `make-*-port` are onto my tricksy ways and won't allow it. Is there a way to do this? Not critical, but it's been nagging at me.

---

<div class="post-metadata">

**Author:** ![simonls](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/simonls/32/170_2.png) [@simonls](https://racket.discourse.group/u/simonls)\
**Post date:** [May 28, 2022, 12:09am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/2 "2022-05-28T00:09:59Z")

</div>

> [@dstorrs](#):
>
> `make-*-port` are onto my tricksy ways and won't allow it

Can you clarify what you mean by this? How does it fail?  
Your example uses 2 parameters, when I look at the documentation those functions take 4 required parameters and more optional ones. (Indicated by the brackets)  
Is this more a pseudo code question or did you expect your example to work?

Personally I haven't used custom ports yet and the functions are quite a bit to read and understand in the documentation, which is why I decided to not tackle this challenge today (can't get too distracted from my own stuff...). However if I did I would probably experiment a bit with one of the provided examples to get a better understanding how those functions work and then try to adapt those towards a solution.

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 28, 2022, 1:05am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/3 "2022-05-28T01:05:12Z")

</div>

Pseudo-code, yes. The issue is that the read procedure cannot return an arbitrary value. It must return:

- an exact positive integer
- eof
- a pipe input port
- a synchronizable event, except the result of the sync must ultimately be one of the other valid options
- a procedure of 4 arguments which can return anything except a character or eof and will raise an exn:fail:contract if called by some read functions

---

<div class="post-metadata">

**Author:** ![greghendershott](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/greghendershott/32/98_2.png) [@greghendershott](https://racket.discourse.group/u/greghendershott)\
**Post date:** [May 28, 2022, 1:47am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/4 "2022-05-28T01:47:31Z")

</div>

This is a neat idea. A speed bump, if it were me, would be that ports seem to be a significantly richer and more complicated abstraction --- with respect to ideas like blocking, buffering, and peeking --- than either channels or async channels?

(Maybe that's partly why defining custom ports feels intimidating?)

As a result, trying to build a port abstraction on top of a channel seems like it might be challenging? I'm not asserting impossible, I'm saying this is what would gnaw at me during the long, dark, tea-time of the soul. 🙂

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 28, 2022, 3:19am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/5 "2022-05-28T03:19:15Z")

</div>

Hm...channels (as opposed to async-channels) block and async channels buffer... async-channel-try-get isn't the same as peek and I'm not sure how to simulate that. I suppose it could offer a particular implementation of port functionality and it's fine as long as the customer knows what to expect.

---

<div class="post-metadata">

**Author:** ![simonls](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/simonls/32/170_2.png) [@simonls](https://racket.discourse.group/u/simonls)\
**Post date:** [May 28, 2022, 3:46pm UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/6 "2022-05-28T15:46:06Z")

</div>

> If peek is #f, then peeking for the port is implemented automatically in terms of reads, but with several limitations. First, the automatic implementation is not thread-safe. Second, the automatic implementation cannot handle [special](https://docs.racket-lang.org/reference/customport.html#%28elem._special%29) results (non-byte and non-eof), so read-in cannot return a procedure for a [special](https://docs.racket-lang.org/reference/customport.html#%28elem._special%29) when peek is #f. Finally, the automatic peek implementation is incompatible with progress events, so if peek is #f, then get-progress-evt and commit must be #f. See also [make-input-port/read-to-peek](https://docs.racket-lang.org/reference/port-lib.html#%28def._%28%28lib._racket%2Fport..rkt%29._make-input-port%2Fread-to-peek%29%29), which implements peeking in terms of read-in without these constraints.

Maybe setting peek to #f would be a good first try? Or alternatively the last sentence may be a good alternative. But as said, I haven't tried yet.

[make-input-port/read-to-peek](https://docs.racket-lang.org/reference/port-lib.html#%28def._%28%28lib._racket%2Fport..rkt%29._make-input-port%2Fread-to-peek%29%29) states:

> Similar to [make-input-port](https://docs.racket-lang.org/reference/customport.html#%28def._%28%28quote._~23~25kernel%29._make-input-port%29%29), but if the given read-in returns an event, the event’s value must be 0. The resulting port’s peek operation is implemented automatically (in terms of read-in) in a way that can handle special non-byte values. The progress-event and commit operations are also implemented automatically. The resulting port is thread-safe, but not kill-safe (i.e., if a thread is terminated or suspended while using the port, the port may become damaged).

I am guessing that it starts buffering whenever somebody tries to peek ahead or maybe it buffers it always and makes use of that (speculation, haven't looked at its implementation).

---

<div class="post-metadata">

**Author:** ![SamPhillips](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samphillips/32/15_2.png) [@SamPhillips](https://racket.discourse.group/u/SamPhillips)\
**Post date:** [May 29, 2022, 1:10am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/7 "2022-05-29T01:10:03Z")

</div>

I'm struggling to think of why I would want to use such an abstraction instead of just channels or a pipe. Is there a more complex use case that you see using this abstraction as desirable?

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 29, 2022, 1:49am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/8 "2022-05-29T01:49:28Z")

</div>

I often find myself wanting to have an in/out channel pair for communicating between a manager thread and a series of workers. A struct with two channels in it, where each worker gets a unique receive thread and a shared transmit thread would make a lot of sense, and being able to read/write to the struct would be more elegant than having to constantly dig fields out or write explicit read/write procedures.

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [May 29, 2022, 2:48am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/9 "2022-05-29T02:48:30Z")

</div>

Are your threads communicating using byte/character IO, or values? If the former, would pipes work for you? (In the sense of [13.1.7&nbsp;Pipes](https://docs.racket-lang.org/reference/pipeports.html) .)

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 29, 2022, 3:23am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/10 "2022-05-29T03:23:10Z")

</div>

General values. Obviously, any message can be encoded into a byte string, but it would be nice to be able to use a straightforward encoding.

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [May 29, 2022, 3:51am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/11 "2022-05-29T03:51:40Z")

</div>

If you're dealing with general values, I'm not sure why you want to get ports involved, rather than something like:

```scheme
#lang racket

(struct mailbox (in out))

(define (mailbox-send mb v)
  (channel-put (mailbox-out mb)))

(define (mailbox-recv mb)
  (channel-get (mailbox-in mb)))

```

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [May 29, 2022, 3:54am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/12 "2022-05-29T03:54:29Z")

</div>

Yes, clearly that works. As I said above, being able to read/write to the struct would be more elegant than having to constantly dig fields out or write explicit read/write procedures.

This isn't a "hey, here's a problem that cannot be solved in any other way", it's a "hey, here's a problem that would be elegantly solve in this particular way, is it possible to do that?"

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [May 29, 2022, 4:31am UTC](https://racket.discourse.group/t/is-it-possible-to-use-channels-as-input-output-ports/1038/13 "2022-05-29T04:31:42Z")

</div>

I would find it extremely confusing, but here's something that runs:

```scheme
#lang racket

(struct in/out-ch (in out)
  #:guard (λ (in-ch out-ch name)
            (define-values [in out]
              (make-pipe))
            (port-read-handler in (case-lambda
                                    [(in)
                                     (channel-get in-ch)]
                                    [(in name)
                                     (error 'in/out-ch "no read-syntax mode")]))
            (port-print-handler out (λ (v out)
                                      (channel-put out-ch v)))
            (values in out))
  #:property prop:input-port (struct-field-index in)
  #:property prop:output-port (struct-field-index out))

(define ch (make-channel))
(define in/out (in/out-ch ch ch))

(thread (λ ()
          (print (void) in/out)))

(void? (read in/out))

```
