2015-02-19 16 views
7

Tôi đang viết một khuôn khổ, trong đó chức năng chính hỏi người dùng về chức năng loại a -> [b].Có cách nào "chuẩn" để sử dụng tính tương đương của Reader và chức năng bình thường không?

Tuy nhiên, bởi vì chức năng đó có thể khá phức tạp, thực hiện của nó có thể thường trông như thế này:

fn a = extractPartOfAAndConvert a ++ extractAnotherPartofAAndConvert a 

Đó là lý do tại sao tôi figured sử dụng Reader có thể là một, ý tưởng thành ngữ tốt đẹp để đấu tranh đó. Tuy nhiên, đồng thời tôi nhận ra rằng một số người có thể không muốn sử dụng một đơn nguyên.

Trong khi thử nghiệm, tôi đã chế tác giải pháp này:

class Iso a b where 
    isoFrom :: a -> b 
    isoTo :: b -> a 

instance Iso a a where 
    isoFrom = id 
    isoTo = id 

instance Iso (a -> b) (Reader a b) where 
    isoFrom f = reader f 
    isoTo m = runReader m 

do đó cho phép tôi làm:

testCallback :: MyState -> Callback -> MyState 
testCallback myState cb = cb myState 

-- The important signature 
testCallbackGeneric :: Iso Callback a => MyState -> a -> MyState 
testCallbackGeneric myState cb = (isoTo cb) myState 

callbackFunction :: Callback 
callbackFunction s = s + 10 

callbackMonad :: Reader MyState MyState 
callbackMonad = do 
    x <- ask 
    return $ x - 10 

----------- 

let myStateA = testCallback myState callbackFunction 
-- let myStateB = testCallback myState callbackMonad -- won't work, obviously 
let myStateC = testCallbackGeneric myState callbackFunction 
let myStateD = testCallbackGeneric myState callbackMonad 

Tuy nhiên, tôi cảm thấy rất giống tôi reinventing the wheel.

Có cách nào để thể hiện sự tương đương của Reader để dễ dàng viết các chức năng chung như vậy mà không cần phải tạo lớp của riêng mình không?

+0

Một điều nữa là cho dù đó là thực sự có lợi ích để cung cấp chữ ký phức tạp như vậy, nơi người dùng có thể dễ dàng chuyển sang preferrable cách, giống như trường hợp. Tôi nghĩ rằng không có lý do tại sao mà boilerplate không nên tránh/lấy ra khỏi người sử dụng. –

+1

Bạn có thể sử dụng ràng buộc 'MonadReader', mà đã tồn tại một cá thể cho các hàm. Nếu bạn viết 'f = do {a <- ask; trả về $ 2 * a + 3 * a} ', sau đó bạn có thể sử dụng nó như một hàm như' f 1 == 5' hoặc trong bất kỳ hàm 'Reader' nào trong đó' f :: MonadReader Int m => m Int'. Điều này có thể chuyên về 'Int -> Int' hoặc' Int 'Int'. – bheklilr

+0

@ bheklilr * mà đã tồn tại một thể hiện cho các chức năng. * NÀY. Đây là những gì tôi đã mất tích toàn bộ thời gian! Viết câu trả lời này để tôi có thể upvote nó! :) (và có 'Callback' chỉ là' MyState -> MyState', và 'MyState ~ Int') –

Trả lời

10

Bạn chỉ có thể sử dụng thực tế là đơn vị chức năng (->) r đã có một phiên bản cho MonadReader r được xác định trong Control.Monad.Reader. Bạn có thể viết các chức năng sử dụng chỉ là MonadReader hạn chế và sử dụng chúng hoặc như các chức năng bình thường hoặc trong ReaderT monads khác:

f :: MonadReader Int m => m Int 
f = do 
    a <- ask 
    return $ 2 * a + 3 * a 

normally :: Int 
normally = f 1 
-- normally == 5 

readerly :: Reader Int Int 
readerly = do 
    result <- f 
    return $ 2 * result 

> runReader f 1 
5 
> runReader readerly 1 
10 
Các vấn đề liên quan