How to write a return case statement for a 2-element tuple?

Hi there! I’d like to use a return-case statement. The top-level function generateResetPeriods fetches the CalculationPeriod from the generateResetPeriodDates, which in turn returns a floatingRateDefinition.

Current return statement with more code:

generateResetPeriods : (Fetch f) => CalculationPeriodDates -> Optional ResetDates -> f [CalculationPeriod]
generateResetPeriods cpds None = generateCalculationPeriods cpds
generateResetPeriods cpds (Some (checkResetDates cpds -> rds)) = do
  -- Roll out calculation periods and unadjusted reset dates
  cps <- generateCalculationPeriods cpds
  let rpDatesV = map generateResetPeriodDates cps

  -- Set reset and fixing dates
  let adj = get "resetDatesAdjustments" rds.resetDatesAdjustments
  let fixingOffset = get "fixingDates" rds.fixingDates
  let resetRelativeTo = fromOptional ResetRelativeToEnumCalculationPeriodStartDate rds.resetRelativeTo
  mapA (setResetDates resetRelativeTo fixingOffset adj) $ zip cps rpDatesV -- takes cps and reset periods and returns one list

  where
    -- multiple resets per period not supported yet
    generateResetPeriodDates : CalculationPeriod -> [(Date, Date)]
    generateResetPeriodDates cp = [(get "adjustedStartDate" cp.adjustedStartDate, get "adjustedEndDate" cp.adjustedEndDate)]

    setResetDates :
      (Fetch f)
      => ResetRelativeToEnum
      -> RelativeDateOffset --
      -> BusinessDayAdjustments --
      -> (CalculationPeriod, [(Date, Date)]) --
      -> f CalculationPeriod
    setResetDates resetRelativeTo fixingOffset adj (cp, rpDates) = do
      resetDates <-
        case resetRelativeTo of
          ResetRelativeToEnumCalculationPeriodStartDate -> mapA (adjustDate adj . fst) rpDates
          ResetRelativeToEnumCalculationPeriodEndDate   -> mapA (adjustDate adj . snd) rpDates

      ros <- mapA generateRateObservation resetDates
      return $ cp { floatingRateDefinition = Some FloatingRateDefinition
                                                    { calculatedRate = None
                                                    , capRate = []
                                                    , floatingRateMultiplier = None
                                                    , floorRate = []
                                                    , rateObservation = ros
                                                    , spread = None
                                                    }}

      ...

Is this the right case statement for a 2-element tuple?:

generateResetPeriods : (Fetch f) => CalculationPeriodDates -> Optional ResetDates -> f [CalculationPeriod]
generateResetPeriods cpds None = generateCalculationPeriods cpds
generateResetPeriods cpds (Some (checkResetDates cpds -> rds)) = do
  
  ...

  where
    
    generateResetPeriodDates : CalculationPeriod -> [(Date, Date)]
    generateResetPeriodDates cp = [(get "adjustedStartDate" cp.adjustedStartDate, get "adjustedEndDate" cp.adjustedEndDate)]

    ...
     
      return $ case (cp.floatingRateDefinition, cp.otherRateDefinition) of
               (Some floating, None) ->
                    cp { floatingRateDefinition = Some FloatingRateDefinition
                                                    { calculatedRate = None
                                                    , capRate = []
                                                    , floatingRateMultiplier = None
                                                    , floorRate = []
                                                    , rateObservation = ros
                                                    , spread = None
                                                    }}
               (None, Some other) ->
                    cp { otherRateDefinition = Some OtherRateDefinition
                                                    { calculatedRate = None
                                                    , capRate = []
                                                    , floatingRateMultiplier = None
                                                    , floorRate = []
                                                    , rateObservation = ros
                                                    , spread = None
                                                    }}
1 Like

Hi @Chris_Rivers, the case statement looks fine apart from the fact that you are not handling the case where both are None and both are Some. Are you seeing any issues?

Two other comments on your code snippet:

  1. It looks like you already define the result of generateResetPeriodDates in the first line of its definition so I’m not sure what the return is supposed to belong to.
  2. Note that return in DAML is not the same as the return statement in languages like Java. It is a function of type Applicative f => a -> f a. What it does depends on the Applicative instance. For lists, i.e. f = [], it wraps the argument in a singleton list. For Update it allows you to lift a pure expression into an Update action that when executed will return the result of evaluating that expression.
1 Like

Hi @cocreature, although I expect either a Some-None or None-Some case, the compiler complained (gave warning) about not having the cases you mentioned and I got this error too:

Scenario execution failed on commit at Test.Event:116:23:
  Aborted:  /Applications/DEV/DAML/ex-cdm-swaps copy/daml/Org/Isda/Cdm/EventSpecificationModule/Impl/Contract/Payout/InterestRatePayout/Schedule/ResetDates.daml:(63,16)-(79,54): Non-exhaustive patterns in case


