2015-04-17 6 views
10

Derleyiciyi, bir kısıtlamanın her zaman kapalı tip bir ailede eşanlamlı karakterlerden memnun olduğunu ikna edebilir miyim? Aile, sınırlı sayıda teşvik edilmiş değer dizisi ile endekslenmiştir. Açık tip ailelere bir kısıtlamayı zorlayabilirKısıtlı kapalı tip aile

data NoShow = NoShow 
data LiftedType = V1 | V2 | V3 

type family (Show (Synonym (a :: LiftedType)) => Synonym (a :: LiftedType)) where 
    Synonym V1 = Int 
    Synonym V2 = NoShow -- no Show instance => compilation error 
    Synonym V3 =() 

çizgisinde

şey:

class (Show (Synonym a)) => SynonymClass (a :: LiftedType) where 
    type Synonym a 
    type Synonym a =() 

instance SynonymClass Int where 
    type Synonym V1 = Int 

-- the compiler complains here 
instance SynonymClass V2 where 
    type Synonym V2 = NoShow 

instance SynonymClass V3 

ama derleyici sonra olurdu var olduğu gerçeği hakkında ikna etmeye muktedir V1, V2 ve V3'un her biri için SynonymClass a numaralı bir örnek? Ama her durumda açık tip bir aile kullanmamayı tercih ederim.

Bunu gerektirme konusundaki motivasyonum, derleyiciyi, kodumdaki kapalı tip bir ailenin tüm örneklerinin Göster/Oku örneklerine sahip olduğuna ikna etmek istemesidir. Bir basitleştirilmiş bir örnektir:

