2012-02-09 17 views
6

Tôi hiện đang viết một dự án nơi tôi sử dụng nhiều máy biến áp đơn lẻ ListT. Khi sử dụng danh sách đơn giản, việc thực hiện không xác định là rất dễ dàng. Tuy nhiên một khi tôi đã phải chuyển đổi mã của tôi thành ListT, nó trở nên phức tạp hơn nhiều .Làm thế nào để chuyển đổi sạch giữa các danh sách và máy biến áp đơn vị ListT?

Là một ví dụ đơn giản: chuyển đổi [a]-ListT a thực sự đòi hỏi phải sáng tác hai chức năng:

conv :: (Monad m) => [a] -> ListT m a 
conv = ListT . return 

Mặc dù nó đơn giản, tôi ngạc nhiên đó là chưa có.

Câu hỏi:

  • Có một số cách tốt hơn để xử lý nondeterminism nơi một biến đơn nguyên là cần thiết?
  • Có bất kỳ kỹ thuật/thư viện nào để chuyển đổi qua lại giữa các danh sách và ListT một cách sạch sẽ không?

Những lý do chính xác là khá phức tạp, vì vậy tôi không thực sự muốn xây dựng quá nhiều về điều đó.

Trả lời

6

Tôi không nghĩ có bất kỳ thư viện nào cho việc này; conv là một chức năng cực kỳ đơn giản, sau khi tất cả, và cách khác xung quanh chỉ là runListT.

conv cũng tương tự như liftMaybe thường mong muốn khi sử dụng MaybeT:

liftMaybe :: (Monad m) => Maybe a -> MaybeT m a 
liftMaybe = MaybeT . return 

tôi sẽ khuyên bạn đặt tên nó một cái gì đó dọc theo dòng của liftList.

Theo như một biến đơn nguyên tốt hơn cho nondeterminism đi, tôi khuyên bạn nên xem xét các gói logict, dựa trên LogicT biến Oleg, mà là một quay lui Logic đơn nguyên tiếp tục dựa vào với some helpful operations. Như một phần thưởng, kể từ [] là một phiên bản của MonadLogic, các hoạt động đó cũng hoạt động trên danh sách.


Điều thú vị là, chúng ta có thể định nghĩa một hàm generalises các mô hình của convliftMaybe:

import Data.Foldable (Foldable) 
import qualified Data.Foldable as F 

choose :: (Foldable t, MonadPlus m) => t a -> m a 
choose = F.foldr (\a b -> return a `mplus` b) mzero 

Điều này có lẽ sẽ làm cho mã của bạn khá lộn xộn, vì vậy tôi không khuyên bạn sử dụng nó :)

+0

Có, tôi đồng ý rằng 'conv' là một hàm đơn giản. Tôi chỉ ngạc nhiên vì nó chưa có ở đó. Hầu như không có bất kỳ tiện ích nào trong mô-đun 'ListT' khiến tôi cảm thấy như tôi đang phát minh lại bánh xe. Đó là tất cả. – julkiewicz

0

Tôi vừa xem qua câu hỏi này một vài tháng sau vì tôi đã tự hỏi điều gì đó tương tự như vậy. Vì vậy, tôi đã đưa ra như sau:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 

import Control.Monad.Trans.Class 
import Control.Monad.Trans.Maybe 
import Control.Monad.Trans.List 


-- | Minimal implementation: either joinLift or joinT 
class (MonadTrans t, Monad m) => MonadTransJoin t m | m -> t, t -> m where 
    joinLift :: (Monad m', Monad (t m')) => m' (m a) -> t m' a 
    joinLift = joinT . lift 

    joinT :: (Monad m', Monad (t m')) => t m' (m a) -> t m' a 
    joinT = (>>= (joinLift . return)) 


instance MonadTransJoin MaybeT Maybe where 
    joinLift = MaybeT 
    joinT = (>>= maybe mzero return) 

instance MonadTransJoin ListT [] where 
    joinLift = ListT 
    joinT = (>>= foldr mcons mzero) 
     where mcons x xs = return x `mplus` xs 

Cho đến nay rất tốt và phương pháp joinT tôi cho cặp ListT/[] vẻ như nó có cái gì để làm với ehird của choose.

Nhưng vấn đề với điều này là thực sự không có giao diện thống nhất giữa một máy biến áp đơn lẻ và đơn nguyên có hành vi mà nó mang lại cho đơn nguyên cơ sở của nó. Chúng tôi có MaybeT :: m (Maybe a) -> MaybeT m aListT :: m [a] -> ListT m a, nhưng OTOH chúng tôi có StateT :: (s -> m (a, s)) -> StateT s m a. Tôi không biết liệu có cách nào để giải quyết vấn đề này không - nó chắc chắn yêu cầu