#haskell
#haskell
Вопрос:
В Data.Typeable
нем есть приведение функции
cast :: forall a b. (Typeable a, Typeable b) => a -> Maybe b
и функция eqT
eqT :: forall a b. (Typeable a, Typeable b) => Maybe (a :~: b)
Они кажутся почти идентичными по эффекту и реализации, мне было интересно, есть ли какой-либо практический смысл в описании eqT: извлеките свидетельство равенства двух типов.
Комментарии:
1. Вы можете легко реализовать
cast
изeqT
. Хотя вы также можете реализоватьeqT
fromcast
, это не так очевидно, IMO (сначала я думал, что это невозможно! Я рекомендую попробовать это в качестве упражнения.). Это потомуeqT
, что позволяетcase eqT @a @b of Just Refl -> ...
, который сообщает компилятору предположитьa~b
в...
, и это предположение автоматически обрабатывается компилятором по мере необходимости, что является очень мощным. Для сравнения,cast
только позволяет конвертироватьa
в (возможно)b
, и это само по себе не дает указания проверке типов что-либо предполагать.
Ответ №1:
Это неудобно:
checkEq :: forall a b. (Typeable a, Typeable b, Eq b) => a -> a -> Maybe Bool
checkEq a a' = do
b <- cast @_ @b a
b' <- cast a'
pure (b == b')
Внутренне это дважды проверяет приведение и выполняет два Maybe
сопоставления с шаблоном. Но как только мы выполним одно приведение, мы знаем, что другое будет успешным!
Это лучше:
checkEq a a' = (Refl -> a == a') <
gt; eqT @a @b
Только одна проверка, только одноMaybe
совпадение с шаблоном.Такого рода вещи часто возникают, когда у вас есть
Typeable
вещи, определяемые количественно в структурах данных, и вы хотите выполнять над ними операции, которые принимают несколько аргументов. Приведение равенства типов в область видимости один раз (сRefl
помощью шаблона), а затем возможность использовать это равенство несколько раз, является как более удобным, так и более эффективным, чем повторныйcast
ввод.Редактировать
чи указывает, что в данном случае на самом деле можно сделать только одно
cast
:checkEq a a' = (eq -> eq a a') <
gt; cast ((==) @b)
checkEq a a' = uncurry ((==) @b) <gt; cast (a, a')
В общем случае вы можете создать кортеж из всех операций, которые вам нужно выполнить, или всех значений, которые вам нужно выполнить. Я думаю, что на самом деле это не оставляет мне веских технических аргументов ни в пользу того, ни в пользу другого!
Комментарии:
1. Я на 100% согласен. Кажется, есть способ использовать только один
cast
, но это еще более неудобно: определитьfoo :: b -> b -> Maybe Bool
, а затемcase cast foo :: Maybe (a->b->Bool) of ...
, который работает, потому что, когдаa~b
у нас есть(b->b->Bool)~(a->b->Bool)
.eqT
намного проще в использовании и гораздо удобнее для чтения.2. @chi О, здорово! Я включу некоторые комментарии по этому поводу.