Blogs

Uniface 10: The new procedural declaration of component variables

By Uniface Test posted 09-02-2016 05:00

  

(Original creator: alex.besteiro)

For Uniface 10 we are constantly reviewing and redefining old concepts, aiming for consistency and reusability while looking for new ways to improve your developing experience and enforce good development practices. This is what the new procedural declaration of component variables is all about.

If you are a developer coming from older Uniface versions you probably are familiar with the 'Define Component Variable' form, that allows the creation of variables with component scope and an optional display format. You certainly also know how to declare local variables using the variables block in your triggers and modules. In that case, you might have wondered while accessing the 'Go To -> Component Variables...' menu, selecting the data type from a dropdown list, entering a display format and filling a couple of optional fields... Why can't I do this from my code, like with local variables?

There are some advantages in the form approach, but the truth is that variables declaration is a fundamental part of coding, as it is the concept of variables scope. It is not only logical but even expected for new developers that variable definitions at component level have component scope. Furthermore, is it a more efficient method of defining and inspect variables. If you think that way, congratulations: You got it! If you are not convinced yet, please keep reading.

In version 10 (10.1.03 and higher) component variables are declared using the 'variables' block in the Declarations container of a component. You may declare as many blocks as you want, taking in account that variable redefinitions are not allowed by the compiler. The display format definition takes place -optionally- in the declaration itself, so it looks like this:

\The display format is defined using the 'DIS(<format>)' syntax, as it used to be in the 'Define Component Variable' form. You can find extensive documentation about display formats in the Uniface Library. As for the missing fields 'Description' and 'Comments'... Well, nothing better than in-code comments to describe our variables.

An actual example of component variables block could be:


Simple, isn't it? But here is the best part: since component variables are declared procedurally, you can use all the precompiler directives to generate, influence and include component variable declarations. Move your variables block to an include proc to make it reusable. Use the #for directive to quickly generate variables. Make use of definitions to avoid typing complex display formats and ensure consistency between components. Create a snippet library with display formats to quickly insert them into your code.

If you are still hesitant don´t despair: We keep working on great ideas to make code editing faster and easier, as well as providing code and compiled objects information on the editors. But more about that in another blog post...

In the meantime, leave us a comment with your thoughts about the new system! What do you like? What can be improved? How would you make the best use out of it?

24 comments
5 views

Comments

05-10-2019 08:05

Hi Istiller,
just to jump into the conversation, while Alex is taking is vacation (and not working I hope :-) ).
Indeed, some hooks in the Migration Utility might valuable to you guys to influence the migration based on your needs, maybe provide the sources of it. The last part is not so easy for us, because the Migration Utility relies too much on generic IDE functions; we have to do something with that first.
That being said, you could already prepare you v9 repository for migration to v10, anticipating on what you want to have in v10. For this use case you mention here, that would indeed require a custom script (fast form) that preps your component variables.
We are actually also considering a new Search and Replace for the IDE, which could (should?) be way more powerful then a simple text search (syntax sensitive).
Keep this post coming guys, it helps us setting priorities on the next steps.
Gerton

05-10-2019 08:05

Hi Alex
I got another challenge :-)
We do have a few component local variables that are included in every component (we call them instance-properties)
After migration, this variables are also in the component variable declartion block.
So I why not using an include ...
But how to eliminate the variables defintions in the variable block?
Do yo have an proc (or new:script) interpreter wich can in a proper way cut off this parts?
Or have we to write our own scipt tools?
Ingo

05-10-2019 08:05

Hi Knut,
Indeed, it could be cumbersome to handle the generated code for big applications like the one you describe. If we think about new applications I believe the new system would simplify the generation and organization of such an amount of component variables, using precompiler directives.
In your particular case it shouldn't be difficult to create a utility to reorganize those variables based on the name into the UXREGS table before the migration is performed. After the migration it could be more complex to do it using text tools, because of the different type of variable declarations in the text (with or without display format, multiple declarations in one line...).
We can't assume that the preferred order for component variables is alphabetical instead of order of creation, so I don't think we should do that ourselves during the migration process.
As a developer I also like the idea of variables' initialization, a practical good practice! Feel free to add this suggestion and any others to the Wishlist (http://u.uniface.info/wishlist/).

05-10-2019 08:05

I'm in two minds on this change...

The 'old' way has the distinct benefit of having the variables sorted alphabetically.
The 'new' way can become a real mess, real fast if/when you have a lot of component variables
(we have 60+ components with 100+ component variables, 5 with 300 or more).
Especially with regards to 2) - will there be a reformat / sort option available to
'reconfigure' a messy layout and 'resort' the variable names?
As an aside, a 'feature' that would be nice to have is to be able to 'init' a vaiable at the
time of declaration (rather than having to do it in code later on...)
variables
date DIS(DD/MM/YYY) ORDER_DATE := $date
endvariables
Knut

05-10-2019 08:05

