Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

Add MultiplicativeSemigroup Value instance #85

Open
Renegatto opened this issue Nov 17, 2021 · 2 comments
Open

Add MultiplicativeSemigroup Value instance #85

Renegatto opened this issue Nov 17, 2021 · 2 comments
Assignees
Labels
enhancement New feature or request

Comments

@Renegatto
Copy link
Contributor

Renegatto commented Nov 17, 2021

Motivation

It is lawful* (that is already a big reason!), and with a AdditiveMonoid Value forms near-semiring that satisfies* all required laws.
MultiplicativeMonoid Value is also technically possible, but seems useless because neutral element would contain all possible currency symbols and all possible tokens of each one. So no semiring, only near-semiring.

Can be useful at least in filtering values and scaling it.

Usage

Currently I came up only with one case, but since it's lawful, I guess there are more of them.

-- when multiplied on, currencies and tokens that doesn't mentioned here will be filtered away
interestingValues = fold
  [ singleton usd "BankPayment" 1
  , singleton usd "PersonalPayment" 1
  , singleton eur "BankPayment" 1 ]

----
val1 = fold
  [ singleton usd "PersonalPayment" 356
  , singleton usd "BankPayment" 488
  , singleton eur "PersonalPayment" (-78) ]
  
-- eur "PersonalPayment" (-78) were filtered away since it's not present in 'interestingValues'
interestingValues * val1 == fold
  [ singleton usd "PersonalPayment" 356
  , singleton usd "BankPayment" 488 ] --should be True
----
val2 = singleton usd "BankPayment" 1576

-- the only currency-token pair mentioned in both 'interestingValues' and 'val2'
interestingValues * val2 == singleton usd "BankPayment" 1576  --should be True
----
-- the only currency-token pair mentioned in both Values
-- amounts multiplied for it
val1 * val2 == singleton usd "BankPayment" (488 * 1576) --should be True

Implementation

Is pretty straightforward:

instance MultiplicativeSemigroup Value where
  {-# INLINEABLE (*) #-}
  Value val1 * Value val2 =
    Value $ mapZipWith (mapZipWith (*)) val1 val2
    
{-# INLINEABLE mapZipWith #-}
mapZipWith :: 
    forall (k :: Type) (a :: Type) (b :: Type) (c :: Type). 
    PlutusTx.Eq k =>
    (a -> b -> c) -> 
    Map k a -> 
    Map k b -> 
    Map k c
mapZipWith f map0 map1 =
  AssocMap.mapMaybeWithKey (\k v -> f v <$> AssocMap.lookup k map1) map0

* - for now, only on normalized Values only, or by comparison Values using Eq AssocMap. Same thing about already existing Value instances that requires some laws. More about this bug in plutus api.

@kozross kozross self-assigned this Nov 17, 2021
@kozross kozross added the enhancement New feature or request label Nov 17, 2021
@kozross
Copy link
Contributor

kozross commented Nov 17, 2021

This is definitely something I agree with in principle. My main question is regarding the Semigroup laws being violated by Value (as per the Plutus issue) - it almost feels like we should provide our own Value wrapper, with a projection function and extensive explanation. This would also solve the issue you cited here regarding clashing Arbitrary instances, and would also allow us to add whatever instances we want.

@kozross kozross assigned re-xyr and unassigned kozross Nov 18, 2021
@kozross
Copy link
Contributor

kozross commented Nov 18, 2021

For @re-xyr - once we figure out the right approach, make this change in plutus-extra. Minor bump should be fine. If we end up going for a full wrapper, ensure the following instances exist, or are inherited:

  • Eq (Plutus and base)
  • Ord (Plutus and base)
  • Show (via a stock derivation)
  • Semigroup and Monoid(Plutus and base, law-abiding)
  • ToJSON and FromJSON
  • ToData, FromData, FromUnsafeData
  • PrettyVal
  • AdditiveMonoid and MultiplicativeSemigroup

These will need tests as well - feel free to use plutus-laws and plutus-golden for this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants