Extracting choice return type defined in DAML templates as a Java type

Hi all,

Issue:
I am trying to specifically retrieve the return type of a choice defined in DAML files in the form of a corresponding java type.

Steps done:
Looking at
codegen java and the DAML to LF conversion process in the docs
data conversion : How Daml Types are Translated to Daml-LF — Daml SDK 2.2.0 documentation
template to api commands conversion : How Daml Types are Translated to Protobuf — Daml SDK 2.2.0 documentation

It seems that the conversion of choices results in the creation of a transaction in Java/Protobuf, therefore not including the return type of a choice in protobuf/java equivalent.

Alternative currently working on:
Something I was thinking of doing was to parse the .daml files, extract the corresponding return type via pattern matching from the strings and pattern match it to determine what it’s corresponding data type is in Java (Given that all the templates and used data types are defined during java codegen)
However this would require manually defining all the available string patterns for DAML types and what their corresponding java type is.

Specific Questions:
I was wondering if either

  1. There is a current solution to extract the choice return types and return their corresponding Java data type.
  2. There is a daml file parser which can map the DAML choice return type into an easily accessible format (such as JSON) and if there is a daml to java mapping library that allows us to use that extracted return type string and identify the corresponding java data type (if not in the specific java data type, at least the wrapper method with a generic data type, such as a tuple, list or the corresponding wrapper class for Either a/Optional a etc.)

Thank you!

Hi @Julius_William,

I must admit I do not understand your question. The Java codegen should parse your Daml files and generate Java types that correspond to the Daml types, to a large extent obviating the need for you to look at LF and protobuf.

Could you perhaps point to a small, open-source repro repository on GitHub, with a sequence of commands you run, and a description of what goes wrong, i.e. what you’d expect the result to be vs. what you actually observe?

Hi Gary,
Thanks for checking in.

The java codegen does indeed parse the DAML files and generate the corresponding Java types necessary for the DAML model.

However, I can’t seem to find a declaration of the return type of a choice in the generated java file.

For example, when generating the java code with a template containing the below DAML choice snippet

    choice AcceptProposal: ContractId RentAgreement
      with 
        reasonOfAgreement: Text
      controller rentor 
      do 
        create RentAgreement with ..

I receive the below java class for the above DAML choice. (To my understanding, individual java classes are created for each DAML choice during codegen)

public class AcceptProposal {
  public static final String _packageId = "8fffc8f6453e92be28b37cbce6783bc9a914a7ede64302e78668df95fd098ee8";

  public final String reasonOfAgreement;

  public AcceptProposal(String reasonOfAgreement) {
    this.reasonOfAgreement = reasonOfAgreement;
  }

  public static AcceptProposal fromValue(Value value$) throws IllegalArgumentException {
    Value recordValue$ = value$;
    DamlRecord record$ = recordValue$.asRecord().orElseThrow(() -> new IllegalArgumentException("Contracts must be constructed from Records"));
    List<DamlRecord.Field> fields$ = record$.getFields();
    int numberOfFields = fields$.size();
    if (numberOfFields != 1) {
      throw new IllegalArgumentException("Expected 1 arguments, got " + numberOfFields);
    }
    String reasonOfAgreement = fields$.get(0).getValue().asText().orElseThrow(() -> new IllegalArgumentException("Expected reasonOfAgreement to be of type com.daml.ledger.javaapi.data.Text")).getValue();
    return new contractkey.AcceptProposal(reasonOfAgreement);
  }

  public DamlRecord toValue() {
    ArrayList<DamlRecord.Field> fields = new ArrayList<DamlRecord.Field>(1);
    fields.add(new DamlRecord.Field("reasonOfAgreement", new Text(this.reasonOfAgreement)));
    return new DamlRecord(fields);
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (object == null) {
      return false;
    }
    if (!(object instanceof AcceptProposal)) {
      return false;
    }
    AcceptProposal other = (AcceptProposal) object;
    return this.reasonOfAgreement.equals(other.reasonOfAgreement);
  }

  @Override
  public int hashCode() {
    return Objects.hash(this.reasonOfAgreement);
  }

  @Override
  public String toString() {
    return String.format("contractkey.AcceptProposal(%s)", this.reasonOfAgreement);
  }
}

From the above, the return type ContractId RentAgreement doesn’t seem to be defined anywhere in the generated java class for the choice.
Though if you are able to point out where the return type is defined in the generated java code, that would be perfect. (Hoping it’s gross oversight on my part here :joy:)

Hope this makes it clear.
I can still upload a sample project if the above code snippets are still too vague.
Appreciate the help!

Julius

Hi @Julius_William,

That’s a lot clearer, thanks.

