B4J Question Immutable Map

aeric

Expert
Licensed User
Longtime User
While developing Pakai v6, I faced an issue that I wish I don't want to talk about.
I tried numerous time but still failed to achieve a concept that I thought was easy.

I have forgotten that Map is always passed by reference.
I wished we have something like immutable map in B4J or B4X where I can declare a map, assign the value once and use many times which the value is not going to change.
Or we can pass by value to the map.

What I was trying to achieve is once I have generated a "static" html template (using MiniHtml), I want to keep the object inside a memory so I don't need to regenerate it again every time a request is made. I think this will boost the performance or make the app more efficient.

I wanted to use a global map in the server app Main module or maybe a thread safe map if it is necessary.
I like map because I can retrieve the value by a key very fast.

When I tried to assign the generated tag object into a map, the value can mutate and reference back to the map. This is not what I want.

For example, I have generated a html table row template look like this:
<tr><td></td><td></td><td></td></tr>

On first loop, I will pass the id = 1 and other values from the database query to this template object.
<tr><td>1</td><td>AAA</td><td>0001</td></tr>

On second loop, the template has already mutated with the updated value above. It is no longer the original template.

I tried to use KeyvalueStore.
However, the value is a non primitive type or more specificly the Tag class of MiniHtml. it will failed to deserialize from KVS.

I can convert the object to String first before storing to KVS but this will defeat the purpose. I have to parse the String and convert it back to Tag object. I think this will involve some CPU computation again. Why not just use something we have already processed?

So, I think immutable map is a good feature to add into B4J.

Or does any member have any alternative solution that is easy to implement?
 
Solution
B4X:
Private Sub Test8 ' Magma (edited)
    Dim trTemplate As Tag
    trTemplate = Tr.init
    Td.up(trTemplate)
    Td.up(trTemplate)
    Td.up(trTemplate)

    Dim Cache As Map = CreateMap("trTemplate": trTemplate)
        
    Dim Data As List = Array(CreateMap("name": "AAA"), CreateMap("name": "BBB"), CreateMap("name": "CCC"))

    Dim tbody1 As Tag = Tbody.init
    For Each item As Map In Data
        ' use DeepCloneTag <> Clone
        Dim trRow1 As Tag =  Cache.Get("trTemplate").As(Tag).DeepCloneTag(Cache.Get("trTemplate").As(Tag))
        trRow1.Child(1).text2(item.Get("name")) ' use text2 to be sure 
        LogColor(trRow1.Build, -16776961)
        trRow1.up(tbody1)
    Next
    
    LogColor(tbody1.Build, -65536)
End Sub
...

aeric

Expert
Licensed User
Longtime User
CreateProductsTable need to be a Tag ? can be a string ?

I can convert the object to String first before storing to KVS but this will defeat the purpose. I have to parse the String and convert it back to Tag object. I think this will involve some CPU computation again. Why not just use something we have already processed?
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
printme returns string - right ?
for test purposes return string from CreateProductsTable - to check
Yes, it is a method for debugging purpose.
My objective is to "hold" the Tag object and update any children properties until it is finally ready to turn into a String.
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
Map is fine for storing "static" values.
If I want to store a "template" into a map that need to generate "dynamic" output such as html table rows with different values then there is something I need to take care.
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
Map is fine for storing "static" values.
If I want to store a "template" into a map that need to generate "dynamic" output such as html table rows with different values then there is something I need to take care.
not saying something different... but check if it returns it right... and then... if returns ok as string... will search for it... why
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
That is right, you can't serialize maps that have classes (in your case the class "tag") as objects.
You may be able to fix it by wrapping the critical elements of "tag" into a standard object then storing that in the map.
@Magma suggestion to use a string would work, but as you say it would defeat the purpose of quick access to the components of the tag.

I believe the class "tag" needs its own internal structure that becomes its external representation of components.
It should be a structure that can be serialized - I suggest a map. Please look again at post #27.
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
That is right, you can't serialize maps that have classes (in your case the class "tag") as objects.
You may be able to fix it by wrapping the critical elements of "tag" into a standard object then storing that in the map.
@Magma suggestion to use a string would work, but as you say it would defeat the purpose of quick access to the components of the tag.

I believe the class "tag" needs its own internal structure that becomes its external representation of components.
It should be a structure that can be serialized - I suggest a map. Please look again at post #27.
I wish there are other developers join me in open source to help and contribute into MiniHtml library.
I have created a Clone method and it seems it can create a copy of a tag object.

B4X:
Public Sub Clone As Tag
    Dim twin As Tag
    twin.Initialize(mTagName)
    'twin.Parent = mParent
    
    Dim childrentwins As CopyOnWriteList
    childrentwins.Initialize(mChildren)
    twin.Children = childrentwins.GetList

    Dim attributestwins As CopyOnWriteMap
    attributestwins.Initialize(mAttributes)
    twin.Attributes = attributestwins.GetMap
        
    'twin.Siblings = mSiblings
    'twin.Attributes = mAttributes
    
    twin.Flat = mFlat
    twin.mId = mId
    twin.mName = mName
    twin.Mode = mMode
    twin.mIndentString = mIndentString
    Return twin
End Sub

But when I do more tests by looping through a map then it failed.

What do you mean by "internal structure" ?
You mean I have to recreate the Tag class by only allow map type?
It may work but then I can't chain and use it like an object oriented class.
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
by only allow map type
No I mean that in addition to the elements you have, add a map Property called for example "AsMap".
Then when you modify the elements of a "Tag" also modify AsMap but only add standard elements.
For example: AsMap.put("Mode", mMode)
For previous defined children, siblings, and parents, put a list of their "AsMap"s.

