Za pomocą ViewPatterns
i Data.Typeable
, udało mi się napisać funkcję, która pozwala mi napisać coś przypominającego analizę przypadku dla typów. Przestrzegać:Jak mogę zapisać ten wzór synonimu bez niejednoznacznych błędów typu?
{-# 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
Powyższy evilId
funkcja jest bardzo zła, rzeczywiście, ponieważ używa Typeable
całkowicie obalić parametricity:
ghci> evilId True
True
ghci> evilId "hello"
"olleh"
Ponieważ uwielbiam być zła, jestem bardzo zadowolony z tego, ale powyższa składnia jest bardzo głośna. Chciałbym móc napisać ten sam kod jaśniej, więc postanowiłem napisać synonim Wzór:
pattern EqT :: forall b a. (Typeable a, Typeable b) => (a ~ b) => b -> a
pattern EqT x <- (viewEqT @b -> Just (Refl, x))
Pomyślałem, że będę mógł korzystać z tego wzoru synonim aby mój zły analiza case znacznie łatwiejsze Aby przeczytać:
evilId :: Typeable a => a -> a
evilId (EqT (n :: Int)) = n + 1
evilId (EqT (str :: String)) = reverse str
evilId x = x
Niestety, to nie działa w ogóle. GHC wydaje się nie sprawdzać adnotacji typu przed sprawdzeniem typu, więc uważa, że b
jest niejednoznaczną zmienną w każdym wzorze. Czy istnieje sposób, w jaki mogę w prosty sposób owijać te wzory synonimem wzoru, czy też utknę w moich dłuższych wzorcach oglądania?
Czy to działa, jeśli zmienisz 'viewEqT' na" Proxy "? –
@BenjaminHodgson Nie mogłem wymyślić sposób, aby uzyskać synonim wzorca, aby przyjąć wyrażenie jako "argument" do użycia we wzorcu widoku zamiast wykonywania wzorców, więc 'Proxy' nie pomagało. –
Wygląda na to, że synonimy wzorców dopuszczają tylko informacje, aby je "wyrzucić". To znaczy. nie możemy zdefiniować 'f (Plus1 5) = ...' gdzie 'Plus1 n = ((= = n + 1) -> True)'. Tutaj 'n' musi być wzorem" out ", a nie wartością wejściową. IIRC w Scali i obiektach ekstraktora jest to możliwe (aczkolwiek w Haskell mamy wzory widoków, które mogą zrobić to samo z bardziej złożoną składnią). Byłoby również miło wpisać-zastosować w wzorach takich jak 'EqT @ Int n', ale' @ 'we wzorach już oznacza coś innego, i znowu będzie to argument typu" w ". – chi