0

In homework of week4, I refactored my solution:

payTrace :: Integer -> Integer -> EmulatorTrace ()
payTrace payment1 payment2 =  do
contract1 <- activateContractWallet (knownWallet 1) payContract
let pkh2 = mockWalletPaymentPubKeyHash $ knownWallet 2
callEndpoint @"pay" contract1 $ PayParams
   {
       ppRecipient = pkh2
   ,   ppLovelace = payment1
   }
void $ Emulator.waitNSlots 1
callEndpoint @"pay" contract1 $ PayParams
   {
       ppRecipient = pkh2
   ,   ppLovelace = payment2
   }
void $ Emulator.waitNSlots 1

to:

payTrace :: Integer -> Integer -> EmulatorTrace ()
payTrace payment1 payment2 =  do
contract1 <- activateContractWallet (knownWallet 1) payContract
let pkh2 = mockWalletPaymentPubKeyHash $ knownWallet 2
    payTo :: (Member RunContract effs) => PaymentPubKeyHash -> Integer -> Eff effs ()
    payTo pkh amount = callEndpoint @"pay" contract1 $ PayParams
       {
           ppRecipient = pkh
       ,   ppLovelace = amount
       }
payTo pkh2 payment1
void $ Emulator.waitNSlots 1
payTo pkh2 payment2
void $ Emulator.waitNSlots 1

(https://gitlab.com/moment-4/courses/plutus-pioneer/plutus-pioneer-program/-/blob/week04/code/week04/src/Week04/Homework.hs)

However, there are still 2 things I don't like about my current solution:

  1. I'd prefer to put the payTo function in the where clause, but that doesn't seem to work properly (although I think I'm close thanks to Luis (https://discord.com/channels/826816523368005654/834340082273878046/942064349831381042). How would one define it in the where clause? (The solution of Luis put me on the right track, but I still can't get it right)

  2. I still get compiler warnings about the type. How to define the type of the function properly, so I can get rid of the warnings as well?

src/Week04/Homework.hs:46:18: warning: [-Wsimplifiable-class-constraints]
    • The constraint ‘Member RunContract effs’ matches
        instance (Data.OpenUnion.Internal.FindElem t r,
                  Data.OpenUnion.Internal.IfNotFound t r r) =>
                 Member t r
          -- Defined in ‘Data.OpenUnion.Internal’
      This makes type inference for inner bindings fragile;
        either use MonoLocalBinds, or simplify it using the instance
    • In the type signature:
        payTo :: (Member RunContract effs) =>
                 PaymentPubKeyHash -> Integer -> Eff effs ()
      In the expression:
        do contract1 <- activateContractWallet (knownWallet 1) payContract
           let pkh2 = mockWalletPaymentPubKeyHash $ knownWallet 2
               payTo ::
                 (Member RunContract effs) =>
                 PaymentPubKeyHash -> Integer -> Eff effs ()
               ....
           payTo pkh2 payment1
           void $ Emulator.waitNSlots 1
           ....
      In an equation for ‘payTrace’:
          payTrace payment1 payment2
            = do contract1 <- activateContractWallet
                                (knownWallet 1) payContract
                 let pkh2 = ...
                     ....
                 payTo pkh2 payment1
                 ....
   |
46 |         payTo :: (Member RunContract effs) => PaymentPubKeyHash -> Integer -> Eff effs ()
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ok, 8 modules loaded.

Thanks!

1 Answer 1

1

I don't know if this is what you expect but there are three ways to get rid of warnings:

  1. add {-# LANGUAGE MonoLocalBinds #-}, like the warning hints
  2. don't give a type for payTo and let Haskell infer it
  3. if things worked before, the body of payTo was fine in the EmulatorTrace monad, so you could go for payTo :: PaymentPubKeyHash -> Integer -> EmulatorTrace ()

I'm not particularly fond of the idea of moving the function in a where clause if it depends on something you obtain in the body of the bigger function. You could consider adding another parameter and passing contract1 as an argument, and then you can move it to where.

3
  • Allright, thanks. Well, I just wanted to get rid of the warning and understand why it's there. So, seeing your solutions: 1. what does it mean (MonoLocalBinds)? 2. I try to understand the types passed everywhere. Is there no way I can give the right type signature? 3. The body was fine, but moving it to the where, makes in different (it's a different type in the do-bindings vs in the were clause. That being said, it makes more sense to keep in the let-binding? (Just trying to figure out the Haskell type system and monads and do-bindings)
    – Kurt Sys
    Commented Feb 13, 2022 at 14:21
  • 1. a Haskell extension meant to infer less polymorphic types. Details here: ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/…. 2. I'd say using EmulatorTrace () for the result is the right type signature. The type you use is more general: (Member RunContract effs) => ... -> Eff effs () can be specialized to Eff '[StartContract, RunContract, Assert, Waiting, EmulatorControl, EmulatedWalletAPI, LogMsg String, Error EmulatorRuntimeError] (), which is EmulatorTrace (), because RunContract is in the list of effects for EmulatorTrace thus the condition holds. Commented Feb 13, 2022 at 14:53
  • And 3, you use contract1 in the body of payTo and if you move payTo to the where clause, contract1 is no longer in scope, as it is local to the body of payTrace. Either leave payTo in the let-binding or make payTo :: ContractHandle w s e -> PaymentPubKeyHash -> Integer -> EmulatorTrace (), move it to where and add contract1 to the calls of payTo in the body of payTrace. Commented Feb 13, 2022 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.