2

If I have a function that takes a number of read only channels (for example for channel aggregation), why can't I call this function with a slice of channels, like

package main
func f(in ...<-chan int) {
    // do something
}

func main() {
    chList := []chan int{make(chan int), make(chan int)}
    f(make(chan int), make(chan int)) // works
    f(chList...) // cannot use chList (type []chan int) as type []<-chan int in argument to f
}

It seems I'm missing something fundamental but I can't figure out what. If the function can't take unidirectional channels, why can it take them when not in the first case?

olovp
  • 81
  • 6
  • 2
    That's how the type system works. `make(chan ...)` makes kinda "undirected" (not bidirectional, undirected) channel which is assignable to a directed channel while `chList` is a slice of bidirectional channels, unassignable to a receive only channel. You can do `chList := []<-chan int{make(chan int), make(chan int)}`. – Volker Nov 04 '16 at 09:29

2 Answers2

1

Ok, so it seems it has to do with lack of slice covariance in go. My solution is to type convert the slice of "nondirectional" channels into a slice of readonly channels. Full example below:

package main

import (
    "fmt"
    "time"
)

func f(in ...<-chan int) chan int {
    fmt.Println("number of channels:", len(in))
    out := make(chan int)
    for _, ch := range in {
        go func(ch <-chan int) {
            for i := range ch {
                out <- i
            }
        }(ch)
    }
    return out
}

func chConv(channels ...chan int) []<-chan int {
    ret := make([]<-chan int, len(channels))
    for n, ch := range channels {
        ret[n] = ch
    }
    return ret
}

func main() {
    chList := []chan int{make(chan int), make(chan int)}
    roChans := chConv(chList...)
    agg := f(roChans...)
    go func() {
        for i := range agg {
            fmt.Println("got:", i)
        }
    }()
    for i := 0; i < 10; i++ {
        for _, ch := range chList {
            ch <- i
        }
    }
    time.Sleep(1 * time.Second)
}
Community
  • 1
  • 1
olovp
  • 81
  • 6
0

you missed the direction,

package main
func f(in ...<-chan int) {
    // do something
}

func main() {
    chList := []<-chan int{make(<-chan int), make(<-chan int)}
    f(make(<-chan int), make(<-chan int)) 
    f(chList...) 
}
  • But then I can't send on the channels (send to receive-only type <-chan int). And why does it work when not putting them into a slice? – olovp Nov 04 '16 at 09:30
  • 1
    You should never `make` a directional channel, since it's pretty much useless. – JimB Nov 04 '16 at 18:55