В экземплярах Monoid и Semigroup используется альтернативный Alt.
Почему мы не можем написать экземпляр без него?
{-# LANGUAGE FlexibleInstances #-}
instance Alternative f => Semigroup (f a) where
(<>) = <|>
instance Alternative f => Monoid (f a) where
mempty = empty
И если мы можем написать это, можем ли мы тогда заменить Альтернативу на (Моноид (f a), Аппликативная f) в функциях?
Я нахожу это там
При этом, хотя многие типы данных могут иметь экземпляры Semigroup
и Monoid
, определенные в зависимости от того, как реализован Alternative
, это не лучший экземпляр сам по себе. Да, Alternative
— это моноид для аппликативных функторов, но сам по себе он не должен быть экземпляром моноида для этих типов данных.
Вы используете его для получения Monoid
для любого Alternative
{-# Language DerivingVia #-}
data F a = ..
deriving (Semigroup, Monoid)
via Alt F a
instance Functor F where ..
instance Applicative F where ..
instance Alternative F where ..
{-# Language DerivingVia #-}
data G a = ..
deriving (Semigroup, Monoid, Num, Bounded)
via Ap G a
instance Functor G where ..
instance Applicative G where ..
Экземпляры, которые вы даете, максимально перекрываются, экземпляр Monoid
любого применяемого типа теперь вынужден быть экземпляром Alternative
, полностью игнорируя параметр a
.
Есть много случаев, когда это было бы неправильно, например, Semigroup a => Semigroup (Maybe a)
не совпадает с Semigroup
, которое вы получили бы от Alternative Maybe
.
Можно использовать довольно новую функцию QuantifiedConstraints
для количественной оценки аргумента конструктора типа forall x. Monoid (f x)
. Это не то же самое, что Alternative
, но похоже
{-# Language QuantifiedConstraints #-}
..
type Alternative' :: (Type -> Type) -> Constraint
class (forall x. Monoid (f x)) => Alternative' f
instance (forall x. Monoid (f x)) => Alternative' f
Что с автономными экземплярами, такими как instance Functor F
? Подразумевает ли DerivingVia
как-то реализацию экземпляра?
Да, когда вы получаете через Alt F a
, для этого требуются экземпляры Alternative F
→ Applicative F
→ Functor F
, а для Ap G a
требуется Applicative G
. Они означают написанные пользователем экземпляры, но интересная функция в последней базе (4.17) имеет возможность получать эти экземпляры для некоторых случаев:
Если бы экземпляры, которые вы предлагаете, существовали в этой форме, каждый тип, соответствующий f a
, немедленно подчинялся бы ей. Это включает в себя типы, где это имеет смысл, но учтите
newtype ResultsSum a = ResultsSum {funToSum :: a -> Int}
instance Semigroup (ResultsSum a) where
ResultsSum p <> ResultsSum q = ResultsSum $ \x -> p x + q x
К сожалению, ResultsSum a
соответствует f a
. Но это не Alternative
; это даже не функтор и не может им быть (скорее, это контравариант). Однако компилятор не принимает это во внимание при разрешении экземпляров: он просто видит два объявления экземпляров, оба заголовка которых предполагают, что ResultsSum
является полугруппой, что вызывает ошибку неоднозначного экземпляра.
Конечно, этот пример можно решить с помощью прагм {-# OVERLAPPING #-}
, но всегда лучше избегать перекрытий экземпляров, поскольку они могут привести к странным результатам. В этом нет необходимости, поскольку вы также можете получить эти экземпляры via
Alternative
. Хотя лично я предпочел бы сделать наоборот: сначала определить экземпляр Monoid
, а затем Alternative
в его терминах.
Где вы нашли это определение? Я бы не ожидал, что это будет в стандартной библиотеке.