Stack trace:
- patError (Control.Exception.Base:36:1)
- generateResetPeriods (Org.Issa.Srm.EventSpecificationModule.Impl.Contract.Payout.InterestRatePayout.Schedule.ResetDates:63:16)
- generateResetPeriods (Org.Issa.Srm.EventSpecificationModule.Impl.Contract.Payout.InterestRatePayout.Schedule.ResetDates:63:7)
...

However, in the case of Some-Some, how to include both floatingRateDefinition and otherRateDefinition within the scope of cp?

For your first comment, I included additional code above for generateResetPeriodDates for background. The code works fine in test cases with a single return statement, but not so when using the return case.

1 Like

Something like this

case (cp.floatingRangeDefinition, cp.otherRateDefinition) of
  (Some floating, Some other) -> …

Then in both floating and other are in scope.

As for your error, I’m sadly still unable to see what is going wrong even with the additional context.
Do you get a compile-time error or a runtime error? Can you share the error message? It would also be super helpful if you have a standalone example to reproduce the error.

1 Like

For the Some-Some, I haven’t figured out the write syntax for include both records in cp:

         (Some floating, Some other) ->
            cp { floatingRateDefinition = Some FloatingRateDefinition
                                                { calculatedRate = None
                                                , capRate = []
                                                , floatingRateMultiplier = None
                                                , floorRate = []
                                                , rateObservation = ros
                                                , spread = None
                                            } 
                 otherRateDefinition = Some OtherRateDefinition
                                                { calculatedRate = None
                                                , capRate = []
                                                , floatingRateMultiplier = None
                                                , floorRate = []
                                                , rateObservation = ros
                                                , spread = None
                                            }}
1 Like

You either need to use with-syntax or a comma:
1.

cp with
  floatingRateDefinition = Some …
  otherRateDefinition = Some …
cp { floatingRateDefinition = Some …,
     otherRateDefinition = Some …
    }

Note that if you want to do the same thing for one of them independent of whether the other is Some you are probably better off doing two independent pattern matches.

1 Like

My runtime error:

Scenario execution failed on commit at Test.Event:116:23:
  Aborted:  /Applications/DEV/DAML/ex-cdm-swaps copy/daml/Org/Issa/Srm/EventSpecificationModule/Impl/Contract/Payout/InterestRatePayout/Schedule/ResetDates.daml:(63,16)-(79,54): Non-exhaustive patterns in case


Stack trace:
- patError (Control.Exception.Base:36:1)
- generateResetPeriods (Org.Isda.Cdm.EventSpecificationModule.Impl.Contract.Payout.InterestRatePayout.Schedule.ResetDates:63:16)
- generateResetPeriods (Org.Isda.Cdm.EventSpecificationModule.Impl.Contract.Payout.InterestRatePayout.Schedule.ResetDates:63:7)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:49:34)
- $$c$u003e$u003e$u003d (Org.Isda.cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:47)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:24)
- $$c$u003c$u002a$u003e (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:42:47)
- $$c$u003c$u002a$u003e (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:42:23)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:46)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:46)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:47)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:24)
- $$c$u003c$u002a$u003e (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:42:47)
- $$c$u003c$u002a$u003e (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:42:23)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:47)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:24)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:46)
- $$c$u003e$u003e$u003d (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:48:23)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:47)
- $$cfmap (Org.Isda.Cdm.EventSpecificationModule.Types.ReferenceData.Fetch:37:24)
- buildDerivedEvents (Org.Isda.Cdm.EventSpecificationModule.EventBuilder.Derived:37:41)
- buildDerivedEvents (Org.Isda.Cdm.EventSpecificationModule.EventBuilder.Derived:37:1)
- flip (DA.Internal.Prelude:369:1)
- $$cfmap (DA.Internal.LF:129:33)
- $$cfmap (DA.Internal.LF:129:22)

Ledger time: 2018-09-24T00:00:00Z

Partial transaction:
  Failed exercise (DA.Internal.Prelude:365:26):
    exercise Trigger on #13:0 (Main.Workflow.DeriveEvents:DeriveEventsWorkflow)
    with
      ciCid = #5:2;
      fromDate = some 2019-03-01T;
      toDate = some 2019-03-26T;
      refData =
        (Main.ReferenceData:RefData with
           holidayCalendarCids = [#10:0]; observationCids = [#11:0]; novelCids = [#12:0])
  Sub-transactions:
     #1
     └─> fetch #5:2 (Main.Market.Contract:ContractInstance)
     
     #2
     └─> fetch #10:0 (Main.ReferenceData:HolidayCalendarInstance)
     
     #3
     └─> fetch #11:0 (Main.ReferenceData:ObservationInstance)
     
     #4
     └─> fetch #12:0 (Main.ReferenceData:NovelInstance)

patError sounds like you forgot a case but hit it in your scenario. I would recommend to add even the cases that you think you might not hit and call something like error "unexpected case 1", error "unexpected case 2".

1 Like

Ok thanks. I’ll reply back either with a solution or standalone example.

1 Like