Une fois, j'ai posé cette question, qui était correctement marqué comme duplicata de celui-ci.

Maintenant, j'ai une curiosité, y a-t-il un cas d'utilisation connu pour l'instance de monade d'une paire de types homogènes?

Voici ses exemples:

data Pair a = Pair a a deriving Show

instance Functor Pair where
  fmap f (Pair a b) = Pair (f a) (f b)

instance Applicative Pair where
  pure a = Pair a a
  Pair f g <*> Pair x y =  Pair (f x) (g y)

instance Monad Pair where
    m >>= f = joinPair (f <$> m)

joinPair :: Pair (Pair a) -> Pair a
joinPair (Pair (Pair x _) (Pair _ y)) = Pair x y
4
Enlico 11 mars 2021 à 20:32

2 réponses

Meilleure réponse

Votre Pair a est isomorphe à Reader Bool a / Bool -> a:

to (Pair f t) = \b -> if b then t else f

from f = Pair (f False) (f True)

En tant que tel, tout cas d'utilisation de la monade Reader est également un cas d'utilisation potentiel pour votre monade. Le terme général pour ces sortes de types de données est foncteurs représentables .

4
Joseph Sible-Reinstate Monica 11 mars 2021 à 18:43

Je n'ai jamais utilisé une monade comme celle-ci auparavant, mais après avoir trouvé cet exemple, je peux voir ses mérites. Cela calculera la distance entre deux coordonnées cartésiennes. Cela semble en fait très utile car il sépare automatiquement toutes les opérations sur les X de toutes les opérations sur les Y.

collapsePair :: (a -> a -> b) -> Pair a -> b
collapsePair f (Pair x y) = f x y

type Coordinates = Pair Float
type Distance = Float
type TriangleSides = Pair Distance

-- Calculate the sides of a triangle given two x/y coordinates
triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides start end = do
    -- Pair x1 y1
    s <- start

    -- Pair x2 y2
    e <- end

    -- Pair (x2 - x1) (y2 - y1)
    Pair (e - s) (e - s)
    
-- Calculate the cartesian distance
distance :: Coordinates -> Coordinates -> Distance
distance start end = collapsePair distanceFormula (triangleSides start end)
    where distanceFormula x y = sqrt (x ^ 2 + y ^ 2)

Modifier:

Il s'avère que cet exemple pourrait être fait avec juste l'instance Applicative de Pair; pas de Monad nécessaire:

import Control.Applicative

triangleSides :: Coordinates -> Coordinates -> TriangleSides
triangleSides = liftA2 (flip (-))

Mais, nous pourrions le faire dépendre de Monad de manière artificielle, en ajoutant 1 à X à la fin:

triangleSides' :: Coordinates -> Coordinates -> TriangleSides
triangleSides' start end = do
    s <- start
    e <- end
    Pair (e - s + 1) (e - s)

Dans ce cas, la dernière ligne ne peut être convertie en une forme quelconque de pure ... et doit donc utiliser l'instance Monad.


Quelque chose d'amusant qui peut être exploré plus loin, c'est que nous pouvons facilement étendre cela pour inclure des coordonnées tridimensionnelles (ou plus). Nous pouvons utiliser l'instance par défaut de Representable et dériver les instances Applicative et Monad via le Co newtype de Data.Functor.Rep.

Nous pouvons ensuite faire abstraction du calcul distance dans sa propre classe de types et il fonctionnera sur des coordonnées de n dimensions, tant que nous écrivons une instance Distributive pour le type de coordonnées.

{-# Language DeriveAnyClass #-}
{-# Language DeriveGeneric #-}
{-# Language DeriveTraversable #-}
{-# Language DerivingVia #-}

import Control.Applicative
import Data.Distributive
import Data.Functor.Rep
import GHC.Generics

class (Applicative c, Foldable c) => Coordinates c where
    distance :: Floating a => c a -> c a -> a
    distance start end = sqrt $ sum $ fmap (^2) $ liftA2 (-) end start

data Triple a = Triple
    { triple1 :: a
    , triple2 :: a
    , triple3 :: a
    }
    deriving ( Show, Eq, Ord, Functor, Foldable, Generic1, Representable
             , Coordinates )
    deriving (Applicative, Monad) via Co Triple

instance Distributive Triple where
    distribute f = Triple (triple1 <$> f) (triple2 <$> f) (triple3 <$> f)
> distance (Triple 7 4 3) (Triple 17 6 2)
10.246950765959598

Vous pouvez vérifier cette réponse ici.

3
hololeap 11 mars 2021 à 20:32