A tag can be saved using its AsMap and reconstructed from AsMap.
I know it is complicated but it might work.
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
Since this is an addition to what you already have, all the stuff that is working now will still work the same way.
It is only when saving, cloning, restoring that the extra internal structure comes into use.

You could add methods to the tag class to do these tasks.
 
Upvote 0

cklester

Well-Known Member
Licensed User
I have forgotten that Map is always passed by reference.

I'm not sure if this helps, but the comments here seem to indicate a map is immutable, at least in that circumstance. Chris2 then writes this, and Erel confirms here that, "LocalMap is actually an independent copy of MyMap" for the code example, "Dim LocalMap As Map = MyMap." So, could you use that construct?
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
I'm not sure if this helps, but the comments here seem to indicate a map is immutable, at least in that circumstance. Chris2 then writes this, and Erel confirms here that, "LocalMap is actually an independent copy of MyMap" for the code example, "Dim LocalMap As Map = MyMap." So, could you use that construct?
I did get good result for simple value inside the map using new Collections or by making copy of the object but once the object become larger with more layers, with object of classes inside, it doesn't work anymore.
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
I did get good result for simple value inside the map using new Collections or by making copy of the object but once the object become larger with more layers, with object of classes inside, it doesn't work anymore.
I would love to see a simple presentation of your idea (for example the map's structure as log), with no extra libs, using immutable maps - objects, to see the difference of time computation (calculation). Will be a nice tutorial - i am sure will help you and us (all the forum) to be better.

I am loosing some points. I am sure that if you want to use them - there are benefits - May be can't understand them.

I am Sharing the whole idea: We must make some Zoom Meetings as Workshops - present some ideas, work together - taking notes - may be in a google drive docs / sheets / etc. Share the knowledge could help each other / I am totally supporting this.
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
I would love to see a simple presentation of your idea (for example the map's structure as log), with no extra libs, using immutable maps - objects, to see the difference of time computation (calculation). Will be a nice tutorial - i am sure will help you and us (all the forum) to be better.

I am loosing some points. I am sure that if you want to use them - there are benefits - May be can't understand them.

I am Sharing the whole idea: We must make some Zoom Meetings as Workshops - present some ideas, work together - taking notes - may be in a google drive docs / sheets / etc. Share the knowledge could help each other / I am totally supporting this.
Do you mean a test project to demonstrate
1. the issue I am facing with Map
or
2. the feature I want to include into Pakai framework?
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
Do you mean a test project to demonstrate
1. the issue I am facing with Map
or
2. the feature I want to include into Pakai framework?
no targetting at Pakai framework.. only

yes, not only a "test-project" - but will be nice to talk how do you think the whole idea - the benefits...

ofcourse it is nice also to have "products-services" presentations..
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
no targetting at Pakai framework.. only

yes, not only a "test-project" - but will be nice to talk how do you think the whole idea - the benefits...

ofcourse it is nice also to have "products-services" presentations..
For anyone who want to do testing on MiniHtml library, please fork my repo from GitHub.

For Pakai server v6, it is already a stable version and ready to use for developing web app.
However, the sample code demonstrated in this project template generates the html on demand.
I think this is against the DRY principle. It can be improved in term of performance and make a better developer experience.

My idea is to store a tag object in memory that is generated using MiniHtml template and then reuse it.
I can also use other caching solution available for server such as KVS, Caffeine or Lettuce.

The template object when retrieved to use further must not changed from the original form.
This is not true as I what I am facing now.

Check this code:
B4X:
Private Sub Test4
    Dim Temp As Tag = Paragraph.init ' <p></p>
    Dim M1 As Map = CreateMap("Temp": Temp)

    Dim D1 As Tag = Div.init
   
    'Dim P1 As Tag = M1.Get("Temp")
    Dim P1 As Tag = M1.Get("Temp").As(Tag).Clone
    P1.text("Demo 1").up(D1)
   
    'Dim P2 As Tag = M1.Get("Temp")
    Dim P2 As Tag = M1.Get("Temp").As(Tag).Clone
    P2.text("Demo 2").up(D1)
   
    Dim PT As Tag = M1.Get("Temp")
    Log(PT.Build)
   
    ' Log 1 (without Clone) : <p>Demo 1Demo 2</p>

    ' Log 2 (with Clone)    : <p></p>
End Sub
 
Upvote 0

Magma

Expert
Licensed User
Longtime User
For anyone who want to do testing on MiniHtml library, please fork my repo from GitHub.

For Pakai server v6, it is already a stable version and ready to use for developing web app.
However, the sample code demonstrated in this project template generates the html on demand.
I think this is against the DRY principle. It can be improved in term of performance and make a better developer experience.

My idea is to store a tag object in memory that is generated using MiniHtml template and then reuse it.
I can also use other caching solution available for server such as KVS, Caffeine or Lettuce.

The template object when retrieved to use further must not changed from the original form.
This is not true as I what I am facing now.

Check this code:
B4X:
Private Sub Test4
    Dim Temp As Tag = Paragraph.init ' <p></p>
    Dim M1 As Map = CreateMap("Temp": Temp)

    Dim D1 As Tag = Div.init
 
    'Dim P1 As Tag = M1.Get("Temp")
    Dim P1 As Tag = M1.Get("Temp").As(Tag).Clone
    P1.text("Demo 1").up(D1)
 
    'Dim P2 As Tag = M1.Get("Temp")
    Dim P2 As Tag = M1.Get("Temp").As(Tag).Clone
    P2.text("Demo 2").up(D1)
 
    Dim PT As Tag = M1.Get("Temp")
    Log(PT.Build)
 
    ' Log 1 (without Clone) : <p>Demo 1Demo 2</p>

    ' Log 2 (with Clone)    : <p></p>
End Sub

1763195245142.png

just downloaded from prelease folder the new b4xlib - but:
.clone ?
 
Upvote 0
Top