"((->) a)"的意思是什么?

8

我以前见过这种类型但不知道它的意思。它代表了什么意思,或者有一个名字吗?

Prelude> :m Data.Functor
Prelude Data.Functor> :t flip . (flip (<$>))
flip . (flip (<$>))
:: Functor ((->) b) => (b -> a) -> b -> (a -> c) -> c

6
这是普通函数箭头的前缀表达形式。也就是说,(->) a ba -> b是相同的。 - Vitus
2
如果你喜欢使用(3+)而不是((+) 3),那么你可以将((->) a)看作(a ->) - 尽管这不是有效的语法,但它可以让你了解它的含义。 - AndrewC
3个回答

9

实际上,((->) a) 不是一种类型,而是一个部分应用的类型构造器

就像函数一样,在Haskell中可以部分应用类型构造器。

您可以在GHCi中检查某些东西的种类

ghci> :k (->)
(->) :: * -> * -> *

ghci> :k (->) Int
(->) Int :: * -> *

所有值都有种类类型*;类型构造器具有像* -> ** -> * -> *等类型。

6
一丝不苟模式:所有的值都具有种类为 * 的类型。 - Daniel Wagner

5
在Haskell中,(->) a 来自 a -> something。请记住,您可以通过使用括号将运算符转换为前缀函数。例如 (+) 1 1 == 2。因此,在这种情况下,“运算符”是类型构造器 ->。它以这种方式使用,因为函子需要一个具有1个变量的类型构造器,但是->有2个变量。所以我们部分应用它。
也许你会发现,对于((->) a)<$> 就是 (.)

我明白了,所以 ((->) a) 的类型是 *->* - Carlos López-Camey
3
更明确地说,我会补充说明 (->) 的种类为 * -> * -> *,而 (->) a 的种类为 * -> * - Jon Purdy
@Carlos 不,((->) a) 不是一种类型!但是如果你把/type/换成/kind/,那就正确了。 - Matt Fenwick

5

除了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

请注意,((->) rReader r 单子是完全相同的。如果你理解了上面的代码,你就基本上理解了 Reader
编辑:我应该澄清,时间相关的值只是函数/Reader单子的一个用途。还有其他用途;Reader单子的经典示例用例是通过计算线程配置值。正如上面的示例所示,它比那更有用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接