2017-08-25 83 views
6

Ben bir Haskell acemi ve şu anda bir api etrafında basit bir sarıcı yapmak için wreq kullanıyorum. Bir süre verilmişse if-modified-since başlığını göndermek istiyorum. Bunu şu şekilde yapıyorum. Haskell/Wreq - http istekleri için karmaşık tip imzalar hakkında öneri

getResponse :: (FormatTime t, Exception e) => File -> Maybe t -> IO (Either e (Response L.ByteString)) 
getResponse file (Just t) = 
    let formattedTime = (B.pack . formatTime defaultTimeLocale rfc822DateFormat) t 
     opts = defaults & header "if-modified-since" .~ [formattedTime] 
    in try $ getWith opts $ buildUrl file 

getResponse file Nothing = try $ (get $ buildUrl file) 

O Either türünü kullanarak benim gerekçesi oldu 304 (not modified) yanıtları istisna olarak geri geliyor fark ettim. Bu API sarıcısını kullanabilecek kişiler için hataların görünmesini sağlamak istedim.

İsteğin başarılı olduğunu varsayarsak, yanıt gövdesini kitaplığımda tanımlı bir türe ayrıştırmak istiyorum. Sunucuda bir şey değiştiyse, desterizasyonun düzgün çalışmayacağı bir şans var, bu yüzden bunu hesaba katmak için Maybe türünü kullanmayı tercih ettim.

getPayload :: FromJSON b => (Either e (Response L.ByteString)) -> Either e (Maybe b) 
getPayload (Left _) = return Nothing 
getPayload (Right a) = return $ fmap Just (^. responseBody) =<< asJSON a 

bu fonksiyonların imzaları boğazımda bir şey var, daha iyi bir yoldur ama emin değilim beni anlatıyor bana bir göze batan şey gibi görünmeye başlıyor. Yaptığım bir şey, onları kullanmanın daha kolay olabileceği umutlarıyla birleştirmek için başka bir işlev yapmaktı. Bu, bireysel kaynaklara daha özel isteklerde bulunmak için diğer işlevleri oluşturmak için kullanmayı planladığım işlevdir.

getResource :: (Exception e, FormatTime t, FromJSON b) => File -> Maybe t -> IO (Either e (Maybe b)) 
getResource f t = getPayload <$> (getResponse f t) 

Şimdi bir http isteği ile uğraşırken 3 katman yapısıyla uğraşmak zorundayım. IO, Either ve Maybe. Bunu çok karmaşık mı yapıyorum? Kullanım ve süreklilik perspektifinden çalışmak için daha az acı çekmek için ne yapabilirim? Bunu nasıl geliştirebilirim?

+4

"e", ayrıca, seri hale getirme hataları hakkında bilgi de taşıyabilecek olan "e" (e) ye (her ikisi de) "e (belki de b)" yi düzleştiremez misiniz? Belki de b, 'e (b)' ye izomorftur. –

+2

Özel bir veri türü kullanmayı düşünürdüm 'data ResourceResult b = Hata e | NotModified | Sonuç b'. Kütüphane toplamı ve ürün çeşitleri iyidir, ancak lezzetsizdir: onlar hiçbir anlamı taşımayan çok geneldir. Doğru, çağrıştırıcı isimlerle özel bir tür kullanmak daha iyidir. – chi

+0

Bu öneri @chi için teşekkür ederiz. Bunu aklımda tutacağım. –

cevap

2

İstediğin oldukça ne olmayabilir, ama asJSONm bir MonadThrow olan dönüş türü m (Response a) sahiptir. Maybe, MonadThrow örneğidir, ancak Either e şeklindedir. Bu, asJSON ile bir sorun olup olmadığını işlemek için 'un'u Maybe kullanması anlamına gelir.

getPayload :: FromJSON b => Either SomeException (Response L.ByteString) 
         -> Either SomeException b 
getPayload = ((fmap (^. responseBody) . asJSON) =<<) 

Açıkçası, bu sol tarafta hatanın türüne ek bir sınırlama koyar

, bu yüzden bu kabul edilebilir olduğundan emin değilim: yerine Either monad içinde 'kalmak' olabilir. Değilse, lütfen yorum bırakın.

+0

Bu gece bunu deneyeceğim ve size döneceğim. –

+0

https://www.schoolofhaskell.com/user/commercial/content/exceptions-best-practices adresinde 'MonadThrow' üzerine bir şeyler okuduktan sonra MonadThrow, atılan istisnayı gizleyecektir. Bunun olmasını istemiyorum çünkü istisna 304 yanıt koduna sahipse, tüketicinin önbelleğe aldıkları verileri sunmaları için bilmesi gerekecek. Özel bir tür kullanma fikri cazip geliyor. –

+1

@NickAcosta Evet, istediğiniz türde özel bir tür olabilir, ancak MonadThrow'un bu özel durumu gizleyeceğini söylemek zor.Bu 'Belki' için doğrudur, çünkü 'Hiçbir şey' hata nedenini atar, ama 'e' için istisna, 'Sol' durumundadır ve 'catch' ile ele alınabilir. –