How to best model an object-oriented styled polymorphic method in Daml?

Let’s say I am building a fruit stall selling three types of fruits (apple, orange, and banana) and each kind of fruit has a Validate choice in place to validate an incoming UnknownFruit (which is specified by the user as one of the three types of fruits, but not necessarily the true fruit type):

newtype Colour = Colour Text deriving (Eq, Show)

data FruitType = Apple | Orange | Banana

data UnknownFruit = UnknownFruit
  with
      colour : Colour
      bestGuessFruitType : FruitType -- This is just a best guess, not necessarily the actual fruit type

template Apple
  with
    colour : Colour
  where
    signatory ..
    nonconsuming choice Validate : Validation Text Colour
      with
        unknownFruit : UnknownFruit
      do
         -- Check if the colour is red

template Orange
  with
    colour : Colour
  where
    signatory ..
    nonconsuming choice Validate : Validation Text Colour
      with
        unknownFruit : UnknownFruit
      do
        -- Check if the colour is orange

template Banana
  with
    colour : Colour
  where
    signatory ..
    nonconsuming choice Validate : Validation Text Colour
      with
        unknownFruit : UnknownFruit
      do
        -- Check if the colour is yellow

My question is, given an UnknownFruit, assuming I want to use bestGuessFruitType to locate the template type I want to call, how can I use exerciseByKey to dynamically determine at runtime which Validate choice to exercise?

You have a name clash in your code: Apple is both a constructor for FruitType and of the record/template type Apple. So it has two types:

Apple : FruitType
Apple : Colour -> Apple

I’d advise you to give enums like FruitType prefixed constructor names:

data FruitType = FTApple | FTOrange | FTBanana

If you had wanted to actually attach the fruit, it would be

data Fruit = FApple Apple | FOrange Orange | FBanana Banana

Either way, there is really nothing “unknown” about UnknownFruit. You just dispatch based on constructor:

validateFruit : UnknownFruit -> Update (Validation Text Colour)
validateFruit unknown_fruit = case unknown_fruit.bestGuessFruitType of
  FTApple -> exerciseByKey @Apple ...
  FTOrange -> exerciseByKey @Orange ...
  FTBanana -> exerciseByKey @Banana ...

Thank you so much @bernhard! Good catch about the name clash I was typing it on the text field without IDE support so apologies about that.

Yeah your answer absolutely makes sense I don’t know why I didn’t think of that before. I do have one more follow up question though:

Let’s say this fruit stall solution is fronted by a REST API and all the Daml ledger interaction happen using the RxJava binding DamlLedgerClient class. How can we ‘call’ this validateFruit function on the Java application?

The Ledger API doesn’t allow you to call arbitrary functions, you can only exercise choices. If you’re in control of the Daml code, that’s not as much of a constraint as it may at first appear: you can create a stateless contract that just exposes that one choice. Something like:

template FruitValidator
  with s : Party
  where
    signatory s
    choice ValidateFruit : Validation Text Color
      with
        unknown_fruit : UnknownFruit
      controller s
      do validateFruit unknown_fruit

which you would call with “create and exercise”.

Or, if you call it often enough that the churn on that contract would matter, you can, say, create one per user and keep it around for further requests. (I.e. by making the choice nonconsuming.)

Thank you @Gary_Verhaegen this is extremely insightful and helpful!