I was reading Gerton’s blog on  Structs and its references, and I realized that he skillfully danced around some complex topics to avoid being distracted from his main focus. It is the only way to get your message across of course, but I was tempted to tackle one of those other tricky topics so I would like to show what happens exactly on struct assignment. Let’s borrow one of Gerton’s structs and assume we have a struct variable myStructVar1, pointing to a struct in Uniface memory: PD1 Now, the following two assignments have very different effects: [uniface] myStructVar2 = myStructVar1 ;- assignment (1) myStructVar3 = $newstruct myStructVar3->someNode = myStructVar1 ;- assignment (2) [/uniface] I’m not talking about the obvious difference that the structure of myStructVar3 is slightly different, but what do these assignment statements do? The crucial difference is that the first assignment is to a Struct Variable, while the other assignment is to a Struct Member; this triggers other behavior. In the first line, myStructVar2 simply becomes another reference to the struct in Uniface memory, which was already referenced by myStructVar1. Consequently, if the struct is modified through myStructVar1, the effect is seen in myStructVar2 and vice versa, because they point to the same memory space. PD2 The assignment to a member of myStructVar3 is more complex. Structs cannot contain references to arbitrary other structs, so myStructVar3‑>someNode simply cannot refer to that same memory space. Instead, a copy is made of the struct referred to by myStructVar1, and it is created as a child Struct Node of myStructVar3. The assignment also dictates that the name of this node is someNode, so the copied struct is renamed from its original name (Node1) to the new name (someNode). PD3 However, you may want to inherit the name from the assigned struct. You have two options to achieve this effect: either explicitly use the name of the source struct in the assignment, or prevent renaming. First, let’s explicitly use the proper name on assignment: [uniface] myStructVar3->"%%(myStructVar1->$name)" = myStructVar1 ;- assignment (3) [/uniface] Alternatively, do not use any name in the assignment, but simply append a new child without any name assignment: [uniface] myStructVar3->*{-1} = myStructVar1 ;- assignment (4) [/uniface] The syntax of this last assignment may puzzle you:  ->* is an operator which returns all children. The index between curly braces specifies where to insert or append the new node. A positive number is an absolute location among the existing child nodes, while -1 simply means: append at the end of the child node list. Then there is one more thing: suppose we do not want to create a copy on assignment, suppose we DO want to assign the struct referred to by myStructvar1, as a member to some other struct, referred to by myStructVar2. This is a very valid requirement, as the effect is that other references to the struct will notice the effect of the assignment. How to do this? It is quite simple, but requires you to think out of the box: do not assign the struct itself, but instead assign its parent: [uniface] myStructVar1->$parent = myStructVar3 ;- assignment (5) [/uniface] PD4 Of course, if the struct referred to by myStructVar1 already had a parent, this assignment will detach the struct from its original parent. In summary the different options, with a short one liner to characterize the effect: [uniface] myStructVar2 = myStructVar1 ;- (1) copy a reference to the struct and assign the struct myStructVar3->someNode = myStructVar1 ;- (2) copy, rename and assign the struct myStructVar3->"%%(myStructVar1->$name)" = myStructVar1 ;- (3) copy, (rename) and assign the struct myStructVar3->*{-1} = myStructVar1 ;- (4) copy and assign the struct myStructVar1->$parent = myStructVar3 ;- (5) attach the struct as a child [/uniface] Pieter van Dijk Developer in the Uniface Lab During the development of Uniface 9.5, I contributed to the design of the Struct. Currently I am actively using the Struct while working on the new version 10 IDE.

