I would have thought this parameters behavior was a bug

I’m trying to code Relink to handle v5.3.x and I came across what I thought was a bug:

\procedure macro(argument)
\parameters(param)
Arg: <<argument>>

Param: <<param>>
\end


<<macro one two>>

Output?

Arg: one

Param: one

I was going to submit a bug report for this, but according to the documentation, this is… expected behavior???

What was the rationale for making it this way? Why doesn’t the \parameter pragma simply append another argument. How is Relink supposed to handle something like this?

(The last one is rhetorical. If Relink detects this situation, it will just erase the tiddler for you, because obviously you made a mistake making it.)

Edit: Link to the documentation. It seems like whoever wrote it already knew it was a bad idea.

Hi @Flibbles
I thought the same, but Jeremy explained it is not!
See

[BUG] The First Attribute of a Parameters Pragma is Overwritten when Is used Inside a Procedure · Issue #7458 · Jermolene/TiddlyWiki5 (github.com)

I like a more clear and transparent code! But this seems the way TW handles parameters.

Really?

They went with this behavior, and the accepted stance is, “If the end users are doing this, they deserve everything they get”?

I can easily think of reasons why someone would have an argument list with their definitions, and then a parameter widget after it for some extra customization options. Or maybe they have alternative parameter widgets with different defaults depending on how the procedure is being used.

I can sure think of a lot more reasons to do that than to ever use the baffling “$depth” attribute.

Ugggghhhhh. So it’s another situation where Relink simply has to fail.

I’m not sure I understand the issue. But if a script author wanted to append arguments, she could do something like:

\parameters (nul  a:100, b:200)

And ignore the “nul” variable. So this way it becomes the author’s choice if arguments are overlaid or appended.

It seems relatively consistent with the underlying JS language:

function foo (a, b) {
  console.log(a, b, [...arguments]);
}

foo (10, 20, 30, 40, 50) //=> 10, 20, [10, 20, 30, 40, 50]

JS does give you an alternative that works the way I think you expect:

function bar (a, b, ...rest) {
  console.log(a, b, rest);
}

bar (10, 20, 30, 40, 50) //=> 10, 20, [30, 40, 50]

But you have to opt into that specifically. And the built-in arguments is still there.

I’m not sure if it was the right decision or not, but it’s an understandable call.

@Flibbles I can understand your confusion but may I suggest as raised in some of the links above it becomes easier to understand if you keep an eye on the position of parameters.

  • I think these pointers will help a lot.

In your example, you use the short form to call the macro and do not “name the parameters”. In the macro definition you are using parameter names both in your procedure definition and the parameters pragma.

  • In my view the key issue arises because you are going from positional to named parameters.
  • tiddlywiki accurately responds correctly, but using the two forms of named parameters in the definitions makes it appear ambiguous, when relying on the position of parameters (rather than naming them when you call).

For example if you call you macro/procedure with the $macrocall widget (still works for procedures) or the unfriendly $transclude $variable= parameter you are forced (not completely) to name your parameters and thus the positional ambiguity will not occur.

  • You can just use them right away eg; <<macro argument:one param:"two">>

If you wish to write solutions that make use of positional parameters there are now a lot more features available to support you. I will not cloud this reply with details but feel free to ask.

Just a few quick notes;

  • I tend to use the $paramaters widget inside procedures and custom widgets (and other places) rather than the parameters pragma because it’s clearer and you gain more control/features.
  • Get in the habit of placing the name of a procedure or widget in its \end clause.
  • When debugging code with parameters using the $parameters widget you can capture calling parameters using the $params parameter and display a readable “JSON array of position/parameter/value pairs” and display them.
  • The parameters widget can also behave like a $let widget to set up other variables for use in your procedure or custom widget.
  • It is now possible to use an arbitrary number of parameters with or without names and have your procedures/custom widgets handle them.

Feel free to ask if needed.

I hope this helps.

1 Like

@mark_S. That’s a neat workaround. I’d hate for it to take off because it doesn’t address, and in fact exacerbate the problem I’m facing with Relink. Relink doesn’t know if an argument is throw-away. If the actual first parameter is relinkable, Relink won’t know what to do with it because it might also be the nul, and thus shouldn’t be relinked.

@Scott_Sauyet. I don’t see it as consistent. The arguments variable in javascript represents all arguments, regardless of how or whether they’re defined. I don’t think it actually maps to this situation, where we’re getting conflicting arguments which could be doubly assigned if the procedure is made poorly.

@TW_Tones. These are all some great tips, but I’m not raising this issue as a confused end-user, but as a plugin developer who needs to deal with confused end-users. They might follow your tips. They might not. And if they don’t, I think it makes more sense if Tiddlywiki does something both practical and unambiguous, rather than allowing for an outcome that’s of no value to end-users, and can be super error prone.

I’m trying to understand how this affects Relink. Are we talking about changing links, or when someone changes a macro name, or maybe even macro parameter names? If the latter, I’ve never used that advanced feature. A little back-grounder might help us sympathise a bit more, for what it’s worth.

Thanks!

@Flibbles with all due respect as a developer or otherwise, your example case was asking for ambiguity, there is none in tiddlywiki. You called a macro with unnamed parameters, thus making it position dependant, and took no account of this in the definition of your macro, and in fact named the parameters in more than one place.

I do really feel for you when it comes to ensuring relink can cope with the new features, relink is possibly the single most important plugin in my view, along side Timimi saver, perhaps ever.

  • keep in mind Relink should not be responsible for dealing with code where the design has intentionally not named parameters.

Let me put it this way, There is no ambiguity in TiddlyWiki I can explain every byte in relation to what you raised. If we were to make any change to the way this operates you are likely to introduce ambiguity, not reduce it, or restrict the possibilities, and break backwards compatibility.

