0

我刚开始玩 Yesod,虽然我已经可以得到一些有趣的结果,但我很难清楚地理解它的一些类型(可能是因为我不熟悉 Template Haskell)。

将hamlet 片段传递给 defaultLayout 函数的常用方法是通过 toWidget 函数。因此,按照下面 ham1 和 ham2 的定义,defaultLayout 中的正确指令分别是toWidget ham1ham2

我的问题:在以下(工作)代码中,为什么指令toWidget ham2会编译,而它与 ham1 是完全不同的动物?我猜这意味着 ham1 的类型 ( ham1 :: t -> Text.Blaze.Internal.Markup (*) ) 和 ham2 的类型 ( ham2 :: Widget) 都是 ToWidget 类的实例,但在阅读此类的实例定义时这对我来说并不完全明显。

(*)另外,如果有人能告诉我是否有比t -> Text.Blaze.Internal.Markupham1 更好的类型同义词,那就太好了。

代码:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

import Yesod
import Text.Blaze.Internal (Markup)

data App = App
instance Yesod App

mkYesod "App" [parseRoutes|
/ HomeR GET
|]

getHomeR :: Handler Html
getHomeR = defaultLayout $ do
    setTitle "Some Title"

    toWidget ham1   -- usual way to produce a Widget from hamlet snippet
    toWidget ham2   -- ??
    ham2            -- already of type Widget


ham1 :: t -> Text.Blaze.Internal.Markup -- explicit signature for reference only
ham1 = [hamlet|
<h1> 1) Hello
<h2> World!
 |]

ham2 :: Widget
ham2 = 
    toWidget [hamlet|
<h1> 2) Hello
<h2> World!
 |]

main :: IO ()
main = do
    warp 3000 App

输出 :

在此处输入图像描述

4

1 回答 1

2

我不完全确定要问什么,所以我将解释它是如何工作的,我希望它会是相关的。

defaultLayout需要一个小部件来很好地放置它……默认布局。因此,您的 do-block 将小部件组合在一起。要通过类型检查器,该块中的每一行都do应该是类型Widget a(或者WidgetT App IO a,您网站的数据类型是App)。的实例ToWidget是可以转换为小部件的事物。Widgets 是这个类型类的实例,以及render -> Html事物。鉴于这Html是 的类型同义词 Text.Blaze.Internal.Markup,您ham1是传递给的完美候选者toWidget,请参见源代码:

instance render ~ RY site => ToWidget site (render -> Html) where
    toWidget x = …

(~)施加额外的约束,它告诉类型检查器(在它已经选择了这个实例之后)顺便说一句render必须有 type RY sitesite一种幻像类型,它确保系统的不同部分属于同一个站点,通常是App在您使用脚手架时。 RY site使用另一个类型同义词RY传递它的site变量,它会产生这样的类型:

Route App -> [(Text, Text)] -> Text

这是参数的类型ham1

hamlet因此,这些实例允许您将不同的东西转换为小部件,包括类似quasi-quoter产生的功能,这里没有魔法。

于 2016-01-05T11:24:38.377 回答