2015-02-21 3 views
11

Я часто читал, чтоПочему Идентификационная монада полезна?

Кажется, что идентичная монада бесполезна. Это не ... но это еще одна тема .

Так может кто-нибудь сказать мне, как это полезно?

+0

Я вижу это как отличный инструмент для объяснения монады для людей, не имеющих прежних знаний об этом виде вещей, и как ценное упражнение для любого, кто научится применять монады. – ThreeFx

ответ

12

Identity является монады, функторов и аппликативных функторы 0 номерамов. Само по себе это кажется бесполезным, но часто это необходимо в местах, где можно ожидать, что монада или (аппликативный) функтор фактически ничего не делают.

Как уже упоминалось, Identity позволяет нам определять только монадные трансформаторы, а затем определять их соответствующие монады как SomeT Identity.

Но это еще не все. Часто бывает удобно также определять другие понятия в терминах монадов, что обычно добавляет большую гибкость. Например, Conduit i m o (также см. this tutorial) определяет элемент в конвейере, который может запрашивать данные типа i, может создавать данные типа o и использует монаду m для внутренней обработки. Затем такой трубопровод может работать в данной монаде с использованием

($$) :: Monad m => Source m a -> Sink a m b -> m b 

(где Source является псевдонимом для Conduit, без ввода и Sink для Conduit без выхода). А когда нет effectful вычисления не нужно в трубопроводе, только чистый код, мы просто специализируемся m на Identity и запустить такой трубопровод, как

runIdentity (source $$ sink) 

Identity также «пустой» функтор и аппликативен функтор: Identity состоящий из другого функтора или аппликативного функтора, изоморфен оригиналу. Например, Lens' определяется как функция полиморфной в Functor:

Functor f => (a -> f a) -> s -> f s 

грубо говоря, такая линза позволяет считывать или манипулировать что-то типа a внутри s, например, поле внутри записи (для введения к объективам см. this post). Если мы специализируемся f на Identity, мы получаем

(a -> Identity a) -> s -> Identity s 

изоморфная

(a -> a) -> s -> s 

так дано функция обновления на a, возвращает функцию обновления на s. (Для полноты: Если мы специализируемся f на Const a, мы получаем (a -> Const b a) -> s -> Const b s, которая изоморфна (a -> b) -> (s -> b), то есть, учитывая читателя на a, возвращают читателя на s.)

3

Один реальный случай использования должен быть (чистым) основанием стека трансформаторов монады, например.

type Reader r = ReaderT r Identity 
9

Одно использование его в качестве базы для монады монад трансформаторных стеков: вместо того, чтобы обеспечить два типа Some :: * ->* и SomeT :: (* -> *) -> * -> *, достаточно, чтобы обеспечить только путем установки последней type Some = SomeT Identity.

Другие, несколько напоминает случай использования (но полностью оторван от всей монады бизнеса), когда вам нужно сослаться на кортежи: мы можем сказать, () является нульарной кортеж, (a, b) двоичным кортеж, (a, b, c) является тройной кортеж, и так далее, но что это оставляет для унарного дела? Высказывание a является унарным кортежем для любого выбора a часто не является удовлетворительным, например, когда мы создаем некоторые экземпляры типа typeclass, такие как Data.Tuple.Select, для создания однозначного ключа необходим некоторый конструктор типов. Таким образом, путем добавления, например, Sel1 экземпляров до Identity a, это заставляет нас различать (a, b) (двухкортеж, содержащий a и b) и Identity (a, b) (один кортеж, содержащий одно значение (a, b)).

(Обратите внимание, что Data.Tuple.Select определяет свой собственный тип, называемый OneTuple вместо использования Identity, но она изоморфна Identity -в самом деле, это просто переименовать прочь и я думаю, что существует только, чтобы избежать не- base зависимости.)

+1

«Идентичность» все равно соединяется с базой в 4.8, так что скоро может стать историческим артефактом. «Data.Sequence» использует внутренний тип «Elem» (также изоморфный «Identity»). – dfeuer

11

Иногда я работаю с записями, поля которых являются необязательными в некоторых контекстах (например, при анализе записи из JSON), но обязательно в других.

Я решаю это путем параметризации записи с помощью функтора и используя Maybe или Identity в каждом случае.

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE StandaloneDeriving #-} 

data Query f = Query 
    { 
     _viewName :: String 
    , _target :: f Server -- Server is some type, it doesn't matter which 
    } 
    deriving (Generic) 

поле сервер не является обязательным при обработке JSON:

instance FromJSON (Query Maybe) 

Но тогда у меня есть функция, как

withDefaultServer :: Server -> Query Maybe -> Query Identity 
withDefaultServer = undefined 

, который возвращает записи, в которой _target поле является обязательным.

(Этот ответ ничего одноместный о Identity не использует, хотя.)