Как я могу написать этот синоним шаблона без неоднозначных ошибок типа?

С помощью ViewPatterns а также Data.TypeableМне удалось написать функцию, которая позволяет мне написать что-то похожее на анализ случаев на типах. Заметим:

{-# LANGUAGE GADTs, PatternSynonyms, RankNTypes, ScopedTypeVariables
           , TypeApplications, TypeOperators, ViewPatterns #-}

import Data.Typeable

viewEqT :: forall b a. (Typeable a, Typeable b) => a -> Maybe ((a :~: b), b)
viewEqT x = case eqT @a @b of
  Just Refl -> Just (Refl, x)
  Nothing -> Nothing

evilId :: Typeable a => a -> a
evilId (viewEqT @Int -> Just (Refl, n)) = n + 1
evilId (viewEqT @String -> Just (Refl, str)) = reverse str
evilId x = x

Выше evilId функция очень злая, так как она использует Typeable чтобы полностью подорвать параметричность:

ghci> evilId True
True
ghci> evilId "hello"
"olleh"

Так как я люблю быть злым, я очень доволен этим, но приведенный выше синтаксис очень шумный. Мне бы хотелось иметь возможность писать этот код более четко, поэтому я решил написать синоним шаблона:

pattern EqT :: forall b a. (Typeable a, Typeable b) => (a ~ b) => b -> a
pattern EqT x <- (viewEqT @b -> Just (Refl, x))

Я подумал, что смогу использовать этот синоним шаблона, чтобы сделать мой анализ злых случаев намного легче для чтения:

evilId :: Typeable a => a -> a
evilId (EqT (n :: Int)) = n + 1
evilId (EqT (str :: String)) = reverse str
evilId x = x

К сожалению, это не работает вообще. GHC, похоже, не проверяет мои аннотации типов перед проверкой типов, поэтому считает, что b является неоднозначной переменной в каждом шаблоне. Есть ли способ, которым я могу аккуратно обернуть эти шаблоны синонимом шаблона, или я застряну с более длинными шаблонами просмотра?

1 ответ

Если цель состоит в том, чтобы найти какой-то чистый синтаксис для реализации вашего evilId функция, вы можете написать это так:

{-# Language ScopedTypeVariables, GADTs, TypeApplications #-}

module Demo where

import Data.Typeable

evilId :: forall a. Typeable a => a -> a
evilId x
  | Just Refl <- eqT @a @Int    = x+1
  | Just Refl <- eqT @a @String = reverse x
  | otherwise                   = x

К сожалению, это не помогает с неоднозначностями, связанными с синонимами вашего паттерна.

Другие вопросы по тегам