Consider this program:
package main
import (
"fmt"
)
type Foo chan string
func main() {
c := make(Foo, 1)
c <- "gotcha"
fmt.Println(<-c)
}
All is well. Now try to declare a direction for a Foo:
package main
type Foo chan string
func main() {
var x <-Foo
}
// or
func x(c <-Foo) { }
This doesn't parse, but it's reasonable to expect it would and maybe, perhaps with parenthesization, easy to define without ambiguity. (A directional Foo with element type Foo could be tricky.)
Reported on twitter.
Isn't this kind of like doing
type MyInt int
var x uMyInt // Is this a uint?
i.e., I wouldn't expect this to work at all.
I'm tentatively marking this as a language change, unless you are suggesting that the current parser is not matching the written spec (in which case this is a bug).
I think it's a language change.
Definitively a language change, possibly backward compatible.
But why would we want to allow such a thing? It makes the direction arrow an operator on types, and only on channel types. And it only can make a bidirectional channel a read-only channel. What if I wanted to create a write-only channel? In the current design of the language <- arrow is really part of the channel syntax pattern, not a separate operator when it comes to channel types. The syntax is simply not compositional (we cannot interject a <- into the middle of a channel type).
I don't see why this would be worth the effort.
This is requesting similar syntax to https://github.com/golang/go/issues/22189.
I'm not saying it is or is not worth the effort, but it definitely a flaw in the design that one can declare read-only channel types only if they are unnamed. I can't think of another property of the language that works like this, for instance that changes the possible types acceptable by a function.
See also #23415.
Take a look at this playground example: https://play.golang.org/p/ih1Uxm4Gfna
package main
import (
"fmt"
)
type (
Foo chan string
FooReadable <-chan string
FooWritable chan<- string
)
func Send(f FooWritable, msg string) {
f <- msg
}
func Recv(f FooReadable) string {
return <-f
}
func Send2(f chan<- string, msg string) {
f <- msg
}
func Recv2(f <-chan string) string {
return <-f
}
func main() {
Scenario1()
Scenario2()
Scenario3()
Scenario4()
Scenario5()
}
func Scenario1() {
// works
//
foo := make(chan string)
// compile error
//
// foo := make(Foo)
go Send(foo, "message")
fmt.Println(Recv(foo))
}
func Scenario2() {
// works
//
// foo := make(chan string)
// works as well
//
foo := make(Foo)
go Send2(foo, "message2")
fmt.Println(Recv2(foo))
}
func Scenario3() {
foo := make(chan string)
reader := FooReadable(foo)
writer := FooWritable(foo)
go Send(writer, "message3")
fmt.Println(Recv(reader))
go Send2(writer, "message4")
fmt.Println(Recv2(reader))
}
func Scenario4() {
foo := make(Foo)
reader := (<-chan string)(foo)
writer := (chan<- string)(foo)
go Send(writer, "message5")
fmt.Println(Recv(reader))
go Send2(writer, "message6")
fmt.Println(Recv2(reader))
}
func Scenario5() {
temp := make(chan string)
foo := Foo(temp)
reader := (<-chan string)(foo)
writer := (chan<- string)(foo)
go Send(writer, "message7")
fmt.Println(Recv(reader))
go Send2(writer, "message8")
fmt.Println(Recv2(reader))
}
Is this expected behavior? It seems odd to me that type conversion can be done in one direction but not the other:
Foo can become chan string, <-chan string and chan<- stringchan string can become Foo, FooReadable and FooWritable<-chan string can become FooReadablechan<- string can become FooWritableFoo cannot become FooReadable or FooWritableIs the inability of Foo to be converted in this way intentional?
@mccolljr I don't think this proposal is the right place to discuss the details of Go type system. For the reasons for what you are seeing, see https://golang.org/ref/spec#Assignability. If you want to discuss this, see https://golang.org/wiki/Questions. If you want to propose a change, please use a different issue. Thanks.
@ianlancetaylor i apologize, I guess I was unclear. Please don't talk down to me, though.
I understand assignability and why you can't assign a named type to another named type without explicit conversion. I'm more concerned with why
https://play.golang.org/p/hqpgFfI26uG
...is legal, but explicitly converting a named channel type to a named directional channel type with the same underlying channel type (for ex. chan string) is forbidden, while the conversions, forced or otherwise, between the underlying type (chan string) and a named directional type (FooReadable or FooWritable) is perfectly legal as well.
The assignability rules in the spec don't exaclty make it clear to me that this is correct, and so my above post was meant to lead into that discussion.
It's relevant here because I was originally going to ask whether it would satisfy @davecheney to simply name the types for his receiving and sending directional channels similarly to his named bidirectional channel to offer the visual clarity. I was surprised that this particular conversion between channel types was impossible, given what else is legal in go.
I understand what has been said in this thread regarding why, as proposed, this is complex to add to the language lexically. I'm not sure I understand why this particular brand of conversion isn't allowed, though. I think that this conversion and the proposal are similar enough to warrant discussing this here.
@mccolljr I apologize for making you feel that I was talking down to you. That was in no way my intent.
That said, please keep this issue for discussion of the specific proposal above. Discussions of type assignability rules are simply not related to this proposal, which is not about assignability, or type conversions, at all. Thanks for your consideration.
An possible problem with this proposal might be ambiguity of the parse tree (requiring type information to resolve it). To be investigated.
Most helpful comment
Definitively a language change, possibly backward compatible.
But why would we want to allow such a thing? It makes the direction arrow an operator on types, and only on channel types. And it only can make a bidirectional channel a read-only channel. What if I wanted to create a write-only channel? In the current design of the language
<-arrow is really part of the channel syntax pattern, not a separate operator when it comes to channel types. The syntax is simply not compositional (we cannot interject a<-into the middle of a channel type).I don't see why this would be worth the effort.