2011-07-20 5 views
6

Chciałbym zdefiniować instancję monady z kontenerem M jako monadą iz zawartym typem a, który powinien być członkiem klasy Show. To ograniczenie (że a jest członkiem Show) powinno być zapewnione przez system typów.Jak zdefiniować instancję Monady "m a" z "a" w programie Typeclass Show?

Dałem mu spróbować takiego, ale M nie jest niestety z prawej Rodzaj:

data M = forall a. Show a => M a 

instance Monad M where 
return x = M x 

Wszystkie inne próby osiągnąć, prowadzonym na następujący problem: Ponieważ Monad jest klasą konstruktor, ja nie mam jawnego dostępu do typu a zawartych elementów, więc nie mogę go ograniczyć.

Czy ktoś ma rozwiązanie tego problemu bez definiowania nowej klasy Monad?

Odpowiedz

7

Cóż, to jest rzeczywiście możliwe, aby ograniczyć parametry konstruktora typu w pewnym sensie, używając GADTs:

data M a where 
    M :: (Show a) => a -> M a 

Niestety to nie faktycznie pomóc tutaj. W pewnym sensie pogarsza to sytuację, ponieważ zamiast mieć instancję Monad bez ograniczenia, nie można w ogóle napisać instancji.

Jeśli spojrzysz na powyższy podpis typu konstruktora, wyraźnie przypomina on return - co pokazuje, dlaczego to, co robisz, jest zasadniczo niemożliwe. Typ zwrotu: (Monad m) => a -> m a, a zmienne typu "niezwiązane" są domyślnie kwantyfikowane na najbardziej zewnętrznym poziomie, więc można je odczytać jako "dla wszystkich możliwych typów a i wszystkich możliwych typów m, które są instancjami Monad, z uwzględnieniem wartości typu a można skonstruować wartość typu m a ". Frazowanie "dla wszystkich" jest tam dosłowne - typ zwrotu nie polega jedynie na użyciu zmiennej typu, ale aktywnie zapewnia, że ​​każdy typ musi być dozwolony.

Krótko mówiąc, nie. Nie ma sposobu, aby zrobić to, co chcesz, ponieważ standard Monad klasa jawnie określa przeciwieństwo.

2

Nie. Nie jest to możliwe, chociaż można je łatwo wytłumaczyć. Rzucić okiem na podpis typu return:

return :: Monad m => a -> m a 

Podpis nie można zmienić, więc monada musi akceptować wszelkie typy zawartości. Ponieważ sama klasa typów nie wspomina o typie zawartości, nie ma sposobu na wymuszenie na nim ograniczeń.

Jedną z rzeczy, którą można zrobić, jest napisanie tego ograniczenia na wszystkich funkcjach, które tego wymagają i zniesienie globalnego ograniczenia. System nadal gwarantuje solidność systemu, jeśli można udowodnić istnienie ograniczenia tylko wtedy, gdy jest ono potrzebne.

+0

'Miły brak dopasowania: Pierwszy argument" Monada "powinien mieć rodzaj" * -> * ", ale" M a "ma rodzaj" * "' –

+0

@camccann Przepraszamy. Nie Rozumiem pytanie. Zobacz moją zaktualizowaną odpowiedź, która jest podobna do twojej. – fuz

+0

Czy osoba przeprowadzająca badanie mogłaby wyjaśnić swoje zdanie? – fuz

1

Jest to możliwe przy ekstremalnych oszustwach. Zobacz pakiet dla implementacji. Jednak prawdopodobnie nie jest tego warte.

Dlaczego potrzebne jest ograniczenie, aby a było w stanie? Łatwiej byłoby po prostu wyegzekwować ograniczenie Show w tym momencie, a następnie będzie ono naturalnie rozprzestrzeniać się w razie potrzeby.

+0

Pytanie zostało określone "bez definiowania nowej klasy" Monad ". Istnieją różne stopnie oszustwa, możliwe do uzyskania różne przybliżenia, ale żaden nie może być zawinięty wokół standardowego 'Monada's. Chociaż 'RMonad' prawdopodobnie jest najbliżej w tworzeniu możliwie najmniejszych ustępstw dla opakowania' AsMonad'. –

+0

@camccann: 'AsMonad' jest użyteczny ze standardową klasą' Monad'. –

+0

Tak, ale robiąc to (oczywiście) ignoruje ograniczenia z 'RMonad'; używanie samego 'AsMonad' nie daje niczego, czego nie możesz zrobić bezpośrednio za pomocą zwykłej instancji' Monad'. Jego użyteczność polega na tym, że owijanie/rozpakowywanie przechowuje dodatkowe informacje, a tym samym pozwala zachować możliwie jak najwięcej właściwości 'RMonad' podczas potykania się przez funkcje za pomocą zwykłych' Monad'ów. –

5

Być może nie jesteś w stanie zrobić dokładnie tego, o co prosisz, ale inna możliwość polega na tym, że twoja monada przedstawia akcję, która wyraźnie robi wszystko, co myślisz o robiąc z Show. Oznacza to, że przy założeniu, masz:

data M a = {- ... -} 
instance Monad M where -- notice: no Show constraint 
    {- ... -} 

Następnie można dodatkowo dostarczyć pewne działania:

report :: Show a => M a -> M a 

nie mogę myśleć poza czubek głowy dobrego wykorzystania tego wzorca Show, ale wiem o podobnym przykładzie, w którym można chcieć ograniczenia na Ord. Konfiguracja polega na tym, że chciałbyś, aby monada była niedeterministyczna (jak na przykład [a]), ale nie ma duplikatów (np. Set a). Usunięcie duplikatów wymaga kontekstu, takiego jak Eq lub Ord, ale nie możemy wymagać tego przy każdej operacji return/>>=. Zamiast więc żądamy, że użytkownik wyraźnie zaznaczyć punkty, w których duplikaty należy coalesced:

newtype Setlike a = Setlike { toList :: [a] } 
instance Monad Setlike where 
    return x = Setlike [x] 
    Setlike xs >>= f = [y | x <- xs, let Setlike ys = f x, y <- ys] 

collapse :: Ord a => Setlike a -> Setlike a 
collapse = Setlike . Data.Set.toList . Data.Set.fromList . toList 

ten może być stosowany tak:

valuesOfInterest = collapse $ do 
    v1 <- allValues 
    v2 <- allValues 
    doSomethingInteresting v1 v2 

Wtedy, nawet jeśli niektóre parowanie v1 i v2 zdarzyć uzyskać taką samą wartość odsetek, ta wartość pojawi się tylko raz w wyniku.

Niektóre podobne sztuczki są prawdopodobnie możliwe również w przypadku użycia.

Powiązane problemy