Hi Rick,

Maybe no fatal errors, but you should check the status codes in case they contain one.  Couple of (untested) change suggestions below to try first...

set zm1 = ##class(EnsLib.HL7.Segment).ImportFromString(zm1str,.sc,pRequest.Separators)
$$$QuitOnError(sc)

 set newseg = newrequest.setSegmentByIndex(zm1,newsegpos) SET pRequest = newrequest
$$$QuitOnError(newseg) 

Not sure I understand, does ObjectScript cause programmers to reinvent wheels?

I agree, there are many wheels that need not be built inside IRIS, but Atwoods law is not advocating for this. 

Consider this premise first...

"Any backend application that is to be written in IRIS could be written in many different languages".

and now Atwoods law...

"Any (backend IRIS) application that can be written in ObjectScript, will eventually be written in ObjectScript."

In my own words, the simplest solutions mostly come with the simplest of languages.

So for me, the future of ObjectScript is ObjectScript.

Here are some arguments for the future of ObjectScript.

Tim Berners-Lee on the Principle of Least Power:

Computer Science spent the last forty years making languages which were as powerful as possible. Nowadays we have to appreciate the reasons for picking not the most powerful solution but the least powerful. The less powerful the language, the more you can do with the data stored in that language. If you write it in a simple declarative from, anyone can write a program to analyze it. If, for example, a web page with weather data has RDF describing that data, a user can retrieve it as a table, perhaps average it, plot it, deduce things from it in combination with other information. At the other end of the scale is the weather information portrayed by the cunning Java applet. While this might allow a very cool user interface, it cannot be analyzed at all. The search engine finding the page will have no idea of what the data is or what it is about. The only way to find out what a Java applet means is to set it running in front of a person.

To steal a line from Jeff Atwood:

Atwoods Law: "Any application that can be written in ObjectScript, will eventually be written in ObjectScript."  

And Dudley Moore...

Volvos ObjectScript, boxy but good

Hi Evgeny,

The current GitHub version will only serialise and deserialise to and from class based objects.

I do however have several other solutions in the unofficial version which will efficiently serialise and deserialise to and from globals. I also have a pollyfill solution for DynamicObject and DynamicArray that uses a type mixer class that would allow older versions of Cache to work with these classes now.

However, I've not used these in production, only unit tested. I am happy to release them if there is a need / someone is willing to collaborate on production level testing and debugging.

Hi David,

In general +1 for the generic boiler plate approach.

In terms of a generic JSON solution is could do with some additional type checks to make sure they are output correctly, e.g. booleans as true / false / null, empty numbers being returned as null and not empty string etc.

So just for booleans as an untested example you might replace...

set $PROPERTY(tRow,tColumnName) = $PROPERTY(rSet,tColumnName)


with this..

do tRow.%Set(tColumnName,$PROPERTY(rSet,tColumnName),$Select(tColumn.clientType=16:"boolean",1:""))


The alternative, if just going from SQL to a serialised JSON string could be to just use SQL and JSON_ARRAYAGG as per the examples here...

https://community.intersystems.com/post/how-do-i-return-json-database-sql-call

Hi Michael,

1. Copy the entire first source OBX to the first target OBX
2. Loop over the source OBX group
3. In the loop, if the loop key is greater than 1, append the source OBX 5 value to the first target OBX 5 with a preceding delimiter

It should look something like this...

If the source is...

OBX|1||||Alpha|
OBX|2||||Bravo|
OBX|3||||Charlie|


Then your target will output as...

OBX|1||||Alpha Bravo Charlie|

You can access the rule name with aux.RuleReason in the transform which provides a way to pass in a delimited value.

Another option is to chain transformations together within the same send action. So you could have a second transformation for each target that does nothing but assign a hard coded value such as MGH and MGP to your facility codes.

To chain the transformations just select a second transformation from the data transform selector, or just add the name of the transformation in a comma delimited list, e.g.

SAH.Common.HL7.Transform.Message.Genericv231ToFacilityConversionDTL,SAH.Common.HL7.Transform.Message.AddMGH

