rhombus-prototype
rhombus-prototype copied to clipboard
Initial Path integration
Some initial Path integration that I've been poking at for a couple of weeks now.
Initially, I used a veneer for Path.read_with and Path.write_with that were syntax and would take a block that would become the read (write) procedure. Once I started thinking about adding the other flags for reading and writing the syntax seemed too messy so I went back to a simple wrapper around call-with-input-file/call-with-output-file.
Crazy idea: what if we want RAAI-style with a Closeable.def form?
block:
Closeable.def in = Port.Input.open_file("x")
....
// `in.close()` is added to the end here
The Closeable.def form could be implemented roughly like this:
namespace Closeable:
export:
rename cdef as def
defn.sequence_macro 'cdef $bind ... = $rhs
$body
...':
values(
'
block:
def mutable resource = #false
try:
~initially:
resource := $rhs
block:
let $bind ... = resource
$body
...
#void
~finally:
resource.close
',
'')
The closeable definition is neat; I would probably want it to return the value of the last expression in the body, personally.
RAII isn't that crazy and I intended to have a more general form. I still forget sometimes that Rhombus macros can see subsequent forms in the same context. I'll take a look at implementing this.
I'm thinking that next we may want to define a Closeable interface that classes can opt into.
I agree with Ben that it might be better to return the last value instead of #void. Also, would using Closeable.defcause the final expression of the block to not be a tail call?
Yes, it should definitely return the value. I needed #void to cover the empty-remainder case, but didn't think through that it should be at the beginning, not the end.
Another issue with this macro implementation is that static info from $rhs should flow to resource. Maybe statiic info will have to be propagated explicitly by Closeable.def, forcing expansion of $rhs and using statinfo_meta.gather.
Note that as written, $rhs will be evaluated with breaks disabled. I think that's ok — anything not meant to be in non-breaking mode can be lifted out — but users of Closeable.def would need to be aware. Disabling breaks was intentional here to ensure that either the resource is closed on escape or it was not allocated (as long as the running thread isn't killed).
I agree about having a Closeable interface that other classes can implement, and maybe Closeable.def should require a Closeable object. The fact that close isn't called as a method above is a bug, of course.
I've been writing some path-related code recently, so this is helpful. How much of the racket/path library should be exposed as Path methods? Do we want something minimal here, or something that exposes basically all of racket/path (and maybe much of racket/file)?
Added the Closeable interface and Closeable.def syntax. Feedback appreciated.
@samth wrote:
I've been writing some path-related code recently, so this is helpful. How much of the
racket/pathlibrary should be exposed asPathmethods? Do we want something minimal here, or something that exposes basically all ofracket/path(and maybe much ofracket/file)?
I've looked at what other languages (specifically the Python pathlib) have and slowly added those as I've needed them.
Is there value in keeping Path.read_with and Path.write_with?
I pushed some adjustments to Closeable, since the interplay between Racket and Rhombus layers there is tricky.
Is there value in keeping
Path.read_withandPath.write_with?
I'm in favor of dropping those.
BesidesCloseable, it looks like to_complete_path is not yet documented, and probably that should be to_absolute_path.
Should AbolutePath and RelativePath be annotations (which are easier to use in patterns)? And then drop Path.is_absolute?
I added one more operation (Path.only)
Incidentally, here are the current path/file-related racket imports in the file I'm working on:
import lib("racket/base.rkt") as r:
expose #{link-exists?} as link_exists
expose #{directory-list} as directory_list
expose #{rename-file-or-directory} as rename_file_or_directory
import lib("racket/file.rkt") open:
expose #{write-to-file} as write_to_file
expose #{make-temporary-file} as make_temporary_file
expose #{delete-directory/files} as delete_directory_files
expose #{make-directory*} as make_directory_alt
I added one more operation (
Path.only)
It was initially unclear to me what that method is supposed to do. Could we use a different name for that? In the shell that would be similar to the dirname operation. In Python pathlib it's close to Path.parent.
Should
AbolutePathandRelativePathbe annotations (which are easier to use in patterns)? And then dropPath.is_absolute?
At first I had made those as annotations. But they didn't seem as useful since they were just predicate annotations and I could just use the predicate directly. I could see them being useful in match and with is_a instead of calling the predicate methods.
expose #{make-directory*} as make_directory_alt
~~Path.make_directory_tree sounds better to me.~~. Just realized you were providing feedback, not volunteering :D. These mostly look relevant and I'll see about fitting them in.
I pushed some adjustments to
Closeable, since the interplay between Racket and Rhombus layers there is tricky.
I'm not sure if this is good or bad, but in the previous iteration this was a static error
class Bar()
block:
use_static
Closeable.def y = Bar()
#void
I'm not sure if this is good or bad, but in the previous iteration this was a static error
Interesting, but I currently lean toward "bad" on the grounds that it's indirect (checking for close as opposed to Closeable) and more ad hoc than most rules.
I added add_suffix. I'm happy with either dirname or basename or parent or something else for path-only.
I added
add_suffix. I'm happy with eitherdirnameorbasenameorparentor something else forpath-only.
An idea based on Vim (and Zsh) path modifiers, which uses :h and :t for the equivalent of dirname and basename: head and tail. Probably too ML-ish/not clear enough without the connection to other tools, but a neat connection nonetheless.
Besides
Closeable, it looks liketo_complete_pathis not yet documented, and probably that should beto_absolute_path.Should
AbolutePathandRelativePathbe annotations (which are easier to use in patterns)? And then dropPath.is_absolute?
Both of these are now implemented.
This seems ready to merge. I still have some small thoughts, but I don't want to create too much delay with more questions and further discussion (because we can always change more after), so let's time out these suggestions if they don't seem immediately like a good idea:
- Change
Path.extendtoPath.add? That seems easier to remember in combination with+/. - Change
Path.partstoPath.split? The terminology "elements" is more established than "parts" within Racket, but both "parts" and "elements" seem generic. The verb "split" is more compelling to me, makes clearer that it's a method instead of a field/property (although that's a weak benefit), and fits with a likelyString.splitto appear.
FWIW, I think some of the path tests will fail on Widows, but we can leave that to later (along with path-kind additions).