我以前见过这种类型,但不知道它是什么意思。它是否意味着什么和/或它有名字吗?
Prelude> :m Data.Functor
Prelude Data.Functor> :t flip . (flip (<$>))
flip . (flip (<$>))
:: Functor ((->) b) => (b -> a) -> b -> (a -> c) -> c
我以前见过这种类型,但不知道它是什么意思。它是否意味着什么和/或它有名字吗?
Prelude> :m Data.Functor
Prelude Data.Functor> :t flip . (flip (<$>))
flip . (flip (<$>))
:: Functor ((->) b) => (b -> a) -> b -> (a -> c) -> c
实际上,((->) a)
不是类型,而是部分应用的类型构造函数。
就像函数一样,类型构造函数可以部分应用在 Haskell 中。
您可以检查GHCi 中的内容:
ghci> :k (->)
(->) :: * -> * -> *
ghci> :k (->) Int
(->) Int :: * -> *
所有值都有kind 类型 *
;类型构造函数有* -> *
,* -> * -> *
等类型。
在 Haskell 中,(->) a
来自a -> something
. 请记住,您可以使用 parans 将运算符转换为前缀函数。即(+) 1 1 == 2
。所以在这种情况下,“操作员”是类型构造函数->
。之所以这样使用,是因为仿函数需要一个类型构造函数,它有 1 个变量,但->
有 2 个变量。所以我们部分应用它。
它可能会帮助您意识到<$>
for((->) a)
只是(.)
为了添加到 Matt Fenwick 和 josefg 的更多技术性答案,((->) a
可以将 阅读为类型构造函数,该构造函数形成依赖于a
. 一个例子:假设您的团队成员随时间而变化。一种可能的表示方式是这样的:
-- | The type of a's that depend on a point in time, using t as the timeline type.
type Historical t a = t -> a
observe :: Historical t a -> t -> a
observe = ($)
-- | The membership of a team is a list of persons whose composition can change
-- from day to day.
membership :: Team -> Historical Day [Person]
membership = ...
((->) a)
是Functor
,Applicative
和Monad
, 这意味着类型类操作是可用的, 当与 一起使用时它们有非常有用的解释Historical t
。
第一个示例:fmap
将函数应用于与时间相关的值。例如,
-- The number of persons in a Team, depending on the Day:
membershipCount :: Team -> Historical Day Int
membershipCount = fmap length . membership
Applicative<*>
操作为您提供同时性:
-- The joint membership of team1 and team2, depending on the Day:
(++) <$> membership team1 <*> membership team2 :: Historical Day [Person]
虽然实际上,因为我们有instance Monoid [a]
和instance Monoid m => Monoid t -> m
,所以前面的例子可以写成这样:
import Data.Monoid
membership team1 ++ membership team2
Monad 给你组合:
personToManager :: Person -> Historical Day Manager
personToManager = ...
managerToVP :: Manager -> Historical Day VP
managerToVP = ...
personToVP :: Person -> Historical Day VP
personToVP p = personToManager p >>= managerToVP
另请注意,这与monad((->) r
完全相同。Reader r
如果你理解了上面的代码,你就很明白了Reader
。
编辑:我应该澄清,与时间相关的值只是函数/Reader monad 的一种用途。还有其他用途;Reader monad 的经典示例用例是通过计算线程化配置值。如上例所示,它的用途远不止这些。