2012-02-15 18 views
5

Pytanie. Czy istnieje sposób, aby ten kod działał bez jawnego podpisu typu?Wnioskowanie o ocenę typu GHC

Kod. Najpierw mam w praktyce znacznie ładniejszą alternatywną klasę MonadTrans, zainspirowaną przez Data.Newtype. Wygląda na to,

{-# LANGUAGE FlexibleContexts, TypeFamilies #-} 

module Alt.Control.Monad.Trans where 

import Control.Monad 

class (Monad , Monad (BaseMonad)) => MonadTrans (:: * -> *) where 
    type BaseMonad :: * -> * 
    lift :: (BaseMonad) α -> α 

Następnie mam klasy A metodą foo, a jeśli niektóre monada baza M jest A, wtedy każda monada T M przekształcona jest również A. W kodzie,

class A where 
    foo :: String -> () 

instance (A (BaseMonad), MonadTrans) => A where 
    foo n = lift $ foo n 

Jednak jeśli teraz chcesz utworzyć skrót do foo z pierwszym argumentem podstawiona, to muszę mieć wyraźny typ podpisu lub przepełnienia stosu kontekst kompilator za.

minimize_call :: A => () 
minimize_call = foo "minimize" 

Możliwa informacji, aby pomóc wnioskowanie. Załóżmy, że mamy powiązany typ: B :: * -> *. Myślę, że chcę powiedzieć kompilatorowi: B spełnia B t /= t, B (B t) /= B t, czyli np. B jest w jakiś sposób "monotonny" - że gonienie powiązanych typów jest równoznaczne z usuwaniem owijki nowego typu, i powinno wiedzieć, że nie może na zawsze usunąć owijki nowego typu , dlatego konieczne jest dodanie do podpisu A.

+0

Przepraszam, powinienem był zadać sobie trud zapamiętania _why_ przerzuciłem się na alternatywny 'MonadTrans' ... na razie, powiedzmy, że produkuje czystszy kod, ale myślę, że był bardziej istotny powód. – gatoatigrado

+0

Interesujące pytanie. Dlaczego nie chcesz jednak podpisu typu jawnego? Czy "minimize_call" nie musi być stałą wartością, a nie stałą polimorficzną (czy może można ją uznać za polimorficzną, nie jestem pewna)? Jeśli ma jakiś pojedynczy stały typ, wolałbym to dokumentować, a jeśli nie, wolałbym dokumentować ** to **. Zmuszenie czytelnika do wykonania w głowie analizy całego programu, aby dowiedzieć się, jaki typ 'minimize_call' wydaje się trochę nieproduktywny. – Ben

+0

@Ben, To prawda, że ​​w tym przypadku posiadanie podpisu typu dla 'minimize_call' jest dobrą praktyką.Jednak sugerowana propozycja typu sugeruje, że coś idzie źle (z projektem, kompilatorem lub komunikacją z kompilatorem) i prawdopodobnie spowoduje problemy, nie wspominając o niezrozumiałych komunikatach o błędach. – gatoatigrado

Odpowiedz

3

Tak, jest sposób. Podaj uziemioną instancję dla A i dodaj NoMonomorphismRestriction do języka pragma (oprócz wymaganych również FlexibleInstances i UndecidableInstances).

Jednak klasa A będzie niezdatna do użytku. Kompilator nie może stwierdzić, że nigdy nie będzie instancji MonadTrans z BaseMonad m = m. W związku z tym nie może wybrać żadnej instancji, ponieważ nie może wiedzieć, czy użyć instancji z tego lub innego.

{-# LANGUAGE FlexibleContexts, TypeFamilies, FlexibleInstances, UndecidableInstances, NoMonomorphismRestriction #-} 

module Trans (MonadTrans(..), A(..), minimize_call) where 

import Control.Monad 

class (Monad m, Monad (BaseMonad m)) => MonadTrans (m :: * -> *) where 
    type BaseMonad m :: * -> * 
    lift :: (BaseMonad m) α -> m α 

class A m where 
    foo :: String -> m() 


data Foo a = Bork 

instance Monad Foo where 
    return _ = Bork 
    _ >>= _ = Bork 

instance A Foo where 
    foo _ = Bork 


instance (A (BaseMonad m), MonadTrans m) => A m where 
    foo n = lift $ foo n 

-- minimize_call :: A m => m() 
minimize_call = foo "minimize" 

kompiluje się z ghc 6.12, 7.0, 7.2 i 7.4. Bez podpisu, minimize_call musi uzyskać typ monomorficzny, chyba że MR jest wyłączony. To i tak nie może działać, ponieważ ograniczenie A m nie jest domyślne. Dlatego MR musi być wyłączone. Ale wtedy kontroler typu wciąż ściga swój własny ogon, próbując udowodnić, że przymus jest satysfakcjonujący. Tylko z instancją do podnoszenia nie może tego zrobić. Jeśli podasz kotwicę, może.

Ale podanie podpisu typu jest o wiele lepsze.

+0

Dzięki, ale nie chcę uziemionej instancji w module. W rzeczywistości mogą istnieć wielokrotne, uziemione instancje, w zależności od tego, ile backendów mam do mojego kompilatora i zakładam, że użycie jednego konkretnego jest niepożądane. Mam już włączone 'FlexibleInstances',' FlexibleContexts' i 'NoMonomorphismRestriction'. – gatoatigrado

+1

Możesz użyć nieeksportowanej sztucznej monady, zobacz aktualizację. Wywiedziony typ 'minimize_call' to' A m => m() ', tak jak powinno być, nie wyklucza użycia innych, uziemionych instancji w innym miejscu, konieczne jest tylko, aby sprawdzanie typu zakończyło się. –

+0

Właściwie, nie możesz w ogóle użyć 'minimize_call' lub' foo'. Z podpisem typu lub bez niego. –