Чтобы сначала ответить на главный вопрос, вы можете разобрать строки байтов известной длины с помощью getByteString
(или getLazyByteString
). Таким образом, двоичный парсер для Request
может быть:
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> getByteString 14
<*> getByteString 12
Если у вас также есть сериализаторы, скажем, putRequest
, вы можете поместить его в экземпляр Binary
с анализатором, что позволит вам использовать некоторые дополнительные функции библиотеки для удобства (но вам не нужно) .
instance Binary Request where
get = parseRequest
put = putRequest
Чтобы не перепутать пароль и идентификатор, неплохо было бы обернуть их в новые типы:
newtype UniqueID = MkUniqueID ByteString -- length 14
newtype Password = MkPassword ByteString -- length 12
При реализации операций над ними убедитесь, что они не создают значения неправильной длины. Затем вы можете скрыть конструкторы при экспорте типов, чтобы пользователи не могли сломать эти инварианты.
В синтаксических анализаторах для этих типов вы указываете желаемую длину:
parseUniqueID :: Get UniqueID
parseUniqueID = MkUniqueID <$> getByteString 14
parsePassword :: Get Password
parsePassword = MkPassword <$> getByteString 12
Теперь это делает определение Request
более описательным, единственный способ смешать пароль и идентификатор в коде на Haskell - это ошибиться при сериализации / десериализации, что снижает вероятность ошибок в других местах.
data Request = Request
{ header :: Header
, uniqueID :: UniqueID
, password :: Password
}
parseRequest :: Get Request
parseRequest =
Request
<$> parseHeader
<*> parseUniqueID
<*> parsePassword