I’m not actually very familiar with the Java codegen, so what I’m going to say here may need a pinch of salt. But I’ll try anyway.

The codegen is mostly there to translate types. It may look like choices are an exception to that at first glance, but they’re actually not: choices in daml are data types. In the same way that a template definition implicitly defines a record type of the same name, a choice definition also implicitly defines a record type of the same name.

So on the Daml side, you have a (implicit) record type equivalent to

data AcceptProposal = AcceptProposal
  with reasonOfAgreement: Text

and that is what you’re seeing translated in your Java code here. There’s nothing special for it “being a choice” here. It’s just a one-field record translated from Daml to Java.

Similarly, the “return type” of the choice is a type that exists in your Daml program (otherwise it wouldn’t compile), and so that type is also going to be translated. Specifically, in this case, the RentAgreement template will define an implicit record type with the same fields as the template, and that (the record type) is what you’ll find in your generated Java code. It, too, will be a “plain” record as far as the Java code is concerned, with little knowledge of the fact that it’s actually a template (except probably an interface added on so it works with whatever the ContractID equivalent is in the Java bindings).

Note that this is also true on the Daml side: when you write

    createCmd Asset with
      issuer = alice
      owner = alice
      name = "TV"

this is parsed as

let assetRecord = Asset with issuer = alice, owner = alice, name = "TV"
createCm assetRecord

i.e. first create a record of the implicit type corresponding to the template, then pass that (plain data) record to the createCmd function. Same is true for exerciseCmd: it takes two arguments, a party and a data record that happens to be implicitly defined by a choice.

From the Java side, you’ll have to work in the same way: first create plain records that represent what you want to do, then send that over the wire as Create or Exercise commands.

I’m in no position to give you a more complete / detailed answer than this, given my level of familiarity with the Java bindings, but hopefully that helps a little bit.

1 Like

No problem Gary, thank you Gary for the detailed answer.

So it seems we can confirm that the codegen indeed does not recognize and attach a special value Return Type to the choice classes created as they are just recognized as data types.

Question:
In that case going back to my initial post, do you know of a currently existing solution to elegantly capture that Return type variable from DAML template choices?

In the absence of which, do you know of any tool mentioned below to make it slightly easier to extract the choice return types from the DAML files?

  • A daml file parser which can map the DAML choice return type into an easily accessible format (such as JSON)
  • A daml to java mapping library that allows us to analyze the extracted return type and identify the corresponding java data type (if not in the specific java data type, at least the wrapper method with a generic data type, such as a tuple, list or the corresponding wrapper class for Either a/Optional a etc.)

Without the help of the above 2 tools, we’d have to construct a DAML file parser and DAML to Java type dictionary from scratch.
I guessed that there has to at least be a parser or dictionary that the java codegen tool is based on, so we’re hoping to leverage on that to prevent reinvention of a wheel hopefully already constructed by our superb product team. :pray:

Thanks!
Julius

Noting that it’s currently not prioritized, are you looking for something along these lines?

To clarify, no tooling associated with the codegen performs any kind of parsing of Daml. The tooling consumes the Daml-LF compiler output in the form of a DAR. To manage the evolution of Daml-LF over time we use a tool internally which we usually refer to as the interface library (here), which however will be renamed to signature library to avoid clashing names with the interface feature in Daml which is about to land. Note that this library is intended for internal consumption and is not part of our product offering, so we may make breaking changes liberally over time. If you can’t or don’t want to use that library, my recommendation is to use the Daml-LF compiler output. Your use case in particular is simple enough that you can probably do it without the signature library.

May I ask what precisely you’re ultimately trying to achieve on top of the tool that you want to build to map choices to return types?

Hi Stephano, thanks for sharing.

Yes, I would like to achieve point 1 ii) specifically in the link, to obtain a reference to the corresponding Java class or data type for the return type of the DAML choice.
We seem to have all the other details in the templates and choices (such as fields in the template, input arguments and their corresponding java types referenced properly using codegen)

Noted on the point regarding Signature library.

In that case, is the feature requested in the link currently the only unreleased method to easily access the return type of the choice and no other solutions exist now? (Apart from using the sig library in-depth which might be prone to mapping changes)

We are trying to map all the significant details in a DAML template (including return types of choices) into Java code which will be further processed in another workflow (Details have not been confirmed yet.)

Thanks for the response,
Julius

Are you trying to make a generic codegen on top of our codegen, or are you programming against a specific, known Daml model?

Yep it has to be generic to account for changing DAML projects, not just a specific one.

If you want to write dynamic, generic code you’ll probably need to parse Daml-LF. See a similar discussion for some of the reasons we don’t recommend going down that path.

Thank you for the link and duly noted