emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Extract to new definition (was: Adding refactoring capabilities to Emacs


From: Eshel Yaron
Subject: Extract to new definition (was: Adding refactoring capabilities to Emacs)
Date: Sun, 01 Oct 2023 17:07:14 +0200
User-agent: Gnus/5.13 (Gnus v5.13)

Hi,

>> ...can you, say, extract subexpressions to variables, organize
>> imports, etc?
>
> Yes, there are some other things already in place, like updating imports
> and adding exports.  I have a work-in-progress implementation for
> extracting subexpressions (subterms, in Prolog), but nothing in terms of
> existing commands.

Since writing this message, I've added that "extract" command to
`sweeprolog`.  I'm still working on some final tweaks, but I'm pretty
happy with the result so I thought I'd share some details and insights.

The new command is called `sweeprolog-extract-region-to-predicate`.  You
call it with point and mark surrounding some code inside one definition
that you want to extract to a new, separate definition.  You do this
either because you want to reuse this piece of computation somewhere
else or simply to break a large complicated definition into smaller
parts.  For example, with the following code in a `sweeprolog-mode`
buffer, where ^ and $ denote the region:

--8<---------------cut here---------------start------------->8---
foo(X, Y, Z) :-
   ^bar1(X,A), bar2(A,B), bar3(C,Y)$,
    baz(Y,Z).
--8<---------------cut here---------------end--------------->8---

Typing `M-x sweeprolog-extract-region-to-predicate RET bar RET` yields:

--8<---------------cut here---------------start------------->8---
foo(X, Y, Z) :-
    bar(X,Y),
    baz(Y,Z).

bar(X, Y) :-
    bar1(X,A), bar2(A,B), bar3(C,Y).
--8<---------------cut here---------------end--------------->8---

The complex goal (expression) with `bar1`, `bar2` and `bar3` makes way
for a single call to `bar`, while a definition of `bar` is created in
the current buffer.  Crucially, this command analyzes the selected goal
and its context to determine how many and which arguments the new
definition should have.

In the simplest case, you mark a piece of code, call the command, it
prompts you for the name of the new definition, and performs the
extraction at once.  There are, however, some subtleties to consider:

- The selection may be invalid.  Invalid means that the contents of the
  region do not constitute a valid Prolog goal, that's a clear user
  error, so this command complains and bails.

- Some extraction operations are unsafe.  Unsafe means that extracting
  the selected piece of code may change the semantics of the program.
  (Un)safety is very language-specific, and it's more difficult to
  determine than validity, since safety is a semantic property while
  validity is syntactic.  For Prolog, extracting a goal that contains a
  "cut" can have a semantic effect, because the "cut" operates on the
  defintion in which it occurs.  In fact, it's not generally decidable
  whether extraction will have an affect on the semantics of the program
  or not.  That's why `sweeprolog-extract-region-to-predicate` asks for
  confirmation if it can't be certain that extracting the specified goal
  preserves the semantics of the surrounding code.

- The name you give to the new definition might already be in use.
  That's something `sweeprolog-extract-region-to-predicate` does not
  currently protect you from, but I intend to address that soon.
  Ideally, the command should detect this collision early, and bail or
  ask for confirmation before changing the buffer.

My hope is that when we get that generic refactoring interface in Emacs,
it'll support these distinctions and handle them appropriately.

This command also has an extension on top of the basic local extraction
operation: If you call it with a prefix argument, then after performing
the extraction as usual, it searches the current buffer for other goals
that the extracted goal subsumes, and suggests replacing them one after
the other with calls to the newly defined predicate with appropriate
arguments.  This is useful when you have several occurrences of some
complex idiom scattered around that you want to extract to a stand alone
definition.

In terms of user interface, there's also a user option that says where
to insert the new definition, and that's about it.
(A couple more details are mentioned in the manual:
https://eshelyaron.com/man/sweep/Extract-Goal.html)


Best,

Eshel



reply via email to

[Prev in Thread] Current Thread [Next in Thread]