2013-07-31 17 views
6

Aynı akışa ve monoya sahip, ancak farklı kullanıcı durumu ve sonucu olan iki ParsecT kodunu birleştirmenin kolay bir yolunu arıyorum. Esasen böyle bir fonksiyon güzel olurdu:Parsec kullanıcı durumunun türünü değiştirmek için kolay bir yol?

withUserState :: u -> ParsecT s u m a -> ParsecT s v m a 

şey kullanıcı durumu bazı durumlarda gerçekten yararlı olduğunu, olduğunu ama farklı zamanlarda farklı durumları ihtiyaç ve yapmak istemiyorum durumları daha büyük yazar. Bunu yapmak için bir şekilde durumu değiştirmem gerekiyor mu, veya şu anda bulamadığım bir işlev var mı?

Düzenleme: alternatif >>= operatör

olarak

ParsecT s u m a -> (a -> ParsecT s u m b) -> ParsecT s u m b 

ve (<*>) türü vardır gibi bunu yapamaz şey

changeUserState :: (u -> v) -> ParsecT s u m a -> ParsecT s v m a 

cevap

8

Parsec doğrudan kutudan bunu yapmasına izin vermez, ancak aşağıdaki gibi bu kullanarak parsekten kamu API elde edebilirsiniz: o u ve v arasında hem ileri hem geri dönüşüm gerektirir

{-# LANGUAGE ScopedTypeVariables #-} 

import Text.Parsec 

changeState 
    :: forall m s u v a . (Functor m, Monad m) 
    => (u -> v) 
    -> (v -> u) 
    -> ParsecT s u m a 
    -> ParsecT s v m a 
changeState forward backward = mkPT . transform . runParsecT 
    where 
    mapState :: forall u v . (u -> v) -> State s u -> State s v 
    mapState f st = st { stateUser = f (stateUser st) } 

    mapReply :: forall u v . (u -> v) -> Reply s u a -> Reply s v a 
    mapReply f (Ok a st err) = Ok a (mapState f st) err 
    mapReply _ (Error e) = Error e 

    fmap3 = fmap . fmap . fmap 

    transform 
     :: (State s u -> m (Consumed (m (Reply s u a)))) 
     -> (State s v -> m (Consumed (m (Reply s v a)))) 
    transform p st = fmap3 (mapReply forward) (p (mapState backward st)) 

Not olduğunu . Bunun nedeni, önce ortam durumunuzu yerel duruma çevirmeniz, iç ayrıştırıcınızı çalıştırmanız ve sonra geri dönüştürmeniz gerektiğidir.

ScopedTypeVariables ve yerel tür imzalar yalnızca netlik için hazırlanmıştır - isterseniz bunları kaldırmaktan çekinmeyin.

+0

Wow - bu tam olarak ihtiyacım olan şey gibi görünüyor. Ayrıca: bu mümkün olduğunu bilmek güzel :) –

2

gibi olacağını düşünüyorum

ParsecT s u m (a -> b) -> ParsecT s u m a -> ParsecT s u m b 

s değişkeni, evrensel olarak ölçülür, ancak her iki koşulla da eşleşmelidir. >>= veya <*> olmadan, hiçbir uygulama veya monadik işlev kullanamazsınız. Bu, parserleri farklı durumlarla birleştirmenin kesinlikle hiçbir yolu olmayacağı anlamına gelir. Bunu yapmanın en iyi yolu, yalnızca

data PotentialStates = State1 ... 
        | State2 ... 
        | State3 ... 

ve daha sonra bununla birlikte çalışır.

+0

(> =) ve (<*>) türleriyle güzel tartışmalar için teşekkürler. Bir şeyi test etmek için bir dakikaya ihtiyacım var, ama muhtemelen beni şimdiden mutlu ettin. –

+1

Yardımcı olduğunu buldum! İyi şanslar! – jozefg

+0

Tamam, parsec'lerin Parsec ™ gibi bir şey döndürmesine izin vermeyi denedim (State is u, a) ve bir sonraki ayrıştırıcının durumunu enjekte ederek ancak stateUser alanını değiştirerek runPT çalıştırırken onları hayal ettiğim şekilde birleştirdim. Bunun çok fazla bir cehennem olduğu ortaya çıktı ve cevabınız bu durumda yol açacak bir şey. Teşekkürler :) –

İlgili konular