if the package A uses package's C version 0.0.1, and package B uses package's C version 0.1.0 (which behaves differently and is not back-and-forth compatible!)

In the Maven/Java world, there is a mechanism to exclude the dependency of Package A on Package C from Package A's dependency list. This results in both Package A and B to use the same version of Package C. If Package A really cannot use the new version of Package C, then you have no choice but to make a patched version of Package A, but at least you are alerted to the potential conflict.

Java has had annotations since version 5, which wikipedia says is 2004, and they are also heavily used in C#, where they are called attributes. They are heavily used in many places, notably in Hibernate, Spring and JUnit/TestNG. Introducing custom attributes/annotations is a very powerful way of extending a language.

It seems to me that Cache method keywords are the most similar thing to Java method annotations. Expanding them to match annotations would require a mechanism to introduce custom method keywords, to be able to fetch keywords for a method at runtime, and also to have custom keywords on properties, and for keywords to have arbitrary objects as values instead of being limited to strings. But I don't think it would be easy to do.

You have probably been on great projects, but in my experience the Bard's words ring true: "There are more things in heaven and Earth, Horatio, / Than are dreamt of in your philosophy". May you find someone better to declarative functional programming. I agree that it is hard to explain these things, I have had much better success gaining converts by simply using the techniques on a real project - and so perhaps I should let this thread go.

Optimizing code can make it more difficult to understand and change later. Sometimes performance requirements dictate that you need to write the code as optimized as possible. Another approach is to make the code easy to understand so that it is easier to safely change later. That's what refactoring means, and that's where I found functional-style programming to have some benefit. The idea behind optimizing for changeability is that you can profile the code later if necessary and optimize for performance where it matters.

I haven't done much with parallel computation, just a couple exploratory projects with Apache Spark which makes use of immutable distributed data sets. The fact that the data sets themselves don't change mean that they can be copied around without worrying about other nodes changing them. It also means a given node can throw away any data that is not used as input to a subsequent function. When writing code for Apache Spark, you construct a data set that is never actually instantiated - instead you define a series of transformations on it which are distributed to the nodes which have the data and execute the transformations, throwing away the intermediate results and sending only the results of aggregation back the master node. It's a lot easier to use than it sounds. It's worth looking into if you want to do map-reduce style things. Apache Spark is not particularly efficient, but it lets you throw lots of cheap machines at the problem and get answers quickly, which makes it popular for ad-hoc analysis of very large datasets.

Hi Mike,

Based on the definitions I have seen, declarative programming is pretty broad and encompasses everything from SQL to more "functional programming" constructs such as what Maks is proposing. Most people are familiar with the pros and cons of using SQL vs implementing queries in code. I have worked on large projects organized around functional programming principles, and in my experience the benefits are:

  • Easily tested code. Because all functions take explicit arguments and return values without side effects, they are easily tested.
  • Safer refactoring. Typically variables and data structures are immutable, so most ways of reordering the code will either throw undefined errors or continue to work as intended. It will not introduce subtle bugs.
  • Easier refactoring. Because statements are mostly used to define variables, you can take an arbitrary set of statements and make them into a method, or conversely take a method and inline it. This makes it easy to extract functionality that you want to reuse in other places, or change the balance of what is done in a method call vs in the current method.

The above combine to make the code easily changeable with lower risk. The ability to reorganize the code without introducing bugs is the main benefit, and that then enables you to quickly deliver on your commitments. There are other potential benefits, such as it being easier to parallelize computation since there are no mutable data structures to synchronize, but I haven't needed or tried to realize such benefits in practice. I have heard functional programming in Cache could incur a significant performance penalty due to how function calls are implemented, but I would test to see what the impact is since it the benefits may outweigh the performance penalty. This may all seem very "pie in the sky", but in practice it is a useful tool to have that is no more fanciful than SQL.

Have you tried the %Stream.FileCharacter class? For example, the following should create a  file that contains "Hello Lambda!" but with the unicode character for lambda:

ClassMethod WriteUTF8()
    Set stream=##class(%Stream.FileCharacter).%New()
    Set sc=stream.LinkToFile("c:\tmp\lambda.txt")
    Do stream.TranslateTableSet("UTF8")
    Do stream.Write("Hello " _ $CHAR(955) _ "!")
    Do stream.%Save()

Though I am not sure how you can create unicode characters in an 8 bit cache installation.

I am having trouble understanding this example in terms of the documentation for open:


What does the "QR" mean here? According to the docbook, the second argument to open should be in parenthesis, why that not necessary here?


Both the "QR" and "|PIPES|" string are documented here: