Как я могу написать этот синоним шаблона без неоднозначных ошибок типа?
С помощью 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
К сожалению, это не помогает с неоднозначностями, связанными с синонимами вашего паттерна.