parseLTandSynonym :: LiftedType -> String -> String 
parseLTandSynonym lt x = 
    case (toSing lt) of 
    SomeSing (slt :: SLiftedType lt') -> parseSynonym slt x 

parseSynonym :: forall lt. SLiftedType lt -> String -> String 
parseSynonym slt flv = 
     case (readEither flv :: Either String (Synonym lt)) of 
     Left err -> "Can't parse synonym: " ++ err 
     Right x -> "Synonym value: " ++ show x 

[mümkün olmadığını yorum olarak bahsedilen Birisi - teknik olarak imkansız bu çünkü (ve eğer öyleyse, neden) veya GHC uygulamasının sadece bir sınırlama?]

+0

Bunu da istedim ama bildiğim kadarıyla bu mümkün değil. Sanırım sadece 1 sınıfa ihtiyacım var. – bennofs

+1

Neden sadece parseF :: forall lt. (Oku (Anlaml lt), Göster (Translationm lt)) => SLiftedType lt -> String -> String'? Anladığım kadarıyla amacın için yeterli. –

+0

@ AndrásKovács Motivasyon örneğime biraz daha içerik ekledim. '' SLiftedType lt'' değeri ön tanımlı değil - '' (String, String) '' '' (LiftedType, String) '' ve sonra '' (SLiftedType lt, Eşanlamlı lt) '', ancak '' SomeSing'' ifadesinde bağlı olarak yazılan kısmı saklıyorum. – dbeacham

cevap

4

Sorun şu ki, bir örnek başlığında Synonym koyamıyorum çünkü bu bir tür aile, ve (forall x. Show (Synonym x)) => ... gibi "evrensel olarak ölçülmüş" bir kısıtlama yazamıyoruz çünkü Haskell'de böyle bir şey yok. Bize yine örnek kafalarına tipi aileleri koymak sağlar (exists x. f x) -> a

  • singletons 'ın defunctionalization için

    • forall x. f x -> a eşdeğerdir:

      Ancak, iki şey kullanabilirsiniz.

      data Some :: (TyFun k * -> *) -> * where 
          Some :: Sing x -> f @@ x -> Some f 
      

      Ve biz de LiftedType için defunctionalization sembolünü içerir::, biz Şimdi

      import Data.Singletons.TH 
      import Text.Read 
      import Control.Applicative 
      
      $(singletons [d| data LiftedType = V1 | V2 | V3 deriving (Eq, Show) |]) 
      
      type family Synonym t where 
          Synonym V1 = Int 
          Synonym V2 =() 
          Synonym V3 = Char 
      
      data SynonymS :: TyFun LiftedType * -> * -- the symbol for Synonym 
      type instance Apply SynonymS t = Synonym t 
      

      Yani

    , biz singletons tarzı tip fonksiyonlar üzerinde çalışan bir varoluşsal sarıcı tanımlamak forall x. Synonym x -> a yerine Some SynonymS -> a kullanın ve bu form örneklerde de kullanılabilir.

    instance Show (Some SynonymS) where 
        show (Some SV1 x) = show x 
        show (Some SV2 x) = show x 
        show (Some SV3 x) = show x 
    
    instance Read (Some SynonymS) where 
        readPrec = undefined -- I don't bother with this now... 
    
    parseLTandSynonym :: LiftedType -> String -> String 
    parseLTandSynonym lt x = 
        case (toSing lt) of 
        SomeSing (slt :: SLiftedType lt') -> parseSynonym slt x 
    
    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = 
         case (readEither flv :: Either String (Some SynonymS)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right x -> "Synonym value: " ++ show x 
    

    Bu doğrudan biz hala sağ tip var olmadığını kontrol etmek varoluşsal etiketinde sonra Some SynonymS ve desen maçı okuyabilir rağmen t herhangi spesifik seçim için bize Read (Synonym t) sağlamak (ve eğer o değil başarısız değil sağ). Bu hemen hemen tüm kullanım durumlarda read kapsar.

    Bu yeterli değilse, başka bir sarıcı kullanabilir ve Some f örneklerini "evrensel olarak nicelendirilmiş" örneklere kaldırabiliriz.

    data At :: (TyFun k * -> *) -> k -> * where 
        At :: Sing x -> f @@ x -> At f x 
    

    At f xf @@ x eşdeğerdir, ancak bunun için örneklerini yazabilir.Özellikle, burada makul bir evrensel Read örneğini yazabiliriz.

    instance (Read (Some f), SDecide (KindOf x), SingKind (KindOf x), SingI x) => 
        Read (At f x) where 
        readPrec = do 
         Some tag x <- readPrec :: ReadPrec (Some f) 
         case tag %~ (sing :: Sing x) of 
         Proved Refl -> pure (At tag x) 
         Disproved _ -> empty 
    

    Biz ilk önce, bir Some f ayrıştırmak ayrıştırılmış tip indeksi biz ayrıştırmak istediğiniz indeksi eşit olup olmadığını kontrol edin. Özel endekslerle ayrıştırma türleri için yukarıda bahsettiğim modelin bir soyutlaması. Bu daha uygun çünkü sadece kaç tane indeksimiz olursa olsun, At modelinde tek bir vaka var. SDecide kısıtlamasına rağmen dikkat edin. %~ yöntemini sağlar ve deriving Eq tekil veri tanımlarına dahil edersek singletons bizim için türetir. Bunu kullanmak için koymak:

    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = withSingI slt $ 
         case (readEither flv :: Either String (At SynonymS lt)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right (At tag x) -> "Synonym value: " ++ show (Some tag x :: Some SynonymS) 
    

    Biz de At ve Some biraz daha kolay arasında dönüşüm yapabilirsiniz: Sana ne yapmak istediğini doğru anlamak

    curry' :: (forall x. At f x -> a) -> Some f -> a 
    curry' f (Some tag x) = f (At tag x) 
    
    uncurry' :: (Some f -> a) -> At f x -> a 
    uncurry' f (At tag x) = f (Some tag x) 
    
    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = withSingI slt $ 
         case (readEither flv :: Either String (At SynonymS lt)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right atx -> "Synonym value: " ++ uncurry' show atx 
    
  • +0

    Ben cevapta 'singletons' ile yakın yakınlık varsaydı. Lütfen bir şey belirsiz olup olmadığını sorun. –

    0

    , bu mümkün değildir. Öyle olsaydı, kolayca

    data YesNo = Yes | No 
    class Foo (yn :: YesNo) where foo :: Proxy yn -> Bool 
    type family (Foo (T t) => T t) where 
        T X = Yes 
        T y = No 
    
    f :: forall t. Proxy t -> Bool 
    f _ = foo (Proxy (T t)) 
    

    çizgisinde tip Proxy t -> Bool olmayan bir sabit fonksiyon olustursaydiniz Ama sen de elbette dahil her türlü kapalı bile böyle bir işlevi, (inşa edemez bakış açınıza bağlı olarak bir özellik veya GHC sınırlaması).