2016-11-08 16 views
5

Jak mogę automatycznie czerpać instancji Read dla tego GADTs:wynikających lektura Instancje dla GADTs

{-# LANGUAGE GADTs, StandaloneDeriving #-} 

data TypeDec a where 
    TypeDecInt :: TypeDec Int 
    TypeDecString :: TypeDec String 

deriving instance Show (TypeDec a) 

data Bar where 
    Bar :: (Show a, Read a) => TypeDec a -> a -> Bar 

deriving instance Show Bar 

Chodzi o to, że Bar jest rodzajem serializacji.

mogę napisać instancji Read następującym fragmencie lub parsec, ale biorąc pod uwagę, że mam wiele podobnych typów do TypeDec i Bar w różnych modułach To boilerplate:

instance Read Bar where 
    readsPrec _ s = 
    let (bar, tup) = second breaks . breaks $ s 
    in if "Bar" == bar then uncurry parse tup else [] 
    where 
     parse :: String -> String -> [(Bar, String)] 
     parse tdec = case tdec of 
     "TypeDecInt" -> parse' TypeDecInt 
     "TypeDecString" -> parse' TypeDecString 
     _    -> const [] 

     parse' :: (Show a, Read a) => TypeDec a -> String -> [(Bar, String)] 
     parse' tdec s = [(Bar tdec (read s), "")] 

     breaks :: String -> (String, String) 
     breaks = second (drop 1) . break (== ' ') 
+2

Jest to całkowicie wykonalne przy użyciu szablonu Haskell. Wystarczy użyć podejścia wspomnianego przez @dfeuer (tzn. Używając 'case' zamiast' GHC.Read.choose'). – Alec

+0

@ Czy znasz jakiś przykład/samouczek do wyprowadzania instancji 'Read' za pomocą szablonu Haskell? – homam

+0

[This] (https://wiki.haskell.org/Template_haskell/Instance_deriving_example) wygląda dobrze. – Alec

Odpowiedz

4

nie wiem ogólnego rozwiązania. Powiedział, że jest to dużo łatwiej napisać Read instancji poprzez zdefiniowanie sposobu readPrec (patrz Text.Read zwłaszcza Text.ParserCombinators.ReadPrec dla niektórych dodatkowych funkcji, a może nawet bardziej dla Text.ParserCombinators.ReadP) zamiast korzystania Parsec lub definiowania readsPrec. Nie jest to absolutna szybkość, jaką można napisać, ale powinna być dość szybka.

import Text.Read 

instance Read Bar where 
    readListPrec = readListPrecDefault 

    readPrec = parens $ do 
    Ident "Bar" <- lexP 
    Ident td <- parens lexP 
    case td of 
     "TypeDecInt" -> Bar TypeDecInt <$> readPrec 
     "TypeDecString" -> Bar TypeDecString <$> readPrec 
     _ -> empty 

Jeśli TypeDec jest bardziej skomplikowana i trzeba Read instancje dla TypeDec Int i TypeDec String (używając FlexibleInstances lub klasę pomocniczą), a następnie będziesz prawdopodobnie chcesz coś bardziej modułowe:

instance Read Bar where 
    readListPrec = readListPrecDefault 
    readPrec = parens $ do 
    Ident "Bar" <- lexP 
    (Bar <$> readPrec <*> (readPrec :: ReadPrec Int)) 
     <|> (Bar <$> readPrec <*> (readPrec :: ReadPrec String)) 

Należy pamiętać, że w drugim przykładzie GHC nie może samodzielnie określić, jakie typy powinny mieć alternatywy, ale gdy ustalimy typ drugiego pola, to wnioskowanie propaguje je do pierwszego pola. W pierwszej kolejności szukamy tylko "TypeDecInt", aw drugim szukamy tylko "TypeDecString".


Pakiet dependent-sum definiuje typ, który uogólnia swoje Bar.

data DSum tag f = forall a . !(tag a) :=> f a 

definiuje również Read instancję DSum korzystając jakieś dodatkowe zajęcia. Ogólne podejście wygląda solidnie, ale polecam dwie zmiany. Pierwszym jest użycie ReadPrec zamiast mieszania z listami. Drugi ma zastąpić typ wyższej rangi GReadResult z następujących (znacznie prostsze) egzystencjalny jeden:

data GReadResult t = forall a . GReadResult (t a) 

Oczywiście, zmiany te zmusi Cię do jego realizacji siebie, ale to jest w porządku.

+0

'Suma zależna' jest interesującą biblioteką. Ale z jego [przykładów] (https://github.com/mokus0/dependent-sum/blob/master/examples/FooGADT.hs#L72) wydaje mi się, że wciąż muszę zdefiniować instancje 'GRead' (i napisać kilka elementów dla każdego konstruktora typu 'tag'). – homam

+0

@homam, tak, podejście "DSum" po prostu narzuca pewną strukturę instancji; nie zyskasz dużo, jeśli nic, chyba że chcesz używać powiązanych narzędzi. Po prostu pomyślałem, że warto o tym wspomnieć. – dfeuer

Powiązane problemy