“take” should leave what is left of the input pipe behind
The take command, when given only one argument (take $n), behaves like take $n (all). In other words, it consumes the entire input pipe and returns the initial $n items (⬥ is my prompt):
⬥ put a b c | { take 1; put ----; all }
▶ a
▶ ----
⬥
In the above example, I think the all command should get b and c. In other words, take $n should return the $n first items, and leave the rest for later commands to consume.
If the current behaviour is desired, one can always use take $n (all) or follow take $n by nop (all).
Note that the Elvish documentation says the take command was inspired by the Haskell function of the same name. That documentation is unclear on what happens if take $n is shorter than the available values but implies the remaining values are left for subsequent functions. I've never written even a trivial "hello world" program in Haskell so I don't know how itstake function behaves. Nonetheless, the proposal by @hanche seems reasonable to me since it less likely to be confusing.
The take function in Haskell belongs to the pure part of the language, i.e., outside of the IO monad. In that context, the notions of “consuming” values or “leaving them behind” (or not) are meaningless. In other words, the second argument is left unchanged by the function, as it has to be in a functional language.
Elvish's two argument take is exactly the same. It is functional in nature, just as Haskell's.
My proposal is about the single argument take, which is a quite different beast, as it does IO and as such has side effects. It has nothing in common with the two argument take other than the name and a small bit of semantics. Moreover, my proposal is all about the side effects.
Supporting this requires take to stop working for byte input and is thus a breaking change.
Looking at the search results, most of the code should be OK. An exception is the first take here:
https://github.com/zzamboni/elvish-modules/blob/0f176b02378912f1d547f2b9249016fb27a1e4ed/git-vcsh.elv#L26
If you say so – I don't see why, but assume it is because of the way structured pipes work? At least, I don't see any obvious reason why it couldn't work with byte input, in principle if not in practice.
take does not know whether the first input will come from the value channel or the byte pipe, so it has to listen to both at the same time; this part isn't hard. But it also has to terminate as soon as one of them has a value and avoid reading the other; this part doesn't seem to be possible.
Happy to be the counterexample 😉. I think that code should be pretty easy to change to use value inputs or to store the output of the command before and then run take on that. I don't even use vcsh anymore, that was one of the first modules I wrote way back then.
This issue needs to be considered when implementing a get builtin.
I was just revisiting this, and noticed a broken link in the previous comment: The reference to a new get builting was surely intended to point at #1054. (Not hard to figure out how that happened.)