Peeking at channel content without consuming

8,932 views
Skip to first unread message

Eleanor McHugh

unread,
Mar 25, 2011, 8:36:55 AM3/25/11
to golang-nuts
I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?

And is there actually a way to access the buffer memory directly?


Ellie

Eleanor McHugh
Games With Brains
http://feyeleanor.tel
----
if _, ok := reality.(Reasonable); !ok { panic(reality) }

Jessta

unread,
Mar 25, 2011, 8:51:40 AM3/25/11
to Eleanor McHugh, golang-nuts
On Fri, Mar 25, 2011 at 11:36 PM, Eleanor McHugh
<ele...@games-with-brains.com> wrote:
> I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?
>
> And is there actually a way to access the buffer memory directly?

The most obvious reason is because it's not thread safe.
Peeking at an item that may not be there when you actually go to read
it, isn't very useful.

You could always read items off the channel and put them back on
again. If you're not interested in the them.

- jessta
--
=====================
http://jessta.id.au

roger peppe

unread,
Mar 25, 2011, 9:36:58 AM3/25/11
to Jessta, Eleanor McHugh, golang-nuts
On 25 March 2011 12:51, Jessta <jes...@jessta.id.au> wrote:
> On Fri, Mar 25, 2011 at 11:36 PM, Eleanor McHugh
> <ele...@games-with-brains.com> wrote:
>> I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?

a synchronous channel has no head (it's like a zero-length slice)

>> And is there actually a way to access the buffer memory directly?

> The most obvious reason is because it's not thread safe.
> Peeking at an item that may not be there when you actually go to read
> it, isn't very useful.

that's true, but is also true of len of a channel.

> You could always read items off the channel and put them back on
> again. If you're not interested in the them.

you can't do that because a) it gets put back at the tail
of the queue rather than the end and b) a writer may have
got there first, so the put may block.

i have wanted a feature like this in the past. it can make
sense when there is only one consumer of the channel
(i wanted it to peek at the first mouse event to do hit
testing before deciding whether to consume it)

you can simulate it with a process acting as intermediary, but
you'd have to do it for each channel type or lose type safety.

on balance i think the need for this is small enough, and the
potential for abuse large enough that it's not a feature that
should be added.

Eleanor McHugh

unread,
Mar 25, 2011, 11:30:36 AM3/25/11
to golang-nuts
On 25 Mar 2011, at 13:36, roger peppe wrote:
> On 25 March 2011 12:51, Jessta <jes...@jessta.id.au> wrote:
>> On Fri, Mar 25, 2011 at 11:36 PM, Eleanor McHugh
>> <ele...@games-with-brains.com> wrote:
>>> I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?
>
> a synchronous channel has no head (it's like a zero-length slice)

True, but conceptually there's still either a value which can be read or there isn't so there's a dimensionality there - at least enough for a Head() call which on asynchronous channels would be At(0) and on synchronous channels wouldn't.

>>> And is there actually a way to access the buffer memory directly?
>
>> The most obvious reason is because it's not thread safe.
>> Peeking at an item that may not be there when you actually go to read
>> it, isn't very useful.
>
> that's true, but is also true of len of a channel.
>
>> You could always read items off the channel and put them back on
>> again. If you're not interested in the them.
>
> you can't do that because a) it gets put back at the tail
> of the queue rather than the end and b) a writer may have
> got there first, so the put may block.
>
> i have wanted a feature like this in the past. it can make
> sense when there is only one consumer of the channel
> (i wanted it to peek at the first mouse event to do hit
> testing before deciding whether to consume it)

My use case also has one consumer which is why my code's naturally pushing in this direction.

> you can simulate it with a process acting as intermediary, but
> you'd have to do it for each channel type or lose type safety.
>
> on balance i think the need for this is small enough, and the
> potential for abuse large enough that it's not a feature that
> should be added.

I agree it has great potential for abuse (it seems most of the things I'm interested in do lol) but there is an occasional need and I don't think it's unreasonable that reflection should support this in some way - even if there are lots of restrictions and caveats.

roger peppe

unread,
Mar 25, 2011, 11:39:16 AM3/25/11
to Eleanor McHugh, golang-nuts
On 25 March 2011 15:30, Eleanor McHugh <ele...@games-with-brains.com> wrote:
> On 25 Mar 2011, at 13:36, roger peppe wrote:
>> On 25 March 2011 12:51, Jessta <jes...@jessta.id.au> wrote:
>>> On Fri, Mar 25, 2011 at 11:36 PM, Eleanor McHugh
>>> <ele...@games-with-brains.com> wrote:
>>>> I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?
>>
>> a synchronous channel has no head (it's like a zero-length slice)
>
> True, but conceptually there's still either a value which can be read or there isn't

not really - there's can be a select at the other end of the channel;
whether there's a value or not depends on which arm of the select
is chosen.

remember there is no buffering, so if you peek at a value,
you'd have to get the value from the other side, which would
be equivalent to reading it - but that's wrong because peeking should
have no side
effects.


> I agree it has great potential for abuse (it seems most of the things I'm interested in do lol) but there is an occasional need and I don't think it's unreasonable that reflection should support this in some way - even if there are lots of restrictions and caveats.

if it's supported by reflection, it should be supported in non-reflection code.

out of interest, how would you wish to use this feature?

Eleanor McHugh

unread,
Mar 25, 2011, 11:49:07 AM3/25/11
to golang-nuts
On 25 Mar 2011, at 15:39, roger peppe wrote:
> On 25 March 2011 15:30, Eleanor McHugh <ele...@games-with-brains.com> wrote:
>> On 25 Mar 2011, at 13:36, roger peppe wrote:
>>> On 25 March 2011 12:51, Jessta <jes...@jessta.id.au> wrote:
>>>> On Fri, Mar 25, 2011 at 11:36 PM, Eleanor McHugh
>>>> <ele...@games-with-brains.com> wrote:
>>>>> I'm currently mucking with a channel-based generator via reflection and I was wondering whether or not there was a good reason for not including something like At(x) to peek at the contents of an item in the channel buffer (or just at the head in the case of a synchronous channel) without consuming those contents?
>>>
>>> a synchronous channel has no head (it's like a zero-length slice)
>>
>> True, but conceptually there's still either a value which can be read or there isn't
>
> not really - there's can be a select at the other end of the channel;
> whether there's a value or not depends on which arm of the select
> is chosen.
>
> remember there is no buffering, so if you peek at a value,
> you'd have to get the value from the other side, which would
> be equivalent to reading it - but that's wrong because peeking should
> have no side
> effects.

I hadn't thought that one through properly :)

>> I agree it has great potential for abuse (it seems most of the things I'm interested in do lol) but there is an occasional need and I don't think it's unreasonable that reflection should support this in some way - even if there are lots of restrictions and caveats.
>
> if it's supported by reflection, it should be supported in non-reflection code.
>
> out of interest, how would you wish to use this feature?

Well today specifically I'm testing whether or not a channel is the same as its embedded, reflected ChanValue - which obviously it is, but I just want some way of confirming that. Being able to directly compare the values queued at equivalent positions via the two access mechanisms would be a way of doing that without slightly fewer hardwire constants. This is certainly not a good reason for changing the language but it did set my mind thinking and now my unconscious mind is intimating that being able to do a peek might also be handy in my VM architecture work.

And yes, my unconscious mind is probably wrong.

Steven

unread,
Mar 25, 2011, 11:51:51 AM3/25/11
to Eleanor McHugh, golang-nuts
You could do something like the following, though without generics,
you'd have to wrap the channels in reflect in order to make it work
for arbitrary types.

type PeekChanInt struct {
in <-chan int
out chan int
}

func NewPeekChanInt(in <-chan int) *PeekChanInt {
pch := PeekChanInt{in, make(chan int, 1)}
go forwardVal(pch.out, pch.in)
return &pch
}

func (pch *PeekChanInt) Peek() (val int, ok bool) {
val, ok = <-pch.out
if ok {
pch.out <- val
}

return
}

func (pch *PeekChanInt) Get() (val int, ok bool) {
val, ok = <-pch.out
if ok {
go forwardVal(pch.out, pch.in)
}

return
}

func forwardVal(dst chan<- int, src <-chan int) {
val, ok := <-src
if ok {
dst <- val
} else {
close(dst)
}
}

Chip Camden

unread,
Mar 25, 2011, 12:36:54 PM3/25/11
to golang-nuts
Quoth Eleanor McHugh on Friday, 25 March 2011:

>
> I agree it has great potential for abuse (it seems most of the things I'm interested in do lol) but there is an occasional need and I don't think it's unreasonable that reflection should support this in some way - even if there are lots of restrictions and caveats.
>
Using "potential for abuse" as a reason to exclude a feature has great
potential for abuse.

--
.o. | Sterling (Chip) Camden | http://camdensoftware.com
..o | ster...@camdensoftware.com | http://chipsquips.com
ooo | 2048R/D6DBAF91 | http://chipstips.com

Eleanor McHugh

unread,
Mar 25, 2011, 12:43:46 PM3/25/11
to golang-nuts
On 25 Mar 2011, at 15:51, Steven wrote:
> You could do something like the following, though without generics,
> you'd have to wrap the channels in reflect in order to make it work
> for arbitrary types.

I quite like that bit of code. Hadn't really thought of the easy-fit pipe connector but if it works in plumbing it should work in software as well :)

roger peppe

unread,
Mar 25, 2011, 1:18:01 PM3/25/11
to Steven, Eleanor McHugh, golang-nuts
On 25 March 2011 15:51, Steven <stev...@gmail.com> wrote:
> You could do something like the following, though without generics,
> you'd have to wrap the channels in reflect in order to make it work
> for arbitrary types.

the principle is sound, but i'm not sure it's necessary to
start a goroutine for every channel send.
how about something like this (untested)?

a deeper objection to this API is that you can no longer use
the channel in a select, although a more sophisticated piece
of code could enable it.

type PeekChanInt struct {
in <-chan int

v int
ok bool
}

func NewPeekChanInt(in <-chan int) *PeekChanInt {

return &PeekChanInt{in: in}
}

func (p *PeekChanInt) Peek() (int, bool) {
if !p.ok {
p.v, p.ok = <-p.in
}
return p.v, p.ok
}

func (p *PeekChanInt) Get() (int, bool) {
if p.ok {
p.ok = false
return p.v, true
}
v, ok := <-p.in
return v, ok
}

Steven

unread,
Mar 25, 2011, 1:37:01 PM3/25/11
to roger peppe, Eleanor McHugh, golang-nuts

I thought of this too, but I was concerned about checking p.ok, and
then a couple instructions later, retrieving it's value, by when it
might not be okay. Of course, the peek might not be useful if there
are multiple readers anyway, for the reasons previously discussed, so
this may have been an unnecessary concern. I suppose you could use a
mutex to protect it without a go routine if necessary.

roger peppe

unread,
Mar 25, 2011, 1:43:29 PM3/25/11
to Steven, Eleanor McHugh, golang-nuts
On 25 March 2011 17:37, Steven <stev...@gmail.com> wrote:
> I thought of this too, but I was concerned about checking p.ok, and
> then a couple instructions later, retrieving it's value, by when it
> might not be okay. Of course, the peek might not be useful if there
> are multiple readers anyway, for the reasons previously discussed, so
> this may have been an unnecessary concern. I suppose you could use a
> mutex to protect it without a go routine if necessary.

if there are multiple readers of the channel, then your original
code will exhibit the same behaviour.

if there are multiple users of the PeekChanInt, that's another matter - it
doesn't make sense to use it concurrently.

Reply all
Reply to author
Forward
0 new messages