Il solito elenco eterogeneo può essere utilizzato qui:
{-# 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
solitamente scrivono le funzioni di HLists
utilizzando classi, con un'istanza per Nil
e un altro per il caso :>
. Tuttavia, non sarebbe abbastanza per avere una classe solo per un caso d'uso singola (cioè i prodotti cartesiani qui), quindi cerchiamo di generalizzare il problema al sequenziamento applicativa:
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
Nota l'uso di ~
vincoli nell'istanza definizione. Aiuta molto tipo l'inferenza (insieme alle dipendenze funzionali nella dichiarazione di classe); l'idea generale è di spostare quante più informazioni possibili dalla testa dell'istanza ai vincoli, perché ciò consente a GHC di risolverli fino a quando non ci sono sufficienti informazioni contestuali.
prodotti Ora cartesiani lavorare fuori dalla scatola:
> hsequence ([1, 2] :> "ab" :> Nil)
[1 : 'a' : Nil,1 : 'b' : Nil,2 : 'a' : Nil,2 : 'b' : Nil]
e possiamo anche utilizzare hsequence
con qualsiasi Applicative
:
> hsequence (Just "foo" :> Just() :> Just 10 :> Nil)
Just "foo" :() : 10 : Nil
EDIT: ho scoperto (grazie dfeuer) che la stessa funzionalità è disponibile dal pacchetto hlist
esistente:
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 davvero? Nella mia interpretazione, chiede combinazioni, non cerniere; Ad esempio, 'liftA2 (,)' e così via (per le liste). – phg
@phb Hai ragione.Tuttavia, se potessimo usare coppie nidificate invece di tuple, saremo in grado di risolverlo attraverso una classe di caratteri e 'liftA2 (,)', come proponete. Con le tuple è più difficile poiché non c'è un modo pratico per passare da una tupla n a una tupla + 1. – chi
Un fatto divertente: GHC non supporta tuple più lunghe di 62 voci: https://downloads.haskell.org/~ghc/latest/docs/html/libraries/ghc-prim-0.3.1.0/src/GHC-Tuple. html. Quindi c'è sicuramente un modo per implementare tutte le varianti di 'cartProdN' fino a 62 a mano;). Detto questo, hai davvero bisogno di prodotti cartesiani arbitrari? C'è probabilmente un motivo per cui 'zipWith *' e le sue varianti si fermano a '7' ... – Zeta