Alternative
jest trochę hacky bestii. Zasadniczo jest to klasa monofonicznych konstruktorów : typowych T
takich, że dla dowolnego zawartego typu jest monoidem. To nie ma wiele wspólnego z funktorami ... monadami i jest znacznie mniej matematycznie głębokie. (Tak, tylko dla matematycznej elegancji, byłoby nieco źle ustawiony Monad
pod Alternative
).
Napiszmy, że wystąpienie w kategoriach Monoid
dla jasności (nie będzie faktycznie kompilacji):
instance (Foldable f, (∀ x . Monoid (f x))) => Monad f where
(>>=) = flip $ \f -> foldr mappend empty . fmap f
≡ flip $ \f -> fold . fmap f
≡ flip foldMap
lub rzeczywiście
to zdecydowanie nie jest coś nieznanego.
Aby sprawdzić przepisy ustawowe, mamy najlepszy wygląd w formułowaniu Kleisli:
(f <=< g) x = f =<< g x
≡ foldMap f $ g x
tj
f <=< g = foldMap f . g
Następnie ustaw Monad są
Lewy tożsamość
f <=< pure ≡ foldMap f . pure =! f
Prawy tożsamość
pure <=< f ≡ foldMap pure . f =! f
Łączność
(f <=< g) <=< h ≡ foldMap (foldMap f . g) . h
=! foldMap f . foldMap g . h
≡ foldMap f . (foldMap g . h) ≡ f <=< (g <=< h)
Tak w skrócie, musimy
foldMap f . pure =! f =! foldMap pure . f
∀ f
foldMap (foldMap f . g) =! foldMap f . foldMap g
∀ f
, g
To na pewno nie wygląda nierozsądne, ale nie widzę skąd można rygorystycznie zawrzeć to dla arbitralnego Foldable
+ Alternative
wystąpień.
Naprawdę, dużym problemem, jaki widzę w tym przypadku, jest to, że nie jest on wystarczająco ogólny. Większość monad nie jest ani Foldable
ani Alternative
. Jeśli istniała definicja typu "cover-all", taka jak ta, którą proponujesz, wymagałaby ona zdefiniowania dowolnej instancji na poziomie OverlappingInstances
, a te są ogólnie uważane za coś, czego nie powinieneś używać bez uzasadnionego powodu.
Zastanawiam się jednak, czy nie byłoby żadnych problemów z domyślnej definicji dla metody wiązania po :
{-# LANGUAGE DefaultSignatures #-}
class Applicative f => Monad f where
return :: a -> m a
return = pure
(>>=) :: m a -> (a -> m b) -> m b
default (>>=) :: (Foldable m, Monoid m b)
=> m a -> (a -> m b) -> m b
(>>=) = flip foldMap
To by przynajmniej umożliwić zdefiniowanie np instancja lista prostu jako
instance Monad []
bez konieczności wypisać metody w ogóle, ponieważ na pewno wystarczy, foldMap ≡ concatMap ≡ (=<<)
.
Nie dlatego, że nie * łamie * żadnych praw, że jest * pożądany *. Myślę, że zwykle nie chcemy automatycznego wyprowadzania Monady, ponieważ można zdecydować się na monadę w inny sposób. –
Na przykład, jeśli masz klasę typu, która obsługuje tablicę asocjacyjną (słownik), możesz zdefiniować ją jako monadę stanu. Być może jednak chcesz, aby jakiś układ asocjacyjny nie działał jako monada stanu, ale jako (emulowany) procesor. –