Attribute assignment where the attribute is computed

To be clear, I’m talking about the attribute itself, not the value being assigned to it.

I’m working inside <$parameters> with the $params attribute (which is a huge leap forward in terms of wikitext production). Having used jsonget to get the params, is it possible to marshal them into attributes to be passed on to a subsequent outgoing call using $transclude?

<$transclude $variable=[derived from param[0]] [derived from param[1]]="value1" param[2]= ... />

For a fleeting moment I considered $wikify… but look…

<$transclude $variable=[derived from param[0]] <<attr1>>="value1" <<attr2>>=... />

non-starter.

So, yes, essentially – and taking this to its logical conclusion – I’m asking for an $eval(...) widget :scream:

Or, failing that, a getsignature[] operator…

<$let my-proc-signature={{{ [[my-proc]getsignature[]] }}}>` --> "arg1 arg2 ... argN"

and “teach” $transclude to understand them.


For those born without the JavaScript gene, window.eval(<string> script) is a way to (re)start a JavaScript engine just to execute a string you built and passed into it for evaluation/execution. If you search for “eval is evil”, you’ll see why I was screaming back there.

@CodaCoder Just so you know I asked a similar question in the recent past, and decided it was unnecessary, because of other ways to achieve what I wanted. Could you suggest a use case or practical example, without code?

  • What are these “variables” you are calling are they procedures?
  • For one thing all parameters become variables that are available to any subsequent transclusion so as long as you know their names you can access them.

Have you looked at the fact with the parameters widget and its $param attribute we can set the incoming parameters in a JSON array?

  • If you control the transclusions you are calling you could just give the parameter array in a parameter value and pull it apart in your transclusion.
  • Once you have this JSON array a common way to use the values is to use something like set multiple variables or any widget that uses the $names/$values parameters. These parameters are filters and as long as the number or variable names matches the values you can use any filter you want.

However I think you want to call an evaluated variable with evaluated parameters and values?

  • The Genesis Widget lets you do what you ask for, for widgets and HTML as you will see it has a $names and $values (filters) parameters that can evaluate the JSON array. In here you can evaluate the parameter names and values as you like.
  • I wonder if it, or a similar solution, could be designed to do this for procedures as well? @jeremyruston

I have more to share on this but look forward to some feedback from your first.

:dart:

Sh1t! I forgot about that little gem. That might be the last bit of 5.3 I have yet to play with, too.

Lemme go look (but tomorrow - 'tis way past my bedtime).

Thanks!

If you used a custom widget you could use genesis to call it, and the custom widget can also call procedures etc…

I prepared this for you, for tomorrow :sleeping:

\procedure call-me()

;Call me  - I call ''next proc''
<$parameters $params=all-params>
<<next-proc>>
</$parameters>
\end call-me
\procedure next-proc()
''next proc''
<<all-params>>
Here you could use setmultiplevariables
\end next-proc

<<call-me a b c d:"hi">>

Sleep well.

I just looked.

:cat:

Purrrr-fect.

1 Like

(@TW_Tones So, here it is – many thanks for the $genesis nudge.)

While the $transclude widget is indeed super powerful, when it comes writing transcludes with computed parameters (attribute values) it starts to get ugly pretty quickly, making it hard to parse (mentally).

Take this example:

 <$transclude $variable="macro-doc-ui.update" tiddler=<<target-tiddler>> varname=<<macroname>> />

So, I wondered if I could somehow introduce a bit of syntax sugar, and get it closer to the shortcut we all love, notionally, something like…

<<macro-doc-ui.update <<target-tiddler>> <<macroname>> >>

or

<<macro-doc-ui.update tiddler:<<target-tiddler>> varname:<<macroname>> >>

While they’re obviously “broken” syntactically, I pushed ahead with an idea – and this is where I’m at:

<<... macro-doc-ui.update "tiddler varname" "target-tiddler macroname" >> 

Formally…

<<... [macro-to-call] [list of param-names]  [list of values] >> 

I chose to call it ... as a reminder of sorts that its eliding the general case of typing out a full $transclude. You can, if you choose to use it, choose any name you wish.

Here’s the code. If anyone can see a way to tighten up the <$set> calls in the preamble, I’m all ears…

 \procedure ...()
  <$parameters $params=params>
    <<stack-clear dot-stack>>
    <$set name="mac" value={{{ [<params>jsonget[0]] }}} >
    <$set name="attrs" filter="[[$variable]] [<params>jsonget[1]split[ ]]">
    <$set name="values" filter="[<params>jsonget[2]split[ ]] +[getvariable[]]">
    <$set name="values" filter="[enlist<values>prepend<mac>]">
     <$genesis $type="$transclude" 
         $names=<<attrs>>
         $values=<<values>>>
     </$genesis>
    </$set></$set></$set></$set>
  </$parameters>
 \end ...

Once again, that’s…

<<... your-macro "your arg-names" "your var-names">>

I think I achive this in any one of the below suggestions;

  • I hope this is as relavant as I think it, and written also for a future reader.

I dont follow all your assumptions @CodaCoder, but just to share an alternate perspective or more;

As soon as you use macrocall or transclude $variable your parameters can be evaluated before passing, but you know this;

  • <$macrocall $name=macro-doc-ui.update tiddler=<<target-tiddler>> macro=<<macroname>> />

You can allow just a name rather than the evaluated variable as a parameter/value, then inside the procedure “obtain the value”. This makes the parameters simpler, the complexity is moved into the procedure.

  • <<procedurename tiddlername varname fieldname>> assume the params names are T/V and F
  • inside the procedure [<t>], [<v>getvariable[]], [<t>get<f>] and other methods.
    • This also means you have the name as well as the ability to get the value to use in your code.
  • Defining \functions nested inside your procedures is a great way to handle these and the next approach

There is "very little in a filter, that can not be passed in a parameter, so you can call a procedure like this;

  • <<proc display-filter="[all[current]]" ...
  • myfieldvalue="[all[current]get[fieldname]]" ...
  • Now inside your procedure use the “filter” to obtain the value.
  • Once you treat parameter values like a filter you can pass a complex thing to evaluate, OR even just a simple title eg; tiddler-filter="mytiddler"

And Penultimate, and easily forgotton, if a variable already exists, it is available to the procedure and it is quite resonable to use a form like this; without any parameters.

\procedure myproc() use `<<tiddlername>>` and `<<varname>>` as needed

<$let tiddlername={{!!afield}} fieldname=<<varname>> >
<<myproc>>
</$let>
  • No need to pass any parameters just make the variables you need in the procedure available and include the $let is parameters and values be part of the code snipit.
  • This in fact is a useful way of documenting the parameters required for the procedure, if the variable is already available in context you can just remove the matching $let varname/value.
  • The $let widget also
    • “Differs from the $vars widget in that variables you’re defining, may depend on earlier variables, defined within the same $let here
    • allowing quite sopisticated variable values to be evaluated.

A final hint;

  • You can write code in the procedure, and sometimes even functions, where if a variable or parameter is not available the code sets a default or computes a value using other info. This reduces the need for a parameter to be given.
    • The simplest example is providing a default value.

Thanks again, Tony.

Assumptions? Please explain…

But regarding assumptions, your last code block assumes that the <$let> assignments are “nearby”, meaning, it’s relatively easy to see the relationship between the caller names and the callee’s handling of them.

When the assignments are further away (perhaps in a different tiddler that calls myproc) that relationship may not be in your (my) head six months from now. You change the name of, say, tiddlername to tiddler and myproc breaks.

It’s far better to “pass by value” without a named argument, then that won’t happen.

In fact, I’ll attempt to rework <<... *>> to get them out of the conversation. Not sure that’s possible with <$parameters> as it currently works, though.

Thanks again.

Just trying to respond, I hope I am not wandering off topic.

What I felt were Your assumptions;

And this Approach:
<<... your-macro "your arg-names" "your var-names">>
What?

  • Because I would not use these approaches to start with.

My Assumptions

I understand your point here but my example is only to illustrate the external variables. For me I try and maintain standard parameter names and often remember them, however the way I collate my macros into packages, before I use, them I usually read the syntax. But I often use the other forms.

  • My first assumption is my macros/procedures and functions operate on current tiddler
  • My second assumption is if the fieldname is likely to vary I have a fieldname or field parameter.
  • If I don’t remember the order, I just name each parameter
  • If the most common use is obvious the parameter have default values, including computed ones.

When we had only macros, I had a view template that on seeing \define would display that line and thus show the names and parameters. And could search for \define macroname so the syntax was only a search away.

  • I need to revisit this for 5.3.x or include functions procedures and custom widgets.

I did toy with <<procedure ?>> where if used displayed all the parameters and suggested values, or a toggle that would show such parameters.

I think you do, too. Sound intriguing.