Здесь можно использовать обычный разнородный список:
{-# LANGUAGE
UndecidableInstances, GADTs,
TypeFamilies, MultiParamTypeClasses,
FunctionalDependencies, DataKinds, TypeOperators,
FlexibleInstances #-}
import Control.Applicative
data HList xs where
Nil :: HList '[]
(:>) :: x -> HList xs -> HList (x ': xs)
infixr 5 :>
-- A Show instance, for illustrative purposes here.
instance Show (HList '[]) where
show _ = "Nil"
instance (Show x, Show (HList xs)) => Show (HList (x ': xs)) where
show (x :> xs) = show x ++ " : " ++ show xs
Обычно мы пишем функции для HLists
, используя классы, с одним экземпляром для Nil
и другим для случая :>
. Однако было бы некрасиво иметь класс только для одного варианта использования (а именно для декартовых произведений), поэтому давайте обобщим проблему на аппликативную последовательность:
class Applicative f => HSequence f (xs :: [*]) (ys :: [*]) | xs -> ys, ys f -> xs where
hsequence :: HList xs -> f (HList ys)
instance Applicative f => HSequence f '[] '[] where
hsequence = pure
instance (Applicative g, HSequence f xs ys, y ~ x, f ~ g) =>
HSequence g (f x ': xs) (y ': ys) where
hsequence (fx :> fxs) = (:>) <$> fx <*> hsequence fxs
Обратите внимание на использование ограничений ~
в определении экземпляра. Это очень помогает в выводе типов (наряду с функциональными зависимостями в объявлении класса); общая идея состоит в том, чтобы переместить как можно больше информации из заголовка экземпляра в ограничения, потому что это позволяет GHC откладывать их решение до тех пор, пока не будет достаточно контекстуальной информации.
Теперь декартовы произведения работают из коробки:
> hsequence ([1, 2] :> "ab" :> Nil)
[1 : 'a' : Nil,1 : 'b' : Nil,2 : 'a' : Nil,2 : 'b' : Nil]
И мы также можем использовать hsequence
с любым Applicative
:
> hsequence (Just "foo" :> Just () :> Just 10 :> Nil)
Just "foo" : () : 10 : Nil
РЕДАКТИРОВАТЬ: я узнал (спасибо dfeuer), что те же функции доступны из существующего пакета hlist
:
import Data.HList.CommonMain
> hSequence ([3, 4] .*. "abc" .*. HNil)
[H[3, 'a'],H[3, 'b'],H[3, 'c'],H[4, 'a'],H[4, 'b'],H[4, 'c']]
person
András Kovács
schedule
03.03.2015
liftA2 (,)
и т. д. (для списков). - person phipsgabler   schedule 03.03.2015liftA2 (,)
, как вы предлагаете. С кортежами сложнее, так как нет удобного способа перейти от n-кортежа к n+1-кортежу. - person chi   schedule 03.03.2015cartProdN
до 62 вручную ;). При этом вам действительно нужны произвольные декартовы произведения? Вероятно, есть причина, по которойzipWith*
и его варианты останавливаются на7
… - person Zeta   schedule 03.03.2015