How can I best imagine a Cartesian product of some fixed size?

This question has been heavily rewritten as suggested by @leftaroundabout. An earlier version can be seen in the editing history.

Haskell is known for facilitating thought by allowing more direct coding of mathematical abstractions. Cartesian work is a very basic mental object, with which many are familiar from childhood. However, there is nothing like this in Haskell. I think I need one so that my thinking flows, if nothing else. (Although this post is really inspired by some kind of ground-related code that I have on hand.) Let’s then form a common understanding that it is Cartesian (I will call it simply Cartesian).

Given the sequence of d :: Intcollection lengths (for example [[1,2], ['a', 'b']]), I would like to have all the combinations of my elements in the shortest possible time. This means that they work for them as if they were in the usual Functor, Foldable, Traversable, Monoid, etc. Indeed, we can represent any Cartesian as a suitable nested list of tuples:

type Lattice = [[(x, y)]]

type WeightedLattice = [[(x, y, w)]]

zipWith2 :: (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
zipWith2 f = zipWith (zipWith f)

fmap2 :: (Functor f, Functor g) => (a -> b) -> g (f a) -> g (f b)
fmap2 = fmap . fmap

sequence2 :: (Traversable s, Traversable t, Monad m) => t (s (m a)) -> m (t (s a))
sequence2 = sequence . fmap sequence

Similar constructions can be written out manually for any nesting depth.

We now introduce the difference between the Cartesian and the initial Cartesian:

  • The n-dimensional Cartesian is constructed from a heterogeneous sequence of collections, taking one element from each collection and combining them in an ordered manner with a suitable typed function. This Cartesian signature [Int, Char, Bool] can be formed using a function such as:

    f :: Int -> Char -> Bool -> Either Char Int
    f i c b = if b then Left c else Right i 
    
  • arity:

    initial :: Int -> Char -> Bool -> (Int, Char, Bool)
    initial = (,,)
    

, , , , :

    (fmap . ... . fmap) (uncurryN f)

; , Char a Right 3. , - , .

Lattice, , , . , . , , . , , .

. , ennuy , : 128 x 64 64 x 128 , 64 x 64; . , , , foldr (.) id [replicate d concat], . haskelly .

, : . , , , -, p , , - . , . Set , ?

, , :

  • . , . Data.List .

  • . , , . , , [1..3] x ['a', 'b'], , , [1,2] x ['a'..'c'], , zip .

  • , , . :

    Cartesian [[1..3], ['a', 'b']] <> Cartesian [[True, False]]
    

    - , :

    Cartesian [[1..3], ['a', 'b'], [True, False]]
    

    - , .

  • - , , , . , a Lattice . , , , , , "".

  • .

, , , , , . - . , .

+4
2

, , vinyl -style. vinyl; , .

{-# language DataKinds, PolyKinds, TypeOperators, GADTs #-}
module Cart where
import Data.Kind (Type)
import Data.Functor.Identity

infixr 4 :<
data Rec :: [k] -> (k -> Type) -> Type where
  Nil :: Rec '[] f
  (:<) :: f a -> Rec as f -> Rec (a ': as) f

newtype HList xs = HList (Rec xs Identity)

prod :: Rec as [] -> [HList as]
prod = map HList . go
  where
    go :: Rec as [] -> [Rec as Identity]
    go Nil = [Nil]
    go (xs :< xss) = [ Identity x :< r | x <- xs, r <- go xss]

Show (, ) -

> prod $ [3,4,5] :< ["hello", "goodbye"] :< ['x'] :< Nil

[ H[3,"hello",'x'], H[3,"goodbye",'x'], H[4,"hello",'x']
, H[4,"goodbye",'x'], H[5,"hello",'x'], H[5,"goodbye",'x'] ]

prod, , , Identity . , "" Rec Applicative, []:

class Trav (t :: (k -> Type) -> Type) where
  trav :: Applicative g => (forall a. f a -> g (h a)) -> t f -> g (t h)

instance Trav (Rec as) where
  trav f Nil = pure Nil
  trav f (xs :< xss) = (:<) <$> f xs <*> trav f xss

Data.Vinyl.rtraverse, , , .


Monoid, mappend . , , :

type family (++) xs ys where
  '[] ++ ys = ys
  (x ': xs) ++ ys = x ': xs ++ ys

(><) :: Rec xs f -> Rec ys f -> Rec (xs ++ ys) f
Nil >< ys = ys
(x :< xs) >< ys = x :< (xs >< ys)

. , , , .

trav f (xs >< ys) = (><) <$> trav f xs <*> trav f ys

( -).

forall f k (as :: [k]) (bs :: [k]). Rec as f -> Rec bs f

, a Rec, , .


:

class Functor1 (t :: (k -> Type) -> Type) where
  map1 :: (forall x. f x -> g x) -> t f -> t g

instance Functor1 (Rec as) where
  map1 f = runIdentity . trav (\x -> Identity (f x))

Zipping:

rzip :: (forall x. f x -> g x -> h x)
     -> Rec as f -> Rec as g -> Rec as h
rzip f Nil Nil = Nil
rzip f (x :< xs) (y :< ys) = f x y :< rzip f xs ys
+4

, :

type A e = Array Int e

data Cartesian v = Cartesian
    { _dimensions :: [Int]
    , _values :: A v
    } deriving (Show, Eq, Ord, Functor, Foldable, Traversable)

-- # Some helper functions.

autoListArray :: [a] -> A a
autoListArray xs = listArray (0, pred (length xs)) xs

-- | Get elements of an array such that they all belong to the
--   congruence class c modulo n.
getByCongruentIndices :: Int -> Int -> A v -> [v]
getByCongruentIndices n c arr =
    let (low, high) = bounds arr
    in  (arr !) <$> [low + c, low + c + n.. high]

arr :: [v] -> A v
arr = autoListArray

unarr :: A v -> [v]
unarr = elems

congr :: Int -> Int -> A v -> [v]
congr = getByCongruentIndices

congr0 :: Int -> A v -> [v]
congr0 n = congr n 0

.

  • , , : .

  • : , , uni cons.

    -- | Consruct a uni-dimensional Cartesian.
    uni :: [v] -> Cartesian v
    uni vs = Cartesian { _dimensions = [length vs], _values = arr vs }
    
    -- | Dimension increment.
    cons :: (u -> v -> w) -> [u] -> Cartesian v -> Cartesian w
    cons f xs Cartesian{..} = Cartesian
        { _dimensions = length xs: _dimensions
        , _values = arr [ x `f` y | x <- xs, y <- unarr _values ]
        }
    
  • : uncons.

    -- | Dimension decrement.
    uncons :: (u -> (v, w)) -> Cartesian u -> Maybe ([v], Cartesian w)
    uncons _ Cartesian { _dimensions = [] } = Nothing
    uncons f Cartesian { _dimensions = (_: ds), _values = xs } =
        let ys = fmap (fst . f) . congr0 (product ds) $ xs
            zs = fmap (snd . f) . take (product ds) . unarr $ xs
        in  Just (ys, Cartesian { _dimensions = ds, _values = arr zs })
    
  • : (↑) . , , , . , (↑) x 1 Data.List.traverse.

    -- | Bubble: apply a cycle from 0 to (i - 1) to the dimensions. That is, make the i-th dimension 
    --   the first. I believe bubbles to be the generators of the symmetric group.
    (↑) :: Cartesian u -> Int -> Cartesian u
    Cartesian{..} ↑ i =
        let d  = product . drop i $ _dimensions
            ds = take i _dimensions ++ drop (succ i) _dimensions  -- Delete the i-th.
        in  Cartesian
                { _dimensions = ds
                , _values = arr . concat $ ($ _values) <$> (congr d <$> [0..pred d])
                }
    
  • cons, uncons (↑) . :

    appendWith :: (u -> v -> w) -> Cartesian u -> Cartesian v -> Cartesian w
    appendWith f x y = Cartesian { _dimensions = _dimensions x ++ _dimensions y
                                 , _values = arr [ x `f` y | x <- unarr (_values x), y <- unarr (_values y) ]
                                 }
    
  • :

    glue f x y | _dimensions x == _dimensions y
                    = Cartesian
                        { _dimensions = _dimensions x
                        , _values = arr $ zipWith f (unarr $ _values x) (unarr $ _values y)
                        }
               | otherwise = undefined
    

, , , Cartesians, . , .

Monoid, mempty, .

0

Source: https://habr.com/ru/post/1693515/


All Articles