19 Comments

  1. Hi Peter, thanks for this more technical contribution. Most of us are used to work with lists to hold structures since they were introduced wint Uniface 7. We have indexed lists which are arrays, associated lists which are hashmaps, and list-in-list constructs. and we have these beautiful List Handling in Proc: $item(), $itemnr(), forlist and forlist/id,$idpart,$valuepart,... Is it possible for you to give us some comparison between the list-world and the struct-world?
  2. Hi Uli,

    That is a nice question. Even though the main reason for introducing the struct was to enable the Uniface proc language to handle any (external) XML stream, it is an additional benefit that structs can be used as a replacement for Uniface lists. So how does the struct compare to the Uniface list?

    The list can be used to hold complex structures, but this only works well for read-only structures - the problem with lists is that they are difficult to modify. Only the top level of a nested list can be updated (adding/deleting or updating list items), This means that an update to a lower nested item can only be done by first decomposing the list down to that level, then update it and finally reconstruct the list. Moreover, it is not possible to insert an item into a list: to achieve this you need to build a new list with the additional item inserted on the right location. Such techniques are cumbersome and expensive. The structure in contrast can be updated at any level: it is possible to add, insert, delete and update items.

    Another problem with lists is that also for the Uniface kernel they are inefficient to handle. One reason lies in the way they are implemented: they use separator characters which depend on the nesting level. This means the whole (sub)list needs to be parsed and updated when popping or pushing takes place - such as when using $item. We cannot fix this, as the physical structure is exposed and public; many 4GL developers manipulate lists using this knowledge.

    Then, structs come with a whole set of new features:
    • Structs have full type support while list items are always strings. Struct members can be of any type: 'struct' itself, but also all scalar types that Uniface supports: 'numeric', 'float', 'datetime' etc. etc.
    • $tags allows adding meta data on struct nodes; we use this feature ourselves when converting lists from one format to another (structToXml, entityToStruct etc.); in this way we make round-trip conversions possible without loss of information. $tags can also be used by 4GL developers.
    • powerful expressions allow mass updates on structs. For example:
      myStruct->*->*->$parent = myOtherStruct
      will remove all grandchildren and unite them under a new parent struct
    • myStruct->client->$name = "Client"
      renames all children of myStruct called 'client' to 'Client'
    • The struct is a reference data type, which means by just passing a reference a massive amount of data can be made available to a different context.This prevents copying of data, which means better performance and it saves memory.
    • Even though passing data by reference is quite efficient, sometimes you prefer passing by value: on parameters of type struct this is supported too.


    Lists currently also have some advantages over structs:
    • at this moment there is no sort functionality on the struct, similar to sort/list there is no equivalent for 'forlist', such as a 'forstruct'
    • Structs currently have no literal notation, so that it is not possible to assign a string to a struct which then creates a struct for you. As an alternave it is possible use XML as literal notation and convert it with 'xmlToStruct' to create a struct. With this technique you are bound to the syntax restrictions of the XML format (such as restrictions on element names).


    • Altogether, we personally believe the struct is superior over lists: specifically due to its strong manipulation power and its reference efficiency. In the new version 10 IDE we use the struct extensively for data manipulation and data exchange. This would simply have been impossible with lists.
  3. Sorry for the awful layout of my post: can't get it to pick up my tags and stuff. Hopefully I can correct this later.
  4. As all text formatting seems to fail, hereby a short and thus more readable summary of my earlier post. Nested lists cannot be easily updated (add/remove/update of items). Inserting items is not support at all. This all works fine for structs. Structs are more efficient to manipulate, both in 4GL and 3GL (=the Uniface kernel). Advantages of structs: data type support, $tags for keeping meta data, powerful expression syntax, the struct is a reference type which makes efficient use possible. Advantages of lists are the sort/list and forlist statements, which currently have no equivalent in struct, and the option to populate lists using a literal notation. In general we regard structs as more powerful and efficient.
  5. Hi Peter, a lot of uniface developers already work with lists to hold structured information and it's not that hard to get a nested list, manipulate this, and load it back to the main construction. A lot of developers already use the some universal proc for this handling and CRUD on nested lists is easy !!. ******************* my question was more about code EXAMPLES for struct which do the same things we already do with lists ****************** how does a $item("myhaskey",mylist) will look like in struct world? ************** P.S. don't worry about lost layout, just publish your reply as a forum entry, as a lot of other members of unifaceinfo.com has done after their call for supporting layout here was not heard.
  6. Hi Pieter, overlooked the "read more" in your post, so my later reply does not reflect your code examples. ****** But as a list is just a string, you can even insert "in between" of an indexed list. because of the slow uniface string handling it may take longer in bulk processes, but it is possible. ******* and all the nice support of put/getlistitems (/occ /field /local /global) are used very often and enable "easy coding" ******* For the moment in time, struct is deeply connected to XML and component data structure, if I read your lines right, isn't it?
  7. Hi Peter, I just have layouted and printed your first reply to have a more readable version. Agreed, the implementation of uniface lists as strings cause a lot of internal performance problems; part of them caused by the dual nature as an array as well as a hashmap (indexed/associated). But on the other hand, lists allow very easy dealing with entities and widgets: a retrieve/e and a putlistitems/id is all you need to populate yor dropdown list from the database. ********* will this be a next step to complete the struct area (including some struct2JSON/JSON2struct and struct2LIST/LIST2struct) ??
  8. Hi Uli, It is true that you can create a library of functions which will do smart updates in (nested) lists, with which you can do CRUD on such structures, and this is a good thing: you can extend the expression power of the language without being dependent on Compuware. The issue is that list handling becomes inefficient in this way and performance issues will impose limitations to what you can do with it. The struct offers a new approach that offers enough expression power to do all necessary maintenance on structured data.As you found out meanwhile there are some coding samples in the blog; clearly the counterpart for $item("key", mylist) is the arrow operator: mystruct->key.
  9. Even though it all started with the requirement to map XML onto Uniface, I would not say that the struct is deeply connected to XML: the struct fully supports conversion from / to XML, but it can also be used for mapping other data structures, like JSON. However it is not just meant as an exchange tool, the struct is also a useful tool to hold and manipulate complex data inside Uniface. With the struct we delivered a much more generic solution than just an XML mapping facility.
  10. Is there then a counterpart for $item(V_variable, mylist)? That was the bit I couldn't pull out of the documentation when I looked. It seemed the key values had to be hard coded into the code.
  11. Yes there is: use a quoted string and string substitution as identifier. So that would be: mystruct->"%%(V_variable)". Note: another effect of using a quoted string is that it enables you to use any character in a struct member identifier, including spaces.
  12. For the mapping between struct and entities (fields) there are the structToComponent and ComponentToStruct statements, they are the counterpart of getlistitems/putlistitems. The difference however is that the struct-statements can work on the complete hierarchy of the painted entities (comparable with xmlload/xmlsave and webload/websave). About other other conversion statements: currently we are working on structToJSON / JSONToStruct, the list conversion is not in the current planning. It is not too difficult though to write such conversion routines yourself .
  13. Hi Peter, thanks for your pretty fast reply with some examples In fact there are a lot of list-enhancements-functions in production already; not because it's so performant or so, but because there was a necessity which could not be implemented with the "standard" uniface language. There have been a lot of requests for an array or a hashmap in the past and (back since the 7.2.04 champions meeting in Amsterdam) a lot of requests to improve the functionality of uniface lists. Because of performance reasons, I think a lot of uniface customers would convert to the struct if they can do the same things as they can do with lists. But $item() is CASEINSENSITIVE, I doubt that this is the same in your struct->key example, so there may be more "hidden" obstacles to carefully work out.
  14. Hi Uli, You certainly have a point here: if you want to convert an implementation with lists to a new implementation with structs that will require effort. The names of struct nodes are indeed case sensitive (we had no choice: the outside world often is, like XML), so it does matter how you refer to them. It would be an interesting exercise to rewrite a typical list solution to a new struct implementation - in order to learn from it where the problems are. I can also imagine that structs will more typically be used in new (parts of) applications.
  15. It is not too difficult though to write such conversion routines yourself .
    It's not that hard for me to implement the "missing link", but thats exactly the reason why we have such a universe of uniface-list extensions already. I would prefer if this list2struct conversion would be done by "the lab". This supported standard would help even migration of existing list-based  implementation concepts because one can use the best of both worlds with ease.
  16. Oh absolutely, it is better if we as tool provider set the standard. I only meant to indicate that if we do not (since our resources are limited) writing the conversion routines for lists in 4GL is quite doable. This is very different for conversions from / to external data formats like XML and JSON, as this requires parsing of the external format.
  17. Hi Pieter, we all face limitations in our resources so we have to make wise decisions what we need and what we have to invest to make it happen. *** I'm prepared to do my own converter (and publish it under the dITo initiative so we may have a single implementation, just as USEQREAD). *** But I need your help because I have no concept how to traverse a struct tree. *** In a list, I visit all the items (forlist), if the item is a list in its own rights($itemnr(1,list)!=list),  I start a recursion to process this sub-list. *** It would be nice if you can give me an example how this can be done in the struct world ***
  18. Hi Uli

    A great initiative. For some practical examples on how to work with structs I'd like to refer the the documentation: search for the topic 'Struct Code Examples'. There is a sample how to traverse through a struct recursively (look for the entry struct_loops).

    good luck!

    Pieter

  19. Hi Peter, thanks for pointing me to the Example: Looping Over Structs looks like there is all I need at least for a start. Uli