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 (т.е. Сбой), когда они этого не делают.