为什么Identity monad有用?

时间:2015-02-21 11:58:31

标签: haskell functional-programming monads

我经常读到

  

看起来身份monad是无用的。它不是......但那是另一个   主题。

所以有人能告诉我它是如何有用的吗?

4 个答案:

答案 0 :(得分:17)

Identity是monad,functors和applicative functors,0代表数字。它本身似乎毫无用处,但是在人们期望monad或(applicative)functor实际上什么都不做的地方经常需要它。

正如已经提到的,Identity允许我们定义monad变换器,然后定义它们相应的monad,就像SomeT Identity一样。

但这不是全部。通过monad来定义其他概念通常也很方便,这通常会增加很多灵活性。例如Conduit i m o(另请参阅this tutorial)定义管道中可以请求类型为i的数据的元素,可以生成类型为o的数据,并使用monad {{ 1}}用于内部处理。然后可以使用

在给定的monad中运行这样的管道
m

(其中($$) :: Monad m => Source m a -> Sink a m b -> m b Source的别名,没有输入,Conduit的{​​{1}}没有输出的别名。当管道中不需要有效的计算时,只需要纯代码,我们只需将Sink专门化为Conduit并运行这样的管道

m

Identity也是“空”仿函数和应用仿函数:runIdentity (source $$ sink) 由另一个仿函数或应用仿函数组成,与原始函数同构。例如,Lens'被定义为Identity中的函数多态:

Identity

粗略地说,这样的镜头允许在Functor内读取或操纵Functor f => (a -> f a) -> s -> f s 类型的某些内容,例如记录内的字段(有关镜头的介绍,请参阅this post)。如果我们将a专门设为s,我们就会

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)上退回读者。)

答案 1 :(得分:11)

有时我使用的字段在某些上下文中是可选的记录(比如从JSON解析记录时),但在其他情况下是必需的。

我通过使用仿函数参数化记录并在每种情况下使用MaybeIdentity来解决这个问题。

{-# 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的monadic。)

答案 2 :(得分:9)

它的一个用途是作为monad变换器堆栈的基础monad:不必提供两种类型Some :: * ->*SomeT :: (* -> *) -> * -> *,只需通过设置{{1}来提供后者即可。 }。

另一个有点类似的用例(但完全脱离整个monad业务)是当你需要引用元组时:我们可以说type Some = SomeT Identity是一个nullary元组,()是一个二元组元组,(a, b)是一个三元组,依此类推,但这对于一元情况有什么影响?说(a, b, c)a的任何选择的一元元组通常都不令人满意,例如当我们构建类似Data.Tuple.Select的类型类实例时,需要一些类型构造函数来表示明确的键。所以通过添加例如aSel1个实例迫使我们区分Identity a(包含(a, b)a的两元组)和{{1} (包含单个b值的一元组)。

(请注意,Identity (a, b)定义了自己的名为OneTuple的类型,而不是重用(a, b),但它与Data.Tuple.Select同构 - 实际上,它只是重命名 - 我认为它只存在以避免非Identity依赖。)

答案 3 :(得分:3)

一个真正的用例是monad变换器堆栈的(纯)基础,例如

type Reader r = ReaderT r Identity