[英]How to cleanly define a Haskell module containing functions with a common variable parameter?
我正在嘗試編寫一個Haskell模塊,該模塊使用庫haxr定義遠程XML-RPC API的函數。 以下是haxr的文檔建議您如何在url
上定義一個調用examples.add
的Haskell函數:
add :: String -> Int -> Int -> IO Int
add url = remote url "examples.add"
像這樣叫:
server = "http://localhost/~bjorn/cgi-bin/simple_server"
add server x y
如果我有一個或兩個XML-RPC方法(我不需要一個單獨的模塊),這對我來說似乎沒問題。 但是,代碼中的server
重復是一個問題,因為我有近100個函數。 我無法在模塊中定義server
,如下所示:
someRemote :: Remote
someRemote = remote "http://example.com/XMLRPC"
add :: Int -> Int -> IO Int
add = someRemote "examples.add"
因為如果URL對於使用它的代碼是靈活的,則不能對其進行硬編碼。 我也無法將someRemote
定義為函數的參數,因為它具有相同的重復問題。
Haxr的例子沒有提供如何解決這個問題的線索。
我通常用命令式OOP語言(即Java,Python)編寫程序。 如果我使用這些語言,我會定義一個帶有構造函數的類,它使用server
,所有函數都使用對象實例的server
變量,而不是詢問調用代碼。
我在Haskell中找到了相同的東西,但我似乎不知道找到它的正確關鍵字。 類型類似乎不是答案。 我可以寫一個更高階的函數來返回部分應用的函數,但是解壓縮它們會更加丑陋。
我不太確定“重復服務器”實際上是那么糟糕。 當然,你永遠不應該復制一個冗長的文字,但對於一個不會使代碼混亂並且易於替換的單個變量名稱,這應該不是什么大問題。
但是當然,通過將共享變量附加到您正在使用的monad中,您可以輕松避免這種重復,類似於將其附加到OO類對象的方式。 那叫做讀者 。
import Control.Monad.Trans.Reader
type RemoteIO = ReaderT String IO -- or perhaps `ReaderT Remote IO`
add :: Int -> Int -> RemoteIO Int
add x y = do
url <- ask
lift $ remote url "examples.add" x y
您可以通過將服務器包裝在“對象”中並將其作為第一個參數傳遞給所有“方法”來模擬Haskell中的OOP方法:
module MyServer (
Server, -- don't expose constructor
newServer,
add,
) where
data Server = Server String
newServer :: String -> IO Server
newServer = return . Server
add :: Server -> Int -> Int -> IO Int
add (Server url) = remote url "examples.add"
您每次撥打電話時仍然必須通過服務器,但現在您可以更改服務器的表示(例如,使其成為持久連接的句柄)。
此外,您可以使用reader monad來隱式傳遞服務器:
class MonadServer m where
withServer :: (Server -> m a) -> m a
instance MonadServer (ReaderT Server m) where
withServer f = ReaderT (\server -> runReaderT (f server) server)
add :: (MonadIO m, MonadServer m) => Int -> Int -> m Int
add x y = withServer (\(Server url) -> liftIO $ remote url "examples.add" x y)
通過使MonadServer成為類型類,可以擴展您使用的任何讀者monad以支持隱式Server參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.