SAH.Common.HL7.Transform.Message.Genericv231ToFacilityConversionDTL,SAH.Common.HL7.Transform.Message.AddMGP

There are only strings in ObjectScript.

We can peek at the $ZTH return with zzdump to prove this...

USER>zzdump $zth("00:00:00.1")
 
0000: 30 2E 31

USER>zzdump $zth("00:00:01.1")
 
0000: 31 2E 31


I think the ZWRITE command is confusing matters a little by displaying quotes around stringy values that are not canonical numbers. It implies that there is a concept of type when there is not.

> shouldn't $ZTH be consistent in its return? All numbers in canonical form, not just those greater than or equal to 1?

It's a valid point, if a function should only ever return a stringy value in canonical form then perhaps it should always quit +val.

In terms of heartburn with $ORDER etc.

Subsrcript values are inserted into a varaible / global in a way that they are automatically sorted, first in canonical form and second (as I understand) in byte order, so stringy non canonical numbers will still be sorted, but they will apear after canonical numbers and before alpha characters.

Without knowing this its a common trip hazzard to insert fractional numbers with a leading zero and see them appear out of sequence. Which is where prefixing the value with a plus will resolve the sort problem at source.

The same problem can also appear in reverse. You might have strings that contain numbers, but you want them evaluated (sorted) as strings and not as canonical numbers.

We can see both in action with the Persistent class. If you create an index on a %Float property then it will canonicalise the string to ensure that all fractional numbers appear in the correct index sequence. If the property is a %String, then it will prefix all values with a space which is why you often see leading spaces in indexes. This ensures that all numbers (canonical or not) appear in byte order along with the strings.

Bottom line, we only have strings, and stringy numbers are only considered numbers when they are in canonical form or converted to a canonical form during a numerical operation.

Hi David,

The ruleset including assign actions are evaluated before the send actions.

In this instance, if both rules are true then the first assign will be overwritten by the second assign, before both send actions are processed.

I guess you could work around this by having two different property names and testing for the existence of a value in the DTL.

It feels like there is a better solution altogether to suggest, but its not clear what the real world use case is here.

Sean.

Hi Reinhard,

Welcome to ObjectScript and the Community.

As you are still learning ObjectScript I hope you don't mind a small amount of extra guidance on what you are doing.

I can understand why a set of popular named string functions might be a good idea, but in practice there are enough differences across languages to make one solution feel familiar to one set of developers and not to another. For instance InStr() might be familiar to Visual Basic developers but Java and JavaScript developers would be expecting indexOf() and Python developers find().

In general abstracting ObjectScript string operators and built in string functions with Macro's is going to produce a non standard solution. Whilst you will be an expert in Reinhard String Functions, you won't be a fully grounded ObjectScript developer and the code you produce will be less readable and maintainable to those that are.

Learning native string operators and built in functions is an important step in becoming a competent ObjectScript developer and its really worth the investment to build these important foundations in your new language skillset. In practice there are very few operators and string functions to learn and the required investment will work out less than developing and unit testing your own library. That's not including the time to write bespoke documentation that other developers would then need.

I can also understand why auto complete goodness might also look like a useful asset in this process, but as soon as you are fluent in ObjectScript foundations, the code will just be streaming off your fingertips, and constant pop up suggestions would just be a distraction.

A few more things to consider. Overuse of Macro's can lead to a condition I call Macro Soup. The more Macro's that are used, the less readable code will become. If you start including very common functions such as strings then you will soon hit this problem. It's great to see you are already exploring these powerful features of ObjectScript, but IMHO its best to be non liberal in using them.

You might also be interested to know that InStr() is actually supported in Caché, both in its SQL dialect and with the Caché Basic transcompiler. If for instance you are a Visual Basic developer shop and will continue long term recruitment from this skills pool, then Caché Basic might make more sense to you. That said, you will never be fully rounded without also being able to read compiled ObjectScript as well.

One last recommendation I always give when teaching ObjectScript is to write your own cheat-sheet. The process itself tends to solidify knowledge more quickly and having the cheat-sheet on hand will help to jog your memory.

Have fun learning!
Sean.