为什么Identity monad有用?

pio*_*rek 12 monads haskell functional-programming

我经常看到这个

看来身份monad是无用的.它不是......但这是另一个话题.

所以任何人都可以告诉我它是如何有用的?

Pet*_*lák 17

Identity对于monad,仿函数和applicative仿函数,0表示数字.它本身似乎毫无用处,但是在人们期望monad或者(应用)仿函数实际上什么也不做的地方经常需要它.

如前所述,Identity允许我们定义monad变换器,然后定义它们相应的monad SomeT Identity.

但那还不是全部.通过monad来定义其他概念通常也很方便,这通常会增加很多灵活性.例如Conduit i m o(也参见本教程)在管道中定义了一个元素,它可以请求类型的数据i,可以生成类型的数据o,并使用monad m进行内部处理.然后可以使用给定的monad运行这样的管道

($$) :: Monad m => Source m a -> Sink a m b -> m b
Run Code Online (Sandbox Code Playgroud)

(其中,Source是一个别名Conduit与没有输入和Sink用于Conduit与无输出).当需要在管道没有effectful计算,只是纯粹的代码,我们只专注mIdentity和运行这样的管道如

runIdentity (source $$ sink)
Run Code Online (Sandbox Code Playgroud)

Identity也是"空"的仿函数和应用仿函数:Identity由另一个仿函数或应用仿函数组成,与原始函数同构.例如,Lens'在以下定义为函数多态Functor:

Functor f => (a -> f a) -> s -> f s
Run Code Online (Sandbox Code Playgroud)

粗略地说,这样的镜头允许读取或操纵a内部类型的东西s,例如记录内的字段(对于镜头的介绍,参见这篇文章).如果我们专注fIdentity我们得到

(a -> Identity a) -> s -> Identity s
Run Code Online (Sandbox Code Playgroud)

这是同构的

(a -> a) -> s -> s
Run Code Online (Sandbox Code Playgroud)

所以给定更新功能a,返回更新功能s.(为了完整性:如果我们专注fConst a,我们得到(a -> Const b a) -> s -> Const b s,这是同构的(a -> b) -> (s -> b),也就是说,给读者a一个回馈读者s.)


dan*_*iaz 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)
Run Code Online (Sandbox Code Playgroud)

解析JSON时,服务器字段是可选的:

instance FromJSON (Query Maybe)
Run Code Online (Sandbox Code Playgroud)

但后来我有一个像这样的功能

withDefaultServer :: Server -> Query Maybe -> Query Identity 
withDefaultServer = undefined
Run Code Online (Sandbox Code Playgroud)

返回该_target字段为必填字段的记录.

(但这个答案并没有使用任何monadic的东西Identity.)


Cac*_*tus 9

一种用途是作为基础单子为单子变压器堆栈:代替具有以提供两种类型的Some :: * ->*SomeT :: (* -> *) -> * -> *,它足以提供只是后者通过设置type Some = SomeT Identity.

另一个有点类似的用例(但完全脱离整个monad业务)就是当你需要引用元组时:我们可以说()是一个nullary元组,(a, b)是一个二元元组,(a, b, c)是一个三元组,等等,但是什么呢留给一元的情况?Saying a是一个单元组,因为任何选择a通常都不令人满意,例如当我们构建一些类型类实例时 Data.Tuple.Select,需要一些类型构造函数来充当明确的键.因此,通过添加例如Sel1实例Identity a,它迫使我们区分(a, b)(包含a a和a 的两元组b)和Identity (a, b)(包含单个(a, b)值的一元组).

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