Почему экземпляр Полугруппы Maybe смещен в сторону Just, а Monoid использует Nothing в качестве пустого элемента?

Maybe выражает вычисления, которые могут не дать результата из-за ошибки. Поэтому такие вычисления должны быть короткими.

Сейчас MaybeПохоже, что экземпляры полугруппы / моноида нарушают эту семантику, потому что первая склонна к Just и последний относится к ошибке Nothing как его пустой элемент:

Just "foo" <> Nothing -- Just "foo"
Nothing <> Just "bar" -- Just "bar"
Just "foo" <> Just "bar" -- Just "foobar"
Nothing <> Nothing -- Nothing

Я бы ожидал Nothing для первых двух случаев.

Вот альтернативная реализация (надеюсь, это правильно / законно):

instance Semigroup a => Semigroup (Maybe a) where
    Nothing <> _       = Nothing
    _       <> Nothing = Nothing
    Just a  <> Just b  = Just (a <> b)

instance Monoid a => Monoid (Maybe a) where
  mempty = Just mempty

Я не хочу сказать, что эти альтернативные примеры лучше. Но они тоже кажутся полезными. Так почему же выбор был сделан в первую очередь вместо того, чтобы оставить реализацию пользователю?

2 ответа

Ваш экземпляр на самом деле является частным случаем гораздо более общего случая для аппликативных функторов.

newtype LiftA f a = LiftA { getLiftA :: f a }

instance (Applicative f, Semigroup a) => Semigroup (LiftA f a) where
    LiftA x <> LiftA y = LiftA $ liftA2 (<>) x y

instance (Applicative f, Monoid a) => Monoid (LiftA f a) where
    mempty = LiftA $ pure mempty

Я предполагал, что он будет где-то в стандартной библиотеке (возможно, под другим именем), но я не смог его найти. Но существование этого общего экземпляра может быть одной из причин, чтобы выбрать версию библиотеки Maybe что больше Maybe особая сила. С другой стороны, очень приятно, когда все ваши алгебраические структуры согласованы друг с другом; т.е. когда тип является Applicative, использовать " LiftA экземпляр стиля, когда это возможно (на всех классах F-алгебры).

С третьей стороны (!) Мы не можем иметь согласованность везде, так как экземпляр библиотеки согласен с Maybe "s MonadPlus пример. Это поразительно параллельно тому факту, что на натуральных числах есть два моноида: сложение и умножение. Для чисел мы просто выбрали не иметь моноидного экземпляра, потому что неясно, какой использовать.

В заключение я не знаю. Но, возможно, эта информация была полезной.

Я думаю, что идея этого заключается в том, что Maybe должен иметь семантику, которая на самом деле только должным образом выражается Option: он берет любую полугруппу и делает из нее моноид, добавляя Nothing в качестве специального "свободного mempty". Т.е. на самом деле экземпляры должны быть

instance Semigroup a => Semigroup (Maybe a) where
  Nothing <> a = a
  a <> Nothing = a
  Just x <> Just y = Just $ x <> y

instance Semigroup a => Monoid (Maybe a) where
  mappend = (<>)
  mempty = Nothing

Это аналогично тому, как [] дает свободный моноид над любым типом, добавив оба mempty а также <>,

Конечно, это требует, чтобы Semigroup класс в base, чего не было до недавнего времени.

Следствием этого является то, что mempty становится явно сопоставимым с шаблоном, потому что параметрически не может зависеть от содержимого типа.

С практической точки зрения этот экземпляр, возможно, более полезен, чем тот, который вы предлагаете, потому что, как заметил Люк, он уже охватывается Applicative экземпляр, так что вы можете легко написать liftA2 (<>) а также pure mempty, Конечно, стандартный экземпляр также уже охватывается Alternative например, но Alternative/MonadPlus всегда считался немного хакерским, уступающим более математически безупречному Semigroup, Monoid, Functor а также Applicative,

Другие вопросы по тегам