I don't think that this can be done "per block", but must be done "per variable".
So this is an awful lot of "noise" to write by the uniface customer just to define 2 component variables:
#ifundefined MYCPTVARS
#define MYCPTVARS
variables
#ifundefined cv_vMyVar1
#define cv_vMyVar1
struct vMyVar1
#else
#warning vMyVar1 already defined
#endif
#ifundefined cv_vMyVar2
#define cv_vMyVar2
float vMyVar2
#else
#warning vMyVar2 already defined
#endif
endvariables
#endif

05-10-2019 08:05

Hello Ingo,
I see your point. We are aiming to improve code editing and navigation in several ways. I ignore the particular purpose of your code-manipulation helper components, but I hope the need for those becomes less and less.
About the component variables block:
1) It is not possible to define display formats for local variables.
2) It is also not possible to define component variables from a global proc.
3) The behavior remains the same, only the way to declare them has changed.
4) These changes are limited to component variables, which are accessed with '$' symbols (not allowed as first character of a field), so the described case doesn't apply. For other kind of variables there are compiler improvements planned so far.

05-10-2019 08:05

We decided to be cautious on this one and prevent duplicates (overwriting). For modules, on redefinitions, we have a powerful method for specialization. Also thanks to the new Compiled Modules Information (CMI) we know exactly and easily which module has been used from all the declarations. Specialization of variables is not that common and we lack the tools to identify the compiled variable. We will keep your comments in mind for future improvements.
In the meanwhile I would suggest a similar approach to the one you propose. By declaring groups of variables in #includes together with some #define you can achieve a similar behavior.
Include MYCPTVARS:
#ifundefined MYCPTVARS
#define MYCPTVARS
variables
string vMyVar1
numeric vMyVar2
endvariables
#endif
Include MYFRMVARS:
#ifundefined MYCPTVARS
#define MYCPTVARS
variables
struct vMyVar1
float vMyVar2
endvariables
#endif
It would be still responsibility of the developer to create sensible variables blocks, but at least you can prevent the compiler complaints.

05-10-2019 08:05

You're welcome. Don't hesitate to post any question for the community.
Component variable declarations differ from local (modules) variables in three main things:
a) They have component scope, meaning they are available for any module defined in that component.
Local variables, in the other hand, are only available to the module (trigger, operation, entry) where they are defined. Global variables have a bigger scope than component variables, being available on all the components using the library where it is defined. So:
Global > Component > Local
b) You can define a display format, to set some specific representation of the value. This is also possible on Global Variables.
c) The syntax to access the variable is different:
- variableName for local variables
- $variableName$ for component variables
- $$variableName for global variables
On this topic I recommend you to take a look to the next topics in the Uniface Library: variables, Variables, component variables. You can find more information about the variables scope concept in Google.
Hope it helps.

05-10-2019 08:05

Hi everyone
Nice and not so nice in one :-)
A great advantage of Uniface (is/was) that the model behind your code was separated into different tables. So by SQL one could search and modify much of the behaviour of Uniface. Now you have all in one (or a few) big text fields.
Comparing a component to another component is much easier if code is splitted (by named triggers) in different fields/tables.
And, as Uniface is very slow one working with strings, manipulating code by other helper-components will be much more slower the now.
So I hope, Uniface in future will not put all code in one big text field :-)
A few questions to the new variable block:
1) Is it also possible to define a format for entry-local-variables?
i.e. this variables blocks can be inserted also in ENTRY/TRIGGER/OPERATION?
2) Could this kind of variable block also used in global procedures?
3) in operations , one can use component local variables as parameters
3a) Ist this stille possible?
3b) and/or can we define parameters with format in a parameter-block?
4) Is a name a variable or a field?
"v" as prefix for entry-local-variables is the same convention then we do.
But this is not for clarity (where is as variable defined). We use this to avoid conflicts with fieldnames.
Will there be anything to say the compiler in a strict way, if a variable is a variable and not a field?
This is very important i global procs
e.g.
A Field MOUNT in table TBL1
-- Global proc --
variables
numeric AMOUNT
endvariables
$01=AMOUNT
$02=MOUNT ; By mistake without A
If one compiles this global procedure, there will be no warning.
If at runtime a component with TBL1 painted in access, the value from MOUNT.TBL1 will moved into $02
So, if you allready change the syntax for variables-blocks, just include two things:
4a) #pragma strict_variables ; every name should be treated as a variable and never as field. Scope is ENTRY&co
4b) new type "FIELD" to say the compiler that a few names a indeed field
So the code become
-- Global proc --
#pragma strict_variables
variables
numeric AMOUNT
field DESCR
endvariables
$01=AMOUNT
$02=MOUNT ; By mistake without A -> Gives ERROR at compile time "variable not found"
$03=DESCR ; Gives no warning and will resolved at runtime to a field DESCR (if avaible)
Just my 2 cents :-)
Ingo

05-10-2019 08:05

Thanks for the welcome Alex,
and sorry for my stupid questions.
So this kind of declaration help programmers to represent a value in the correct manner ?
Thanks
Claudio

05-10-2019 08:05

Duplicate definition found on componentvariables:
perhaps it would be benefitial to implement an overwrite information logic as we have in entries
and use the last specification for the compile.
The coder can react upon the message and this would make handmade safegards unnecessary:
#ifundefinedcomponentvariable ABC
string ABC
#endif ; undefinedcomponentvariable ABC

05-10-2019 08:05

Hi Alex,
so we have to pay a lot of more attention reading the cptlst file as depending on the position of some variable-block this will cause very different results.
I would prefer at least the keyword "componentvariables" to give an optical hint.
I use to compose component variables from a couple of groups (with a little utility), so I expect using some #includes in the future.
And as I have to handle duplicates myself, it would be helpful if I can check the existence of componentvariables with something like
#ifundefinedcomponentvariable ABC
string ABC
#endif ; undefinedcomponentvariable ABC

05-10-2019 08:05

Hi Ulrich,
Note that the location of the variables block is the one causing the different behavior (meaning different scope), not unlike many other programming languages. Only the variables block defined in the Declarations container of the component object generates component variables.
In that sense, the location might be new but the concept remains the same as in local variables: the fact that the variables block is defined within a module declaration makes it local to that module and the fact that component variables are defined at component level makes them available in the whole component.
Although we considered different keywords for the variables block we figured that the purpose is the same. We hope customers will adapt quickly to the new way and because of the different accessing method, without surprises on their code.

05-10-2019 08:05

Then you think just like us. :)
Precompiler directives can be very powerful for generalization purposes, either you are generating code with #for or creating switches with #if, #ifdefined, etc.
We love people making the best use of the tools we provide. Please, keep sharing your suggestions!

05-10-2019 08:05

First of all, welcome to the Uniface world. :)
The blog post is about the new declaration method for an already existing type of variable. We find it more consistent, efficient and flexible than the current method (Component Variables form).
I am not sure I get your question, so let us know if this helps... When you mention the data visible in the components you are probably referring to the data in the fields, for instance 'NAME.CUSTOMER'. If that's the case, the misinterpretation could come from the choosing of variables in the code examples, which are indeed quite data related.
Variables are just placeholders for data of a certain type that we can use for whatever purpose. A typical case could be counters or totals that keep the summation of several records of an entity. In principile you won't put fields' data in a component variable, as it is directly accesible from the occurrence, but there could be nice applications to it.
For instance, as clipboard: you could create an entry to copy a record to a set of component variables and later on 'paste' it by reading those variables. Or, like in the post's example, for formatting. Suppose you have and entity with a field SALARY of type numeric and the component variable declaration:
float DIS(99999P99 US$) vSalaryDollars
Then you assign:
$vSalaryDollars$ = SALARY.EMPLOYEE
And print:
putmess "Salary is %%($vSalaryDollars$)."
You get the formatted value:
"Salary is 54321.00 US$"
Of course you could do the same in several ways. This is just another example of usage. Experiment and let us know!

05-10-2019 08:05

Hi Alex,
so we have a real problem that the same code in variables-endvariables will create different results.
Most books like "clean code" etc. will call this a severe violation of the "least surprise" principle.
To avoid confusions, I recommend to specify component variables not in variables-endvariables block, but as precompiler directives.
#componentvariables DataType {DisplayFormat} VariableName {, VariableName2 ... {, VariableNameN}}
Beauty: one is able to specify a component variable immediately in the sourcecode wherever you need one persistant storage

05-10-2019 08:05

Short answer: As usual.
Although the variable name in the declaration is not between '$' symbols, the accessing is still the same. Same applies to Global Variables and their '$$' prefix. So you can even use the same variable name for a component and a local variable and access them in the local module: vMyVariable, $vMyVariable$.

05-10-2019 08:05

Thanks for your feedback.
Indeed, we can provide type information as part of the variable name. We encourage users to create their own coding conventions to make code more legible. For this reason I think the proposed method is not always desirable, as we would be forcing a specific convention. The code, particulary the variables blocks, would become less readable and compilation would differ depending on the selected option.
To provide variable-type information in the code your first advice is good enough. We will try to assist users with variable declarations using other methods.

05-10-2019 08:05

Defining variables through the 'Define component variable' form is no longer possible, it can only be done through procedural declarations.
Component variables from existing applications will be migrated, respecting datatypes, display formats, names and even the comments.

05-10-2019 08:05

I love the idea of declaring these component variables within a #for loop.
I can see this being used in global include procs for component template design.
I give it a thumbs up!

05-10-2019 08:05

I'm new in the Uniface world but....what's the usefulness of that?
Why I must declare a variabile to handle the check of input data when the input data is done by visibile components?
It's possibile to assign the variable type to a visible component to check the input from user?

05-10-2019 08:05

How do one accessing these component variables from code is informed about it's scope?
right now we have $componentvar$ to make clear this has some permanent existence,
while vMyVariable dies with the end of the entry/trigger/operation.

05-10-2019 08:04

Defining a variable name with the prefix "v" seems like a wasted oppertunity. Why not use the datatype instrad (so "c" in front of a combined date and time)... in fact, whyadd an option to allow the initial letter to determine the data type? Didn't they once do that. in Fortran?

05-10-2019 08:04

Will the current convention of declaring component variables still be supported? Will the current component variables be automatically migrated when moving to Uniface 10 from a previous version of Uniface?