Idiomatic refactoring to remove duplication between `<!let ...>` calls

In the latest version of my Periodic Table work, some duplication has crept in, and I’m wondering how best to get rid of it. In three different tiddlers, I have <$let...> statements that include among other things, three identical definitions. Is there an idiomatic way in TW to remove this duplication?

  • In $:/_/my/tools/Reactor, thanks to @telumire, I have this:

    <$let 
      elements={{{ [list[!!elements]get[symbol]join[ ]] }}}
      element="([A-Z][a-z]?)"
      not-a-letter="[^A-Za-z]"
      split-to-symbols="[splitregexp:m<not-a-letter>splitregexp:m<element>!is[blank]]"
      is-missing-elements="[enlist<elements>]-[subfilter<split-to-symbols>]"
      contains-wrong-elements="[subfilter<split-to-symbols>]:filter[!enlist<elements>]"
      exact-match="[!filter<contains-wrong-elements>!filter<is-missing-elements>]"
    >
    
  • In $:/_/my/macros/constituent-elements, I have

    <$let
      element="([A-Z][a-z]?)"
      not-a-letter="[^A-Za-z]"
      split-to-symbols="[splitregexp:m<not-a-letter>splitregexp:m<element>!is[blank]]"
    >
    
  • and in $:/_/my/macros/containing-compounds, I have

    <$let
       element="([A-Z][a-z]?)"
       not-a-letter="[^A-Za-z]"
       split-to-symbols="[splitregexp:m<not-a-letter>splitregexp:m<element>!is[blank]]"
       matcher="\b$symb$\b"
    > 
    

Is there a clean way to centralize the definitions of element, not-a-letter, and split-to-symbols to be used by simple calls from these three tiddlers?

I’m excited about the upcoming 5.3.0 ability to define more complex global variables with \function… but for now, I’d try making them global macros, which are a great way to define regex strings, for instance.

1 Like

In a separate tiddler, define your variables using macro definitions or set widgets. Note that if you need multiple set widgets, they should be nested with no other widget used.

Then where you want to use the variables, start the tiddler with the \import pragma and import the variables from the tiddler holding the definitions.

1 Like

Give this a try:

First, in $:/_/my/tools/Reactor, move these lines to \define macros, like this:

\define element()          ([A-Z][a-z]?)
\define not-a-letter()     [^A-Za-z]
\define split-to-symbols() [splitregexp:m<not-a-letter>splitregexp:m<element>!is[blank]]

Then, in each of the other tiddlers, add this line at the top of the tiddler:

\import $:/_/my/tools/Reactor

enjoy,
-e

1 Like

Thank you @etardiff, @saqimtiaz, and @EricShulman!

Clearly there is an idiomatic way, as you are all suggesting variants on the same theme.

I swear when I tried that it didn’t work. But I’m sure now that I must have been doing something else wrong. I will try this soon.

Thank you for helping make this such a great community.

@Scott_Sauyet if it helps, when writting TiddlyWiki script, if I have a string that represents something like a filter or regular expression that, has a fixed meaning, as in the example the “not-a-letter” regular expression (this is and will always be the regular expression for this), I would never use it in a set or let widget because it is effectivly a “resuable” or potentialy global value and I would at first use a define at the top of the tiddler until the code is working, but once the code is working I would cut it out and put it in a tiddler tagged $:/tags/macro as a rule.

When I move this to a global macro tiddler I think about the scope of the variable; and this influences the tiddler I use for this; such as;

  • If it relates to a particular plugin or package and I want it to “travel” with the plugin or package I put it a tiddler in its namespace.
  • If it relates to the specific wiki and not a more general subject I put it in a seperate tiddler such as $:/wiki/macros
  • If it makes sence as a multi-wiki general solution, I may add it to a tiddler such as $:/global/macros/regular-expressions, or just $:/global/macros

If a macro definition is more specific in nature, or part of a large set of macros, for example; I had a set of macros and string variables relating to say “print page layouts”, there is no value in making them global, and as a result I put them all in a tiddler with a name that scopes it such as $:/print-layouts/macros and use the \import pragma to import the set of macros only where needed.

By adopting such rules each of my solutions, in some ways “accrue collections” of macros and reusable strings, and I often harvest the more general ones and add them to my references and documentation for later reuse or deployment.

  • I have never had performance issue as a result of this approach
  • One advantage is, if I were working on a new package or wiki, where I need say “page layout macros” its easy to recall another wiki where I did this
  • If I have gone to a particular wiki more than once looking for something, I usualy package the shareable code and export it. eg page-layout-macros.json

With the introduction of the \function in TW5.3.x I expect to start doing this for any reusable filter as well.

Finaly to aid this process I am always thinking about naming and how to make reusable content, and try and drive it into plan language so it is meaningful and searchable. Perhaps idiomatic?

Thank you @TW_Tones. I do use local and global macros a fair bit. What I really was missing was \import. That will cover the in-between ground beautifully.

But I never know when I start that something will be reusable. Often I’ll allow a second duplication. But when I get to a third, I know it’s time to refactor. Somehow I just wasn’t seeing what to do here.

Thanks for the great advice. I will take it to heart.