Morning,
I thought I’d create a more generalised thread as a way to start the discussion about some parts of the stdlib that could then be moved into their own thread/issue or be resolved quickly if there was something of a consensus.
Process
The Process module provides a high-level API, but I wonder if it’s worth either clarifying the docs or standardising some of the methods. I was a bit hung up on trying to tee the contents of stdout & stderr (show in foreground and have access to it after completion) and get the exit code at the end.
While I realise it’s a bit of an edge case, I ended up with:
-
Process.new,outputanderrorasIO::Memoryobjects. - Tail the
IO::Memoryobjects with awhile Process.exists?loop. - Use
process.wait.exit_statusat the end (after I know the Process has already completed) to get the exit code.
(Some of this may be doable in a fiber but I didn’t know the blocking semantics for this kind of process and it wasn’t required in this simple example)
This was all totally workable, but since it’s a high-level API, I wondered if it would be worth tweaking so that:
- Provide a way of getting the
Process::Statusvia. a property instead of relying onwait, aswaitseemed a bit strange since I already knew the process had exited. - Standardise all of the main entrypoint methods (
.fork,.run,.new) to return aProcess- Currently, only.runreturns aProcess::Status - Clarify docs/arguments of
.forkas it seems to be impossible to get the contents ofoutputanderroras the process has forked?
IO
This one was kinda a mix bag - query, suggestion, request for input.
Does the current IO implementation - or child implementations, like File - provide a way to ‘stream’ (lazy-load) at a high level? Methods like .each_line would be a great candidate, or perhaps another abstraction that provides a Stream object which acts like a generator instead of loading in the entire contents into memory.
I’ve looked at the IO::Buffered docs and it looks like it should be doable with the current tools, but I’m not experienced in this kind of process - what’s a normal amount of bytes to read, do we rely on file encodings to determine when to break, etc.?
Just kinda wondering the current state and if anyone has any input in how we can get this implemented?
Path
Currently I find Path a useful module, coming from using Python’s pathlib, I feel like it gives a load of great, high-level abstractions.
I had a gripe with the syntax (Path[root].join(foo, bar)) but just realised that if I’d RTFM, I can use Path.new(root, foo, bar)
However, it seems a little odd that the likes of Dir do not accept a Path object. This is only a minor objection because the .to_s method fixes this issue, but it seems like a bit of a disconnect when the Path module is so useful, and it usually means going String -> Path -> String -> Dir - Is there any cause for a bit tighter integration? Or is it fine the way it is?
Now, I don’t want this to come across as negative in any way. So far, Crystal has been a pleasure to write.
But I am trying to start a meaningful discussion on parts of the stdlib that I think could be improved upon (rough edges), and I’m willing to contribute code and docs to so.
Looking forward to any feedback.
