Zwykła lista heterogeniczne można stosować tutaj:
{-# 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
Zwykle napisać funkcje na HLists
korzystających z zajęć, z jednej instancji do Nil
i drugiego w przypadku :>
. Jednak nie byłoby to dość mieć klasę tylko dla pojedynczego przypadku użycia (czyli iloczyn kartezjański tutaj), więc niech generalizować problemu do aplikacyjnej sekwencjonowania:
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
Uwaga wykorzystanie ~
ograniczeń w instancji definicja. Bardzo pomaga w typowaniu wnioskowania (wraz z zależnościami funkcjonalnymi w deklaracji klasy); ogólną ideą jest przeniesienie jak największej ilości informacji z głowy instancji do ograniczeń, ponieważ to pozwala GHC opóźniać ich rozwiązywanie, dopóki nie będzie wystarczającej informacji kontekstowej.
produkty Teraz kartezjańskie pracy po wyjęciu z pudełka:
> hsequence ([1, 2] :> "ab" :> Nil)
[1 : 'a' : Nil,1 : 'b' : Nil,2 : 'a' : Nil,2 : 'b' : Nil]
I możemy również użyć hsequence
z dowolnym Applicative
:
> hsequence (Just "foo" :> Just() :> Just 10 :> Nil)
Just "foo" :() : 10 : Nil
EDIT: znalazłem się (dzięki dfeuer), że taką samą funkcjonalność jest dostępny z istniejącego pakietu 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']]
@chi naprawdę? W mojej interpretacji prosi o kombinacje, a nie zamki; tj. 'liftA2 (,)' i tak dalej (dla list). – phg
@phb Masz rację.Mimo to, gdybyśmy mogli użyć par zagnieżdżonych zamiast krotek, bylibyśmy w stanie rozwiązać to poprzez klasę typów i 'liftA2 (,)', jak proponujesz. Z krotkami jest to trudniejsze, ponieważ nie ma wygodnego sposobu na przejście z n-krotki na n + 1-krotkę. – chi
Zabawny fakt: GHC nie obsługuje krotek dłuższych niż 62 wpisy: https://downloads.haskell.org/~ghc/latest/docs/html/libraries/ghc-prim-0.3.1.0/src/GHC-Tuple. html. Więc zdecydowanie istnieje sposób na wdrożenie wszystkich wariantów 'cartProdN' do 62 ręcznie;). Biorąc to pod uwagę, czy naprawdę potrzebujesz arbitralnych produktów kartezjańskich? Prawdopodobnie istnieje powód, dla którego 'zipWith *' i jego warianty zatrzymują się na '7' ... – Zeta