The company I work for writes one (COBOL —> Java, and separately COBOL—> C++ — as well as other translators for MF tooling). It took us a …while… to reach high success rates on new customer source.

There are several issues at play:

1. The language is ridiculously verbose and inconsistent. It was designed in the era when people thought the only barrier to everyone learning programming was that it wasn’t “English” enough — so stdlib functions randomly introduce keywords for “readability”, and throw consistency out the window.

2. The mainframe tries to provide anything and everything you’ll ever need, rather than relying on user-supplied libraries, and does so by adding that functionality to the language itself (combined with above arbitrary inconsistencies). Wikipedia notes 300 keywords, and probably many more terminals in general (another reference: SQL standard apparently specifies some 1700 terminals)

3. The language is itself not well-defined, or well standardized (a standard exists, but like SQL, everyone extends it arbitrarily. And again, they extend the language, rather than supplying libraries utilizing the existing languages). There are many cobol dialects in the wild. A few major players (ibm, gcos, microfocus, and a lot of little & dead ones).

4. COBOL compiler implementors (notably IBM) have/had a habit of asking customers what they needed, and adding the feature to the compiler, intending it for the one customer. Eventually other people find it too. Undocumented unspecified statements used at random.

5. COBOL does not live on its own. It expects to run under the terms of the mainframe transaction processing and batch processing runtimes. These have to be replicated as well. Additionally database (eg DB2, Oracle), filesystem (eg VSAM), third party apps, etc. Additionally, different mainframe providers have different semantics, sometimes subtle, sometimes not.

6. COBOL does things differently than Java. I consider it a fork in the timeline of language design. Functions don’t necessarily have to return to the caller. Definition order in source code matters (eg PERFORM THRU). Weird artifacts from its punchcard history (notably 70-char limit, but also weird functions like REDEFINE). It loves fucking around with bit fields. Variables are basically global, always. Structured Control flow is really a shallow layer on top of GOTO.

7. Companies with mainframes have typically already tried & failed to get off the mainframe. By translation, or reimplementation, or emulation. They’re often cautious to try again (a problem for our sales team). Lots of companies offer to do it manually, and fail on any decent sized codebase, causing companies to be quite cautious

8. Mainframes are fast at what they do, and very reliable. It’s very much a don’t fix what ain’t broke situation. It’s also difficult to meet same performance expectations when you’re emulating the whole thing — though half the point in converting is that it’s much easier to simply throw more hardware at the problem. Conversion generally is “COBOL in Java” because safely rearchitecting it automatically to something more idiomatic is often quite difficult, or not even possible without violating unspoken assumptions; its also difficult for current maintainers to verify correctness, if you’ve mucked about with the logic flow significantly.