32
\$\begingroup\$

In the absence of a full-fledged tutorial, I am teaching myself to program in the Factor language by writing a bunch of functions that I will eventually use in command-line scripts.

One common operation I like to perform is to change the case of names of files contained in a given directory to uppercase or lowercase. I wrote the following code to perform this operation in Factor:

USING: fry kernel io.directories sequences unicode.case ;
IN: util.directories

: move-each-directory-file>lower ( pathname -- )
    [ dup >lower ] move-each-directory-file ;

: move-each-directory-file>upper ( pathname -- )
    [ dup >upper ] move-each-directory-file ;

: each-directory-file ( pathname quot -- )
    '[ [ @ ] each ] with-directory-files ; inline

: move-each-directory-file ( pathname quot -- )
    '[ @ move-file ] each-directory-file ; inline

I have tried to factor as much code as possible into a reusable vocabulary. I look forward to suggestions for improvement.

\$\endgroup\$
3
+50
\$\begingroup\$

Comments! Comments are good, especially to people who are just learning Factor right now. Admittedly, for Factor, it's pretty readable -- I finally got it, after staring a while and doing lots of Googling -- but comments are always nice.

I can't find anything about the : name>something ( etc -- etc ) word syntax online -- namely, the >something part. Because of that, I'm just gonna assume that it's good syntax and move on. Alright, I finally found it! And yes, that's good syntax -- you're supposed to use a>b for functions converting a to b, and that's exactly what you do. It might be better called directory-file-names>upper/>lower, but that's just personal preference.

It looks good, though. After figuring out how Factor works, it's pretty readable (for Factor), and actually makes me want to use the language more. Plus, as far as I can tell, you're following the official coding style to the letter. Well done.

\$\endgroup\$
3
\$\begingroup\$

I've spent a good few hours figuring out Factor. It's a very interesting language.

Your implementation seems very good and straightforward:

  • Using higher order functions, essentially passing a name transformation operation to a file move operation, which in turn is passed to a file iterator operation. Thanks to this, there is no duplicated logic anywhere in this code

  • The names you came up with are all top-notch:

    • each-directory-file: consistent with with-directory-files from io.directories, and each in the language itself
    • move-each-directory-file, ...>lower, ...>upper: all doing exactly what they say, the move- prefix and >lower, >upper suffixes all makes sense
    • pathname: good, but "path" might be slightly better, to be consistent with with-directory-files
    • quot: consistent with many examples in the language

When I tried to run the program, the compiler complains:

4: : move-each-directory-file>lower ( pathname -- )
5:     [ dup >lower ] move-each-directory-file ;
                                              ^
No word named “move-each-directory-file” found in current vocabulary search path

It seems you cannot define a word in terms of another word that is not yet known. I can load your program if I reorder the declarations like this:

USING: fry kernel io.directories sequences unicode.case ;
IN: util.directories

: each-directory-file ( pathname quot -- )
    '[ [ @ ] each ] with-directory-files ; inline

: move-each-directory-file ( pathname quot -- )
    '[ @ move-file ] each-directory-file ; inline

: move-each-directory-file>lower ( pathname -- )
    [ dup >lower ] move-each-directory-file ;

: move-each-directory-file>upper ( pathname -- )
    [ dup >upper ] move-each-directory-file ;
\$\endgroup\$
  • 1
    \$\begingroup\$ Well technically you could add one other function which calls dup while passing in the case conversion function. "Pathnames" are also what's used in the documentation, so that's arguably better than "path". Another interesting thing is the documentation facilities, so for each function it's possible to add add very detailed docstrings, the scaffolding tool will generate a skeleton for a vocabulary. \$\endgroup\$ – ferada Jun 28 '15 at 11:15
  • \$\begingroup\$ @ferada if you write up an answer on all that I would upvote \$\endgroup\$ – janos Jun 28 '15 at 11:35
3
\$\begingroup\$

Comment to @janos now as a full post:

  • Technically you could add one other function which calls dup while passing in the case conversion function. The code I had saved some time ago (directories.factor) looked like this (btw. I could load this with USE: util.directories without any problems:

    USING: fry kernel io.directories sequences unicode.case ;
    IN: util.directories
    
    : each-directory-file ( pathname quot -- )
        '[ [ @ ] each ] with-directory-files ; inline
    
    : move-each-directory-file ( pathname quot -- )
        '[ @ move-file ] each-directory-file ; inline
    
    : transform-each-directory-file ( pathname quot -- )
        '[ dup @ ] move-each-directory-file ; inline
    
    : move-each-directory-file>lower ( pathname -- )
        [ >lower ] transform-each-directory-file ;
    
    : move-each-directory-file>upper ( pathname -- )
        [ >upper ] transform-each-directory-file ;
    
  • "Pathnames" are also what's used in the documentation, so that's using the "correct" naming convention.

  • Another interesting thing is the documentation facilities, so for each function it's possible to add add very detailed docstrings, the scaffolding tool will generate a skeleton for a vocabulary. After running USE: tools.scaffold and "resource:work" "util.directories" scaffold-vocab and filling in the spots I ended up with another file (directories-docs.factor) like this:

    USING: help.markup help.syntax kernel quotations ;
    IN: util.directories
    
    HELP: each-directory-file
    { $values
        { "pathname" "a pathname string" } { "quot" quotation }
    }
    { $description "Run the quotation on each filename in a directory." } ;
    
    HELP: move-each-directory-file
    { $values
        { "pathname" "a pathname string" } { "quot" quotation }
    }
    { $description "Move each file in a directory to the return value of the quotation running on the filename." } ;
    
    HELP: move-each-directory-file>lower
    { $values
        { "pathname" "a pathname string" }
    }
    { $description "Rename each file in a directory to lower case." } ;
    
    HELP: move-each-directory-file>upper
    { $values
        { "pathname" "a pathname string" }
    }
    { $description "Rename each file in a directory to upper case." } ;
    
    HELP: transform-each-directory-file
    { $values
        { "pathname" "a pathname string" } { "quot" quotation }
    }
    { $description "Rename each file in a directory to the return value of running the quotation on the filename." } ;
    
    ARTICLE: "util.directories" "util.directories"
    { $vocab-link "util.directories" }
    ;
    
    ABOUT: "util.directories"
    

    This is really nice to document everything in more detail and access it via the inbuilt browser etc.

\$\endgroup\$

protected by Ethan Bierlein Jun 29 '15 at 1:15

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.