Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Channel{T}(f::Function) constructors. #30855

Merged
merged 6 commits into from
Aug 6, 2019

Conversation

NHDaly
Copy link
Member

@NHDaly NHDaly commented Jan 27, 2019

The existing Channel constructors are a bit inconvenient to work with. I love that you can create a Channel and spawn a task at the same time; it's super convenient! But the task-spawning constructor is a bit more cumbersome than the non-task-spawning constructor, because it takes its params as keyword arguments instead of the nicer way that the non-task constructor does it.

This PR adds two convenience constructors to combine those styles, to allow you to write things like this:

Channel{Char}(1) do chnl
    for c in "hello world"
        put!(chnl, c)
    end
end

The Channel constructors that do and don't create a Task are currently
written in different styles:

  • The non-Task constructor takes a Type {T} as a type parameter, and
    the size as a positional argument.

    • Channel{Int}(Inf)
    • Channel(0)
  • The Task constructor takes a Function, but the size and type are
    keyword arguments:

    • Channel(c->put!(c,0), ctype=Int, csize=0)
    • Channel(ctype=Int, csize=0, taskref=t) do c; put!(c,0); end

This commit adds convenience methods to the Task-creating constructor,
allowing you to specify the Type and (optionally) size as in the non-Task constructor:

  • Channel{Char}(c->put!(c, 'a'))
  • Channel{Char}(1) do c; put!(c, 'a'); end
  • Channel{Char}(csize=2, taskref=t) do c; put!(c, 'a'); end
  • Channel{Char}(c->put!(c, 'a'), Inf)

It also adds default constructors, defaulting to unbuffered channels (ie sz=0):

  • Channel() = Channel(0)
  • Channel{T}() where T = Channel{T}(0)

I also added tests for the existing Task-creating constructors, which weren't explicitly tested before.


EDIT: Some other things to consider:

  • In this PR, I kept it so that the empty constructor, Channel() is not valid. I think that's probably still reasonable, although one could argue that this should simply use all the defaults -- ie Channel() = Channel{Any}(0). But I dunno, it's probably fine as-is.
  • Let me know if the tests are reasonable.

@NHDaly
Copy link
Member Author

NHDaly commented Jan 27, 2019

This came up when i was working on adding examples to my CspExamples.jl repo, such as NHDaly/CspExamples.jl#2 and NHDaly/CspExamples.jl#4.

In the tests, it's a bit cumbersome to create new channels and fill them, which led me to want to add these constructors.

Copy link
Member

@vchuravy vchuravy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like a good enhancement

base/channels.jl Show resolved Hide resolved
@NHDaly
Copy link
Member Author

NHDaly commented Jan 29, 2019

Also, while we're here talking about this, I wonder if we should reevaluate that currently Channel{Int}() is disallowed. I think having this default to csize=0 might be beneficial. (And I guess relatedly then, Channel() would default to Channel{Any}(0).)

After reading a lot more about coroutines in Go, it sounds like you're almost always supposed to use an unbuffered channel (our Channel(0)), and the times where you need a buffered channel are rare and complex, and usually mean you're making things harder than they need to be. Since our constructor forces you to pick a number for the normal case, we leave people open to potential foot-guns. For example, before reading lots about it, I assumed that choosing Inf was a sane default unless I had reason to believe otherwise, thinking it seemed the "safest".

So I think this is a good opportunity to get ahead of potential bad practice. (That said, I'm fork this discussion into another issue/PR if you don't want to discuss it here, but this seems like as good a place as any! 😄)

@vchuravy
Copy link
Member

vchuravy commented Jan 29, 2019 via email

@NHDaly NHDaly force-pushed the Channel-constructors branch 3 times, most recently from 126ae69 to fbd248c Compare July 1, 2019 21:26
The Channel constructors that do and don't create a Task are currently
written in different styles:

 - The non-Task constructor takes a Type {T} as a type parameter, and
 the size as a positional argument.
    - `Channel{Int}(Inf)`
    - `Channel(0)`

 - The Task constructor takes a Function, but the size and type are
keyword arguments:
    - `Channel(c->put!(c,0), ctype=Int, csize=0)`
    - `Channel(ctype=Int, csize=0, taskref=t) do c; put!(c,0); end`

This commit adds convenience methods to the Task-creating constructor,
allowing you to specify the Type and/or size as in the non-Task
constructor:
    - `Channel{Char}(1) do c; put!(c, 'a'); end`
    - `Channel{Char}(csize=2, taskref=t) do c; put!(c, 'a'); end`

---

Also adds tests for the existing Task-creating constructors, which
weren't explicitly tested before.
Add unit tests for default constructor
I copy/pasted from my terminal, and I had a custom output prompt set via
OhMyREPL that I didn't notice.
@NHDaly NHDaly closed this Jul 14, 2019
@NHDaly NHDaly reopened this Jul 14, 2019
@NHDaly
Copy link
Member Author

NHDaly commented Jul 14, 2019

PTAL: This should be good to review again!

I have added compat statements for julia 1.3, and set the default Channel() constructor to default to unbuffered channels of Any.

@NHDaly
Copy link
Member Author

NHDaly commented Aug 5, 2019

Please take another look 😄

I know that feature-freeze for 1.3 is coming up soon, and I feel like since this is going to be the multithreading release, it would be nice to have a user-friendly interface on Channels.

Hopefully this can be resolved before feature freeze? :) Thanks!

@vchuravy vchuravy merged commit e891bca into JuliaLang:master Aug 6, 2019
@vchuravy
Copy link
Member

vchuravy commented Aug 6, 2019

Ah sorry should have been squashed -- mea culpa.

@NHDaly NHDaly deleted the Channel-constructors branch August 6, 2019 16:32
@@ -105,6 +129,12 @@ function Channel(func::Function; ctype=Any, csize=0, taskref=nothing)
isa(taskref, Ref{Task}) && (taskref[] = task)
return chnl
end
function Channel{T}(f::Function, sz=0) where T
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes a method overwrite warning. This signature can be Channel{T}(f::Function, sz).

Copy link
Member Author

@NHDaly NHDaly Aug 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, sorry! I opened #32818 to fix it!

Thanks, good catch! Sorry I didn't notice (the error comes up while building and it's easy to miss. I'll keep an eye out for that in the future!) :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants