如果您尝试稍微忽略详细类型并查看全局,这会有所帮助。
将<$>
其视为在不同级别上工作的<*>
特殊版本。$
一般的应用函子
让我们先看一下一般情况 - 假设我有一个应用函子AF
和对象
x :: AF a
y :: AF b
z :: AF c
有点意思他们“做某事”然后返回类型的值a
,b
和c
,还有一个纯函数
f :: a -> b -> c -> d
我想用来将这些值组合在一起以获得d
. 然后
f <$> x <*> y <*> z :: AF d
通过“做” x
,然后y
,z
然后应用于f
结果来工作。
请注意,这类似于f $ a $ b $ c
.
如果您的应用函子与 monad 实例一致,这f <$> x <*> y <*> z
是一种很好的写作方式
do
a <- x
b <- y
c <- z
return (f a b c)
作为应用函子的形式
将表单视为产生数据(来自用户)的东西。areq
并且aopt
都返回一个AForm sub master ??
. 您可以忽略sub
and master
- 它们用于使用类型系统跟踪站点/子站点。??
是返回的数据类型。
所以
areq textField "Name" Nothing
是一个产生的小表格Text
,
areq (jqueryDayField def) "Birthday" Nothing
一个产生a的小表格,Day
另外
三个也产生Text
。
现在我们有
data Person = Person Text Day Text Text Text
所以这Person
是一个函数:: Text -> Day -> Text -> Text -> Text -> Person
,所以如果你有
Person
<$> areq textField "Name" Nothing
<*> areq (jqueryDayField def
{ jdsChangeYear = True -- give a year dropdown
, jdsYearRange = "1900:-5" -- 1900 till five years ago
}) "Birthday" Nothing
<*> aopt textField "Favorite color" Nothing
<*> areq emailField "Email address" Nothing
<*> aopt urlField "Website" Nothing
Person
通过获取字段的所有单独值并将纯Person
函数应用于它们,您已将所有这些表单组合成一个生成单个值的表单。
要以单子风格写,你会写
do
name <- areq textField "Name" Nothing
day <- areq (jqueryDayField def
{ jdsChangeYear = True -- give a year dropdown
, jdsYearRange = "1900:-5" -- 1900 till five years ago
}) "Birthday" Nothing
color <- aopt textField "Favorite color" Nothing
email <- areq emailField "Email address" Nothing
website <- aopt urlField "Website" Nothing
return $ Person name day color email website
我非常喜欢 applicative functor 风格,因为它感觉就像将函数应用于某些数据,而不是执行一系列指令。
那么<$>
vs有什么关系<*>
呢?
你可能已经注意到我总是这样做
pureFunction <$> af1 <*> af2 <*> af3 <*> af4 ....
首先<$>
,<*>
其余的。那是因为第一件事pureFunction
是纯粹的,并且不适用于应用函子值。它举起它。(所有应用函子都是函子。)让我们比较一下类型:
<$> :: Functor f => (a -> b) -> f a -> f b
<*> :: Applicative f => f (a -> b) -> f a -> f b
这意味着这<$>
是为了提升一个纯函数,但是当您使用<*>
左侧时,必须已经在生成函数而不仅仅是数据,这最初看起来很奇怪,但如果您<$>
先使用则不会。例如,如果你部分应用(++)
到"Hello"
你得到一个函数:: String -> String
,那么
getLine :: IO String -- produces a String
(++) <$> getLine :: IO (String -> String) -- produces an appender
(++) <$> getLine <*> getLine :: IO String -- produces a combined String
将其与
areq textField "Name" Nothing
:: AForm sub master Text
因为Person :: Text -> Day -> Text -> Text -> Text -> Person
,如果我们给它一个值,我们得到一个类型name::Text
的部分应用函数Person name
Day -> Text -> Text -> Text -> Person
Person <$> areq textField "Name" Nothing
:: AForm sub master (Day -> Text -> Text -> Text -> Person)
我们可以将 using<*>
与产生 a 的东西结合起来Day
,得到产生 a 的东西,(Text -> Text -> Text -> Person)
依此类推,直到我们得到产生 a 的东西Person
。(这一切都有效,因为->
关联到右侧和<$>
关联<*>
到左侧,就像这样$
做一样。)