Yesod适用表格

Mat*_*w H 5 haskell yesod

我一直在玩Yesod,并且出现了一个问题:如何使用表格作为Applicatives?

采取:

    personForm :: Html -> MForm Synopsis Synopsis (FormResult Person, Widget)
personForm = renderDivs $ 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
Run Code Online (Sandbox Code Playgroud)

我不明白<*>运算符是如何f (a -> b)应用于AForm值的,因为我理解它需要某种类型(即包含二进制函数的仿函数)作为其第一个参数:

(AForm f) <*> (AForm g) = AForm $ \mr env ints -> do ...
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

https://github.com/yesodweb/yesod/blob/bf293e6a1f6e691281520d254f72b9441cc64704/yesod-form/Yesod/Form/Types.hs#L95

And*_*ewC 13

如果您尝试忽略详细类型并查看大图,它会有所帮助.

在不同的层面上考虑<$><*>作为$该工作的特殊版本.

一般的应用函子

让我们首先看一下 - 假设我有一个应用函子AF和对象

x :: AF a
y :: AF b
z :: AF c
Run Code Online (Sandbox Code Playgroud)

有点意思,他们"做一些事情",那么返回的值类型a,b并且c,也是一个纯函数

f :: a -> b -> c -> d
Run Code Online (Sandbox Code Playgroud)

我想用它来将这些值组合在一起得到一个d.然后

f <$> x <*> y <*> z    :: AF d
Run Code Online (Sandbox Code Playgroud)

在"做"的作品x,然后yz和应用f的结果.

请注意,这与...类似f $ a $ b $ c.

如果你的applicative functor同意monad实例,那么这f <$> x <*> y <*> z是一种很好的写作方式

do
  a <- x
  b <- y
  c <- z
  return (f a b c)
Run Code Online (Sandbox Code Playgroud)

形成适用的仿函数

将表单视为产生数据的东西(来自用户).areq并且aopt都返回了AForm sub master ??.你可以忽略submaster-它们是使用类型系统保持网站/子网站的轨道.这??是返回的数据类型.

所以,
areq textField "Name" Nothing是产生一个小的形式Text,
areq (jqueryDayField def) "Birthday" Nothing是一个小的形式产生Day,
其他三个也生产Text.

现在我们有

data Person = Person Text Day Text Text Text
Run Code Online (Sandbox Code Playgroud)

所以这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
Run Code Online (Sandbox Code Playgroud)

Person通过获取字段的所有单个值并将纯Person函数应用于它们,您将所有这些表单组合成一个生成单个值的表单.

要用monadic风格写下你要写的

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
Run Code Online (Sandbox Code Playgroud)

我非常喜欢应用程序仿函数样式,因为它感觉就像将函数应用于某些数据而不是执行一系列指令.

那么对付的<$><*>什么?

你可能已经注意到我一直这样做

pureFunction <$> af1 <*> af2 <*> af3 <*> af4 ....
Run Code Online (Sandbox Code Playgroud)

<$>第一,而<*>其余部分.这是因为第一件事pureFunction是纯粹的,并不适用于应用函子值.它提升了它.(所有应用仿函数都是仿函数.)让我们比较一下类型:

<$> :: Functor f     =>    (a -> b) -> f a -> f b
<*> :: Applicative f =>  f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

这意味着它<$>是用于提升纯函数,但是当你使用<*>左手边时必须已经生成函数而不仅仅是数据,这最初看起来很奇怪,但如果<$>先使用则不行.例如,如果部分适用(++)"Hello"你得到一个功能:: String -> String,所以

         getLine             :: IO String             -- produces a String
(++) <$> getLine             :: IO (String -> String) -- produces an appender
(++) <$> getLine <*> getLine ::            IO String  -- produces a combined String
Run Code Online (Sandbox Code Playgroud)

与之相比

areq textField "Name" Nothing
   :: AForm sub master Text
Run Code Online (Sandbox Code Playgroud)

因为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)
Run Code Online (Sandbox Code Playgroud)

我们可以结合使用<*>产生a的东西来产生产生a的Day东西,(Text -> Text -> Text -> Person)依此类推,直到我们得到产生a的东西Person.(这一切都有效,因为->与右边相关,<$><*>与左边相关,就像$.)