2017-01-19 30 views
5

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?

+0

Czy to działa, jeśli zmienisz 'viewEqT' na" Proxy "? –

+0

@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. –

+0

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

Odpowiedz

1

Jeśli celem jest, aby znaleźć czyste składni, aby zaimplementować funkcję evilId, można napisać to tak:

{-# 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 

to nie pomaga z niejasności otaczających swoje synonimy wzór, niestety.