Haskell магический код, что здесь происходит
Я просматриваю пакет Cloud Haskell Encoding.hs и столкнулся с каким-то странным кодом, который я надеялся, что кто-то может помочь мне лучше понять, Включен необходимый код:
class (Binary a,Typeable a) => Serializable a
instance (Binary a,Typeable a) => Serializable a
data Payload = Payload
{
payloadType :: !ByteString,
payloadContent :: !ByteString
} deriving (Typeable)
serialDecodePure :: (Serializable a) => Payload -> Maybe a
serialDecodePure a = (\id ->
let pc = payloadContent a
in pc `seq`
if (decode $! payloadType a) == show (typeOf $ id undefined)
then Just (id $! decode pc)
else Nothing ) id
Мне просто интересно, что такое $! (я угадываю, просто строго оценивает), а также почему нам нужен трюк id (что-то с ленивой оценкой?). Также у меня проблемы с этой линией:
if (decode $! payloadType a) == show (typeOf $ id undefined)
Я предполагаю, что это видно, если payloadType недействителен по какой-либо причине, но если это так, не следует переключать предложения then и else, то есть change:
if (decode $! payloadType a) == show (typeOf $ id undefined)
then Just (id $! decode pc)
else Nothing
к
if (decode $! payloadType a) == show (typeOf $ id undefined)
then Nothing
else Just (id $! decode pc)
Спасибо за любую помощь, которую вы можете предоставить.
Ответы
Ответ 1
Вы правы, что $!
является строгим оценщиком. Этот тип идентичен $
, и единственная семантическая разница состоит в том, что второй аргумент seq
'd перед передачей функции.
Я думаю, что id
на самом деле там, чтобы помочь ввести вывод типа. Внутри функционального блока (\id -> ...)
функция id
вынуждена иметь тип a -> a
, где a
- это не просто переменная типа, а то же a
, что и в
serialDecodePure :: (Serializable a) => Payload -> Maybe a
Это связано с этой строкой:
Just (id $! decode pc)
так как это имеет тип Maybe a
, id
имеет выведенный тип a -> a
. Как следствие, на линии, на которую вы смотрите,
if (decode $! payloadType a) == show (typeOf $ id undefined)
id undefined :: a
, где a
снова совпадает с выходом.
Теперь мы можем перейти к проверке типов. Поскольку эта функция является полиморфной и будет декодироваться для любого типа, необходимо проверить, что кодированные данные совместимы с типом, который он декодирует. Что делать, если вы закодировали String
и пытаетесь декодировать на Int
? LHS будет декодироваться до "[Char]", который является представлением TypeRep для String. Вместо этого RHS будет "Int", тип, который он пытается декодировать. Поскольку они не равны, путь "else" - это тот, который возвращает None
.
Вместо этого ограничения типа функции id вы можете сделать то же самое с расширением ScopedTypeVariables
.
Ответ 2
Ничего себе, это какой-то странный код! Как вы догадались, ($!)
касается строгости:
f $! x = x `seq` f x
Тройка id
сложна, и все это касается ограничения типа. Вы заметите, что id
используется дважды в теле функции. Второй раз он использовался как id $! decode pc
; это фиксирует тип id
для работы с любыми выводами decode
. Первое использование - typeOf $! id undefined
; так как тип id
уже исправлен, это фиксирует тип undefined
, так что typeOf
применяется к мономорфному аргументу (и вы не получаете ошибки "неоднозначного типа" ). Подобные вещи часто выполняются с расширением ScopedTypeVariables
вместо этого обмана, но, возможно, они хотели избежать расширений, где это возможно. Что касается значения этого:
(decode $! payloadType a) == show (typeOf $ id undefined)
... мне кажется, что это проверка того, что полезная нагрузка соответствует типу вещи, возвращенной вызовом decode
в ветке then
. Кажется, имеет смысл иметь значение Just
(т.е. Успех), когда они совпадают, и значение Nothing
(т.е. Сбой), когда они этого не делают.