Scriptlike: New lib to aid in writing script-like programs

Nick Sabalausky SeeWebsiteToContactMe at semitwist.com
Tue Feb 11 16:56:46 PST 2014


On 2/11/2014 5:01 PM, Vladimir Panteleev wrote:
> On Tuesday, 11 February 2014 at 11:38:06 UTC, Nick Sabalausky wrote:
>> I've released a little one-module utility, Scriptlike, to help
>> simplify writing shell script-like programs in D:
>>
>>   https://github.com/Abscissa/scriptlike
>
> I've been thinking of creating something with a similar purpose, however
> also remove the need for any import lines

Not sure if you noticed, but Scriptlike publicly imports much of Phobos. 
If you think something else should be included that isn't already 
included, just submit a ticket or a pull request.


 > or declaring a main()
> function, so you can start writing statements from an empty file. It
> would use a different file extension.

Yea, I've given that some thought in the past, too, esp in the context 
of D-based makefile alternatives. Newer features like scoped imports do 
make it more appealing than ever.

But the thing I always get hung up in is the fact that, inside 
functions, you can't forward-reference declarations like you can outside 
of functions. So I always felt that would be more of a pain than just 
typing "import something; int main(string[] args) {}", which I've gotten 
pretty accustomed to. Heck, it's still less boilerplate than C# or Java :)


> Alternatively, you could do what vibe.d does and move the main()
> function to the library. That allows the library to parse command-line
> arguments, or catch the Fail exception (see below).

Yea, I thought of that, but I've always been [irrationally?] 
uncomfortable with hidden-main-provided-by-libs. (Of course, druntime 
itself does the hidden-real-main thing too ;) ) Plus then the user has 
to remember the new name to use instead of "main". So I shied away from 
doing that here. Still though, it is something to consider.


>> - A thin wrapper over std.path and std.file that provides a dedicated
>> Path type specifically designed for managing file paths in a simple,
>> reliable, cross-platform way. No more dealing with slashes,
>> paths-with-spaces, calling buildPath, normalizing, or getting paths
>> mixed up with ordinary strings.
>
> I think this is subjective. Path objects add too much syntax overhead,
> especially in shell script substitutes, IMO.

Perhaps. I've tried to keep it as simple as possible though:

auto p = path("/foo/bar");


>> - Less-pedantic filesystem operations for when you don't care whether
>> it exists or not: existsAsFile, existsAsDir, existsAsSymlink,
>> tryRename, trySymlink, tryCopy, tryMkdir, tryMkdirRecurse, tryRmdir,
>> tryRmdirRecurse, tryRemove: All check whether the source path exists
>> and return WITHOUT throwing if there's nothing to do.
>
> I think creating a new family of functions is not the best way to do
> this. How about a function/template that wraps a function instead? In
> Phobos, we have collectException(fun(...)), but something shorter like
> attempt!fun(...) would be better for scripting.
>

I'm not entirely opposed to that. I didn't pursue it for two reasons, 
though:

1. I was lazy and just didn't feel like writing a function forwarding 
template.

2. They don't all have the semantics. For example, tryMkdir does nothing 
and returns false if the path DOES exist, but tryRmdir must behave that 
way if the path DOESN'T exist. Flipping either of those around wouldn't 
make sense. So this way, I can have each func do the right thing without 
the user thinking about it, or having to come up with a system fancy 
enough to handle all that in one template.


> The biggest advantage to this approach is that such wrappers are
> composable.

Composability is good, but since the goal of Scriptlike is to simplify 
common script needs as much as possible, one of my intents is to wrap 
common compositions into single one-part identifiers. "No needless 
wrappers" is good for Phobos, but works against my goals for Scriptlike.

That said, *implementing* the existsAs*() and try*() functions by using 
compositional elements might be a good idea.


 > For example, in my library I have safeUpdate, which wraps a
> function that creates a file by redirecting its output to a temporary
> file, then replacing the target atomically with one rename() call, and
> obtainUsing, which skips the operation entirely if the target file
> already exists (useful for expensive operations such as downloading a
> file). The latter implies the former, so that e.g. a program terminated
> in the middle of the download does not consider that file to have been
> downloaded successfully in the previous invocation.
>

Nice. I like that. I may steal some of that ;)


>> - One simple call, runShell, to run a shell command script-style (ie,
>> synchronously with forwarded stdout/in/err) from any working
>> directory. (Also automatically works around DMD #10863 without waiting
>> for v2.066 - BTW, thanks all involved who fixed that.)
>
> Sorry about breaking it in the first place ;)

:)

Command escaping rules are admittedly a pain, *especially* on Windows 
where it tends to resemble a certain rabbit-hole Alice once discovered...


> But I would avoid running things "shell-like" though, as it's a mess of
> whitespace/escaping. Passing arguments as an array rather than a string
> is much better for many reasons.

Yea. I was thinking of providing "arguments as an array" too, but I 
haven't had a chance to give it much thought yet. But keep in mind, 
Path.toString() automatically quotes paths with spaces (use 
Path.toRawString if you don't want that), plus it ensures "current 
directory" remains as "." and never "", so those alone should reduce a 
fair amount of potential trouble.


>> - One simple function, fail(string msg), to help you exit with an
>> error message in an exception-safe way. (Does require some minor
>> boilerplate added to your main().)
>
> I think this idea combines even better when the main() function is
> created for you.

Agreed. As it is right now, "fail" is more of a semi-standardized 
convention than an actual implementation, since the real work is done in 
main by the user.




More information about the Digitalmars-d-announce mailing list