Nick Tchayka
Nick Tchayka

Reputation: 573

Is there a Monoid equivalent of Bifunctor?

When working with a Bifunctor, we gain access to the first and second "map" functions. So basically it is a Functor that allows us to fmap in two different ways.

Is there something like this for Monoid? Some concept that allows us to append in two different ways?

For example, imagine an opaque Matrix type. It is not a list of lists or a vector of vectors, we have no clue how it is structured inside, but we know that we can append rows and columns to it.

Would there be some type class that allowed to do this?

class X a where
    firstAppend :: a -> a -> a
    secondAppend :: a -> a -> a

instance X Matrix where
    firstAppend = appendRow
    secondAppend = appendColumn

Upvotes: 3

Views: 146

Answers (1)

gallais
gallais

Reputation: 12123

I guess you could do something like this with indexed Monoids:

{-# LANGUAGE PolyKinds      #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies   #-}

module IndexedMonoids where

class MonoidIx (m :: k -> *) where
  type Null m :: k
  type Mult m (i :: k) (j :: k) :: k

  nullIx :: m (Null m)
  multIx :: m i -> m j -> m (Mult m i j)

class MonoidIx2 (m :: k -> l -> *) where
  type Null1 m :: k
  type Null2 m :: l
  type Mult1 m (i :: k) (j :: k) :: k
  type Mult2 m (p :: l) (q :: l) :: l

  null1Ix :: m (Null1 m) p
  null2Ix :: m i (Null2 m)
  mult1Ix :: m i p -> m j p -> m (Mult1 m i j) p
  mult2Ix :: m i p -> m i q -> m i (Mult2 m p q)

You'd expect a bunch of laws (identity, associativity, commutativity when you put 4 blocks together). A trivial example of an indexed Monoid: the one where the index does not matter:

newtype Dummy (m :: *) (i :: k) = Dummy { getDummy :: m }

instance Monoid m => MonoidIx (Dummy m :: * -> *) where
  type Null (Dummy m)     = ()
  type Mult (Dummy m) i j = ()

  nullIx = Dummy mempty
  multIx (Dummy i) (Dummy j) = Dummy $ mappend i j

I'll let you implement the instance for matrices ;)

Upvotes: 2

Related Questions