Y a-t-il quelque chose de similaire à la classe de type Applicative
, mais où il y a deux foncteurs pour chaque côté de l'application qui sont différents?
c'est-à-dire (<*>) :: (Functor f, Functor g) => f (a -> b) -> g a -> f b
4 Réponses :
D'après votre commentaire, je pense que vous essayez peut-être de construire:
> import qualified Data.Vector as V > foo [(+1),(+2),(+3)] (V.fromList [5,6,7]) [8,8,8] >
Cela permet, par exemple:
import Data.Foldable import Data.Traversable foo :: (Traversable f, Foldable g) => f (a -> b) -> g a -> f b foo f g = snd $ mapAccumR (\(a:as) fab -> (as, fab a)) (toList g) f
toto [(+1), (+ 2), (+ 3)] (V.fromList [5,6,7,8])
fonctionne, toto [(+1), (+2), (+ 3)] (V.fromList [5,6])
bombes avec "Modèles non exhaustifs en lambda"
.
Oui, je savais que la fonction était partielle, mais je ne pense pas qu'il y ait d'alternative claire. Vous pouvez parcourir
la liste à partir de g
, ce qui semble être une mauvaise idée, ou vous pouvez limiter la fonction à f
qui sont isomorphes à []
(par exemple, utilisez la contrainte IsList f
).
Je ne connais aucun J'écrirais la version concrète, ou tout au plus généraliserais sur les types d'entrée. Voici des exemples avec fromList
général. Vector
, en ignorant que Data.Vector.zip
existe déjà.
import qualified Data.Vector as V import Data.Vector (Vector) import Data.Foldable import GHC.Exts (IsList(fromList)) zipV1 :: Vector (a -> b) -> Vector a -> Vector b zipV1 fs as = V.fromList (zipWith ($) (V.toList fs) (V.toList as)) zipV2 :: (Foldable f, Foldable g, IsList (f b)) => f (a -> b) -> g a -> f b zipV2 fs as = fromList (zipWith ($) (toList fs) (toList as))
Vous pouvez utiliser IsList code> au lieu de
Foldable
dans le deuxième exemple.
Notez qu'il y a fromList
from IsList
, donc cela fonctionnerait, et de nombreuses séquences standard isomorphes de liste le supportent (par exemple, Vector
une instance).
Un concept général de "type séquence" est un monoïde libre. Puisque vous regardez des types de séquences polymorphes, nous pouvons construire sur Traversable
.
fromList = foldMap singleton
Voir la note ci-dessous.
type Semigroup1 t = forall a. Semigroup (t a) type Monoid1 t = forall a. Monoid (t a)
En quoi est-ce un type de séquence? Très inefficace. Mais nous pourrions ajouter un tas de méthodes avec des implémentations par défaut pour le rendre efficace. Voici quelques fonctions de base:
zipApp :: (Foldable t, Foldable u, Sequence v) = t (a -> b) -> u a -> v b zipApp fs xs = fromList $ zipWith ($) (toList fs) (toList xs)
Avec ces outils, vous pouvez compresser deux séquences quelconques pour en créer une troisième.
cons :: Sequence t => a -> t a -> t a cons x xs = singleton x <=> xs fromList :: (Foldable f, Sequence t) => f a -> t a fromList = foldr cons mempty1 uncons :: Sequence t => t a -> Maybe (a, t a) uncons xs = case toList xs of y:ys -> Just (y, fromList ys) [] -> Nothing
Pour le GHC de pointe, vous pouvez utiliser QuantifiedConstraints
et RankNTypes
et ConstraintKinds
et définissez
class (Traversable t, Monoid1 t) => Sequence t where singleton :: a -> t a
Le faire de cette façon vous permettrait d'écrire, par exemple,
class Semigroup1 t where (<=>) :: t a -> t a -> t a class Semigroup1 t => Monoid1 t where mempty1 :: t a
(Suite à une suggestion de @dfeuer dans les commentaires.)
Il existe une construction appelée day convolution qui vous permet de conserver la distinction entre deux foncteurs lors de l'exécution d'opérations applicatives, et de retarder le moment de la transformation de l'un dans l'autre.
Le Day code>
type est simplement une paire de valeurs fonctionnelles, avec une fonction qui combine leurs résultats respectifs:
res' :: ZipList (Char,Char) res' = dap $ trans2 (ZipList . toList) res ghci> res' ZipList {getZipList = [('b','f'),('a','o')]}
Notez que les valeurs de retour réelles des foncteurs sont existencialisé; la valeur de retour de la composition est celle de la fonction.
Day
présente des avantages par rapport aux autres façons de combiner des foncteurs applicatifs. Contrairement à Sum
a>, la composition est toujours applicative. Contrairement à Compose
a>, la composition est "impartiale" et n'impose pas d'ordre d'imbrication. Contrairement à Produit
a>, cela nous permet de combiner facilement des actions applicatives avec différents types de retour, nous avons juste besoin de fournir une fonction d'adaptation appropriée.
Par exemple, voici deux Day ZipList (Vec Nat2) Char
valeurs:
res :: Day ZipList (Vec Nat2) (Char,Char) res = (,) <$> day1 <*> day2
( Nat2
provient de fin package, il est utilisé pour paramétrer un Vec
de taille fixe à partir de vec .)
Nous pouvons très bien les compresser ensemble:
{-# LANGUAGE DataKinds #-} import Data.Functor.Day -- from "kan-extensions" import Data.Type.Nat -- from "fin" import Data.Vec.Lazy -- from "vec" import Control.Applicative day1 :: Day ZipList (Vec Nat2) Char day1 = Day (pure ()) ('b' ::: 'a' ::: VNil) (flip const) day2 :: Day ZipList (Vec Nat2) Char day2 = Day (ZipList "foo") (pure ()) const
Et puis transformez le Vec
en une ZipList
et réduisez le Jour
:
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
Utilisation de dap
et trans2 code>
.
Problème de performance possible: lorsque nous élevons l'un des foncteurs à Day
, l'autre reçoit un mannequin pure () code> valeur. Mais c'est un poids mort lors de la combinaison de
Day
s avec ()
. On peut travailler plus intelligemment en enveloppant les foncteurs dans Lift
pour les transformateurs, pour obtenir des opérations plus rapides pour les cas simples "purs".
Un exemple pratique de Day
: ma bibliothèque "process-streaming" utilise Day
en interne pour combiner des gestionnaires pour les stdin, stdout et stderr d'un processus externe en un seul Applicatif
. hackage.haskell. org / package / process-streaming-0.9.3.0 / docs /… Il utilise également l'astuce Lift
.
Y a-t-il des types de béton spécifiques avec lesquels vous souhaitez l'utiliser? Cette fonction ne peut pas être dérivée pour n'importe quel deux foncteurs.
Ou deux applicatifs quelconques.
@ 4castle: Désolé, je n'ai pas assez bien réfléchi. Je recherche un zip généralisé capable de gérer et de convertir automatiquement différents types de séquences.
Je pense que vous auriez besoin de fournir vous-même la transformation naturelle d'un foncteur à l'autre; même en supposant que vous puissiez extraire la fonction de l'applicatif
f
et l'appliquer auga
pour obtenir ungb
, vous avez toujours besoin de quelque chose avec le typegb -> fb
pour obtenir votre résultat final.(Probablement deux transformations naturelles, l'autre pour obtenir un
g (a -> b)
def (a -> b)
dans le premier endroit.)