关于Yesod Widget相关类型的混淆

时间:2016-01-05 10:39:15

标签: haskell yesod hamlet

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

将一个hamlet片段传递给defaultLayout函数的usual way是通过toWidget函数。因此,遵循下面的ham1和ham2的定义,defaultLayout中的正确指令分别是toWidget ham1ham2

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

(*)此外,如果有人可以告诉我是否有更好的"那么它会很棒。为ham1键入同义词而不是t -> Text.Blaze.Internal.Markup

代码:

{-# 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

输出:

enter image description here

1 个答案:

答案 0 :(得分:2)

我不完全确定被问到了什么,所以我只是解释一下它是如何工作的, 我希望它具有相关性。

defaultLayout将小部件放入井中...默认布局。因此你的 do - 块单独组合小部件。传递类型检查器,每行都在 do - 阻止类型应为Widget a(或WidgetT App IO a,是您的 网站的数据类型为App)。 ToWidget的实例是可以的 转换为小部件。 Widget也是此类型类的实例 作为render -> Html事。鉴于Html是类型的同义词 Text.Blaze.Internal.Markup,您的ham1是完美的候选人 传递给toWidget,请参阅源代码:

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

(~)添加了额外的约束,它告诉类型检查器(在此实例之后) 它已被选中)render必须具有RY site类型。 site幻像类型,它确保系统的不同部分 属于同一网站,如果您正在使用脚手架,通常会AppRY site使用另一个类型同义词RY传递它site变量,结果 在这样的类型:

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

这是ham1参数的类型。

因此,这些实例允许您将不同的东西转换为小部件, 包括由hamlet准引号产生的函数,没有魔法 这里。