Веб-маршруты-бумеранг с простым алгебраическим типом данных
Я немного запутался в том, как правильно использовать бумеранг для генерации 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)'
1 ответ
Типы выглядят по-разному, потому что вы используете состояние в 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
Тем не менее, в любом случае мы не должны использовать здесь "чтение", потому что "чтение" может привести к сбою и из-за ошибки. Предполагается, что первым аргументом xmaph будет полная функция.
Я загрузил бумеранг 1.3.1, который добавляет новый комбинатор к Strings
модуль с именем readshow
, Эта функция правильно использует экземпляры Read и Show. К сожалению, сообщения об ошибках немного небрежно, так как, когда 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.