Thanks again for RELINK it is a most valuable thing. Please count on us in the community to support you as best we can.

I have since seen you new reply if the code contained the following would it be easier for relink <<car model:Honda>> it seems to me good code would do more to process such a parameter.


[Post Script] @jeremyruston could I ask that the core team keep in mind RELINK for all future changes to the core. RELINK is an essential piece many of us now install as a rule for anything beyond a simple wiki, there are arguments it should be in the core, but it’s modularity as a plugin is important. We do not want to make a “rod for @Flibbles back” just to maintain it, keeping up with core changes that did not take into account RELINK, or where the core team did not assist @Flibbles deal with change.

Yeah sure. I forgot I’m neck deep in relinking, and nobody else is. Here’s a case:

\procedure car(model)
<$parameters null="" doors={{Sedan!!doors}} color={{Colors!!default}}>
...
\end
\relink car model

...

<<car Toyota>>

If Toyota is a tiddler, and that tiddler is renamed to Honda, then Relink will have to return an error. In this case, the “Toyota” argument is filling both the “model” and the “null” argument. And so Relink won’t know whether to relink it as a model, in which case it updates it, or as a null, in which case it leaves it alone.

It might seem like my example isn’t a good design, but I guarantee you someone will do this, where they think required arguments can be in the \procedure line, where they don’t need defaults, and optional arguments can be on a different line where they can specify defaults however they want. It might be even more likely if $parameters is defined using a $genesis widget, because the parameters can be dynamic based on some CarFacts tag or something.

What? It absolutely is responsible for this, and wouldn’t be a comprehensive relinker if it didn’t. Until the \parameters stuff showed up, Relink had no trouble at all dealing with positional arguments.

I realize in my example that I’m introducing ambiguity. I wouldn’t personally write macros like that because I know about this problem. My issue is because of the way TiddlyWiki is choosing to interpret the presence of multiple parameter declarations, it’s a guaranteed bug for the end-user, when an end-user might have assumed that later declarations just add later parameters. That seems intuitive to me at least.

Overlaying arguments serves no one, and is not what anyone would expect. And yet we’re defining that this the expected behavior. If nobody is supposed to be doing this, why not change the documentation to say “Don’t do this. The behavior is undefined.”?

  • Thats excellent

That’s true but until the $parameters widget turned up.

  • We had to define/name every parameter
  • Thus only a finite number of parameters was allowed
  • There was no way to preprocess parameters before they were referenced
  • and more that is now possible.

Perhaps you can exercise the right to set the scope of relinks capabilities?

My argument is nothing is undefined and behaves as expected. Nor is there anything we are telling people they should not do. However I do believe there are good programing practices we can promote.

Yes, Lets document our way out of this!

All right. I’m just going to program Relink to only support multiple parameter definitions if you make sure to use named parameters (because I don’t have a choice).

But listen. Somewhere out there is a mangy raccoon living in someone’s attic, and when the owners of the house leave for work, this raccoon comes down and uses their computer to program his website using Tiddlywiki. He is going to see that the documentation describes how argument overlapping is expected behavior, and using his little raccoon brain, he’s going to somehow design a macro that makes use of this. And when Relink fails to update his macro parameter references, that raccoon is going to submit a bug report at me.

Now I can’t just call animal control and have them catch this raccoon and release him back into the wild. I actually have to answer him. And I’d have to say that it’s simply not something Relink can do, and he’s going to get pissy and argue that this is a Tiddlywiki feature since it’s documented, and when he does, I’m going to (at) you into the conversation.

I still strongly believe that argument overlapping is not an intuitive or useful behavior. Appending make sense. End-users might expect it. It could theoretically have uses. Why not just have this strange behavior do that instead?

Also, thank you for the vote of confidence. I do appreciate this. I know I can get huffy over these design choices, so I appreciate the effort to remind us all that we’re all friends collaborating here.

:laughing:

I totally understand, that is in part why I mentioned @jeremyruston the “code controller, and whisperer” .

One persons “argument over lapping” is another persons “sophisticated parameter handling”.

  • I think this fair. Actually you may be in a position to write a short guide, telling people how to wright code that is relink aware. Why leave it in the lap of the gods?, let god himself write the commandments :nerd_face:

You know relink is so good, I often ask myself will it handle this change or that and it just does, it is successful so often, I get a little lackadaisical.

  • I do check the details when writing something I expect to rename, especially recently, when playing games with namespaces (optional reading only Expanding the is system namespace) and thus I do make RELINK a prerequisite.
  • Another time I check the documentation is when I am writing something where I want to ensure referential integrity beyond normal tiddlywiki behaviour. Relink has always being there to allow me to do such things.

Actually, I possibly only depend on and can’t live without the first 20% of relinks functionality, all the rest is just a bonus, and due to my own ignorance I check if it’s going to work for me in special cases, when the need arises. Further If I found something that did not work, or code I wrote poorly, all I do is use other tiddlywiki refactoring methods.

  • The point being, I appreciate you trying to make RELINK all things for all people and you get very, very close but it is a lot to expect of yourself.

:star: :star: :star: :star: :star: /5

2 Likes

Is the relink pragma working by names, or by position? Because in this case it seems like it’s using names, so you could know which one is being referenced.

Relink pragma uses names, but it figures out the position.

You can do this:

\define macro(A B C) ...
\relink macro B

<<macro FirstArg MyTiddler>>

and it will figure out that MyTiddler is argument B. Relink has been able to do this since it was released. Nameless parameters have always been a Tiddlywiki feature. I use them. The core uses them. Everybody does, but if someone were to use \parameters mulitple times, the use of nameless parameters becomes flawed and ambiguous.

1 Like