emacs/var/elfeed/db/data/d9/d948a3193e8b96bae3218d52d89fd6a98c83bd2d
2022-01-03 12:49:32 -06:00

1 line
5.7 KiB
Plaintext

<!-- SC_OFF --><div class="md"><p>Hello all! I&#39;m learning emacs lisp and getting used to the language. Yesterday I was trying to write a function to build and execute a shell command that I type frequently, to backup some of my files to google drive. I could make it work by hardcoding the commands, but as a learning exercise, I decided to try some fancy, overengineered stuff, to see if I can incorporate what I&#39;m thinking in Elisp.</p> <p>Well, I couldn&#39;t. I still get confused with the function/var namespace separation, and the multiple ways I can declare things (<code>let</code>, <code>letf</code>, <code>flet</code>, <code>defalias</code>, ...). When to use lambda functions or just quoted expressions. If I should use partial application or just macros (like the threading ones). Etc.</p> <p>What I wanted to write is something like this:</p> <p>Given a list of file paths:</p> <ol> <li><p>&quot;Duplicate&quot; each</p></li> <li><p>Prepend the local root directory to one part</p></li> <li><p>Prepend the remote root directory to the other</p></li> <li><p>If the remote (second) path is a file, transform it to its directory name*</p></li> <li><p>Build the shell command for each file <code>rclone copy local_path remote_dir_path</code></p></li> <li><p>Concatenate all commands with <code>;</code></p></li> <li><p>Execute (print) them</p></li> </ol> <p>*because <a href="https://rclone.org/commands/rclone_copy/">rclone&#39;s copy</a> command requires a directory as destination.</p> <p>In Python, I could write something like this:</p> <p>```python from os import path</p> <p>files = [&#39;org/&#39;, &#39;pictures/image1.png&#39;, &#39;pictures/image2.png&#39;, &#39;readme.md&#39;]</p> <p>build_prepend_function = lambda prefix: lambda str: prefix + str prepend_local_root = build_prepend_function(&#39;~/&#39;) prepend_remote_root = build_prepend_function(&#39;d:macbook/&#39;) extract_dir_path = lambda file: \ file if file.endswith(path.sep) else path.dirname(file)</p> <p>build_move_to_remote_shell_command = lambda file: \ &#39;rclone copy {} {}&#39;.format(prepend_local_root(file), extract_dir_path(prepend_remote_root(file)))</p> <p>commands = (build_move_to_remote_shell_command(file) for file in files) script = &#39;;&#39;.join(commands)</p> <p>print(script) ```</p> <p>I can even translate it to C#:</p> <p>```csharp using System.IO;</p> <p>var files = new List&lt;string&gt; { &quot;org/&quot;, &quot;pictures/image1.png&quot;, &quot;pictures/image2.png&quot;, &quot;readme.md&quot; };</p> <p>Func&lt;string, Func&lt;string, string&gt;&gt; buildPrependFunction = prefix =&gt; str =&gt; $&quot;{prefix}{str}&quot;; Func&lt;string, string&gt; prependLocalRoot = buildPrependFunction(&quot;~/&quot;); Func&lt;string, string&gt; prependRemoteRoot = buildPrependFunction(&quot;d:macbook/&quot;); Func&lt;string, string&gt; extractDirPath = file =&gt; file.EndsWith(Path.DirectorySeparatorChar) ? file : Path.GetDirectoryName(file); Func&lt;string, string&gt; buildMoveToRemoteShellCommand = file =&gt; string.Format( &quot;rclone copy {0} {1}&quot;, prependLocalRoot(file), extractDirPath(prependRemoteRoot(file) ));</p> <p>var commands = files.Select(buildMoveToRemoteShellCommand); var script = string.Join(&#39;;&#39;, commands);</p> <p>Console.WriteLine(script); ```</p> <p>Both cases yielding this as result:</p> <pre><code>rclone copy ~/org/ d:macbook/org/;rclone copy ~/pictures/image1.png d:macbook/pictures;rclone copy ~/pictures/image2.png d:macbook/pictures;rclone copy ~/readme.md d:macbook </code></pre> <p>But I&#39;m strugling to replicate that in Emacs Lisp:</p> <p><code>elisp (defun fun () (interactive) (let* ((selected-files &#39;(&quot;org/&quot; &quot;pictures/image1.png&quot; &quot;pictures/image2.png&quot; &quot;readme.md&quot;)) (build-prepend-fn (lambda (prefix) (apply-partially #&#39;concat prefix))) (prepend-local-root (funcall #&#39;build-prepend-fn &quot;~/&quot;)) (prepend-remote-root (funcall #&#39;build-prepend-fn &quot;d:macbook/&quot;)) (extract-dir-path #&#39;file-name-directory) (build-move-to-remote-shell-command (lambda (file) (format &quot;rclone copy %s %s&quot; (funcall #&#39;prepend-local-root file) (funcall #&#39;extract-dir-path (funcall #&#39;prepend-local-root file))))) (commands &#39;(mapcar #&#39;build-shell-command selected-files)) (script &#39;(string-join (funcall (commands)) &quot;;&quot;))) (message (funcall (script))))) </code></p> <p>While I&#39;m continuously checking the documentation and practicing, it would be nice if somebody more experienced than me could pinpoint what is wrong in this one-to-one translation.</p> <p>And of course. I&#39;m open to suggestions of how you would write this, preferably in a more functional way. I believe writing everything inside a <code>let</code> [or any other binding-] form is not the most idiomatic way of writing elisp, given all the features lisp can offer. But I&#39;m interested in the exact translation since it helps me alot in learning how elisp works.</p> <p>Oh, and I&#39;m also aware that I can manipulate remote files with TRAMP, but I still didn&#39;t get to learn it. One thing at a time!</p> <p><strong>tl;dr: Learning elisp. Would like to translate above Python/C# code to equivalent e-lisp, for learning purposes. Open for suggestions of how things are done in elisp.</strong></p> <p><strong>edit: gists available <a href="https://gist.github.com/mabo3n/9b86df34bcb68501eda4d16e576a61f1">here</a></strong></p> </div><!-- SC_ON --> &#32; submitted by &#32; <a href="https://www.reddit.com/user/kromobrn"> /u/kromobrn </a> <br/> <span><a href="https://www.reddit.com/r/emacs/comments/r0i8d6/help_me_translate_this_pythonc_code_to_emacs_lisp/">[link]</a></span> &#32; <span><a href="https://www.reddit.com/r/emacs/comments/r0i8d6/help_me_translate_this_pythonc_code_to_emacs_lisp/">[comments]</a></span>