Disambiguate accessing to contract fields

I have a couple of templates that share a field name (in this case name)

template DivulgeableAsset
  with
    owner: Party
    name: Text
  where
    signatory owner

    -- choices...

template PrivateAsset
  with
    owner: Party
    name: Text
  where
    signatory owner

    -- choices...

Later, in DAML Script, I would like to retrieve contracts that instantiate these templates and extract their respective names. I do something like the following:

run : Script ()
run = do
  owner <- fromSome $ partyFromText "owner"
  divulgee <- fromSome $ partyFromText "divulgee"

  -- expect the owner to see both the divulgeable...
  divulgeableAssets <- query @DivulgeableAsset owner
  assertEq ["first"] (map ((name : DivulgeableAsset -> Text) . snd) divulgeableAssets)

  -- ... and the private asset
  privateAssets <- query @PrivateAsset owner
  assertEq ["second"] (map ((name : PrivateAsset -> Text) . snd) privateAssets)

Note that I explicitly hinted the compiler the type of the name function. If I donā€™t, I get the following error:

Ambiguous occurrence ā€˜nameā€™
It could refer to
   either the field ā€˜nameā€™,
          imported from ā€˜Mainā€™ at /some/path/daml/Test.daml:13:1-11
          (and originally defined
             at /some/path/daml/Main.daml:47:5-8)
       or the field ā€˜nameā€™,
          imported from ā€˜Mainā€™ at /some/path/daml/Test.daml:13:1-11
          (and originally defined
             at /some/path/daml/Main.daml:34:5-8)

Is there a cleaner way to disambiguate between those two functions (or, in general, to extract those fields)?

The full project is available here.

2 Likes

You can use record-dot-syntax which will resolve the ambiguity automatically. So for your example, that would be something like the following

  assertEq ["first"] (map (\(_, asset) -> asset.name) divulgeableAssetsSeenByOwner)
2 Likes

Is this pattern that dlint could potentially catch? @cocreature @shaynefletcher

3 Likes

In general no, HLint does not have enough information to judge whether something is a record field or not. You could probably make something that works as long as the record and the usage of the field are in the same module but I expect the implementation effort to be fairly high

4 Likes

I concur. Itā€™s easy enough to locate constructions like name : DivulgeableAsset -> Text however the syntax alone does not carry enough ā€œcontextā€ for DLint to classify it as a field selector (as @cocreature says, which would then induce a lot of ā€œspecial caseā€ implementation effort) so if it were to issue hints, they would be highly speculative and often likely false positives which would be annoying and consequently quickly get disabled. Actually, the sample @stefanobaghino-da provides is somewhat interesting in that he reached for selectors in the first instance over dot-syntax which Iā€™d think might be considered as slightly unusual when programming DAML and probably a consequence of prior Haskell experience. In fact if DAML already had the long desired and highly anticipated NoFieldSelectors feature it wouldnā€™t have been an option :smiley:

3 Likes

Iā€™d have to write a program to test this but I expect sections to work here too e.g. map ((.name) . snd) divulgeableAssetsSeenByOwner and wonder if inference succeeds if phrased that way? I guess here is another way to dodge the ambiguity : map (\(_, DivulgeableAsset {name}) -> name) divulgeableAssetsSeenByOwner

2 Likes

Using a section, i.e., (.name) . snd does work but at least to me that is significantly harder to read and understand than \(_, asset) -> asset.name so I would not recommend that option here. Of course there might be other examples where the section is useful.

2 Likes