我对如何正确使用回旋镖生成URL有点困惑。我有以下几点:
data State =
AK | AL | AR | AZ | CA ... WY
data Sitemap
= Home
| State State
| Place State String
deriving (Eq, Ord, Read, Show, Data, Typeable)
$(derivePrinterParsers ''Sitemap)
sitemap ∷ Router Sitemap
sitemap =
( rHome
<> rState . state
<> rPlace . (state </> anyString)
)
state :: PrinterParser StringsError [String] o (State :- o)
state = xmaph read (Just . show) anyString
这似乎有效,但是当我将我的state
实现与articleId
文档中的实现进行比较时,它们似乎以相反的方式工作:
articleId :: Router ArticleId
articleId = xmaph ArticleId (Just . unArticleId) int
这些类型完全不同,看起来它们的方向相反,但我的sitemap
有效,应用程序可以正确处理 URL。我认为它应该看起来更像这样:
maybeState :: String → Maybe State
maybeState stateString = case reads stateString of
[(state, "")] -> Just state
_ -> Nothing
stateR :: Router State
stateR = xpure show maybeState
这不会进行类型检查,但即使用undefined
代替其定义,在上面的sitemap
中,rState . stateR
会起作用,但rPlace . (stateR </> anyString)
不起作用。
似乎这种情况经常出现,可能有一个库函数可以为我解决这个问题,但我没有看到。
编辑:这是我得到的一些类型错误:
对于state = xpure show maybeState
:
Main.hs:56:16:
Couldn't match expected type `State :- ()'
with actual type `[Char]'
Expected type: () -> State :- ()
Actual type: () -> String
In the first argument of `xpure', namely `show'
In the expression: xpure show maybeState
对于state = undefined :: Router State
(此错误在sitemap
定义中(:
Main.hs:45:18:
Couldn't match expected type `String :- ()' with actual type `()'
Expected type: PrinterParser
StringsError [String] () (State :- (String :- ()))
Actual type: Router State
In the first argument of `(</>)', namely `state'
In the second argument of `(.)', namely `(state </> anyString)'
这些类型看起来不同,因为在rPlace
行中使用状态需要比Router
类型别名允许的更通用的类型签名。(你的代码很好。但也许我们应该在回旋镖中提供一个更通用的别名。
如果删除 rPlace 行,则可以将状态的类型签名更改为:
state :: Router State
state = xmaph read (Just . show) anyString
如果你更仔细地观察,我想你会发现state
和articleId
实际上确实朝着同一个方向发展。
articleId :: Router ArticleId
articleId = xmaph ArticleId (Just . unArticleId) int
xmaph
的第三个参数指定如何解析某些基础值。在articleId
的情况下,它解析int
,对于state
,它解析anyString
。
xmaph
的第一个参数指定如何将该值转换为所需的返回类型。在articleId
中,我们简单地应用 ArticleId
构造函数。在state
中,我们应用read
函数。但在这两种情况下,我们都是从基础值转到所需的返回类型:
ArticleId :: Int -> ArticleId
read :: String -> State
要xmaph
的第二个参数指定如何将返回类型转换回基础值。
show :: State -> String
unArticleId :: ArticleId -> Int
也就是说,无论如何,我们实际上不应该在这里使用"read",因为"read"可能会失败并通过错误。xmaph 的第一个参数将是一个全函数。
我上传了回旋镖 1.3.1,它为名为 readshow
的Strings
模块添加了一个新的运算器。此函数正确使用读取和显示实例。不幸的是,错误报告有点草率,因为当reads
失败时,它不会告诉我们它失败的原因或位置。但总比没有好:)
使用它,您现在可以编写:
state :: PrinterParser StringsError [String] o (State :- o)
state = readshow
如果我们提供无效状态,我们现在得到:
> parseStrings sitemap ["AZ"]
Right (State AZ)
> parseStrings sitemap ["FOEU"]
Left parse error at (0, 0): unexpected FOEU; decoding using 'read' failed.