如果你想對源代碼級的功能,如做到這一點:
myFoo x y = x + y
你非常不走運了,除非你想要去的編譯器黑客左右。但是,您可以定義您自己的支持此功能的概念,並帶有一些適當的註釋。我們稱這個概念爲UserAction a
,其中a
是該操作的返回類型。爲了組成UserAction
中的計算,它應該是Monad
。沒有想到過非常辛苦,我的第一感覺就是使用這個堆棧單子變壓器:
type UserAction = WriterT [LogEntry] (ReaderT FuncIdentifier IO)
的WriterT [LogEntry]
組件說,一個UserAction
,運行時產生的LogEntry
S [1]的序列,其中含有你想要寫入數據庫的信息;是這樣的:
data LogEntry = Call FuncIdentifier FuncIdentifier
沒關係推遲存儲隨機種子,任務標識符等現在 - 這可以通過將信息添加到LogEntry
被納入這個設計。
ReaderT FuncIdentifier
組件說,UserAction
取決於FuncIdentifier
;即調用它的函數的標識符。
FuncIdentifier
可以通過爲
type FuncIdentifier = String
簡單的東西來實現,或者你使用的東西與更多的結構,如果你喜歡。
IO
組件說,UserAction
s可以做任意的輸入和輸出到文件,控制檯,產卵線程,整個很多。如果您的操作不需要此操作,請不要使用它(使用Identity
代替)。但是既然你提到了產生隨機數,我想你並沒有純粹的計算[2]。
那麼你會標註你想要這樣的功能來記錄日誌,每個動作:
userAction :: FuncIdentifier -> UserAction a -> UserAction a
這會像這樣使用:
randRange :: (Integer, Integer) -> UserAction Integer
randRange (low,hi) = userAction "randRange" $ do
-- implementation
userAction
將記錄呼叫,並設置記錄他們的呼叫;例如是這樣的:
userAction func action = do
caller <- ask
-- record the current call
tell [Call caller func]
-- Call the body of this action, passing the current identifier as its caller.
local (const func) action
從頂層,運行所需的行動和完成任務後,收集了所有的LogEntry
S和它們寫入數據庫。
如果您需要在代碼執行時實時寫入呼叫,則需要不同的UserAction
monad;但你仍然可以呈現相同的界面。
該方法使用了一些中間Haskell概念,如monad變換器。我建議去IRC irc.freenode.net
#haskell
頻道詢問關於填寫本實施草圖細節的指導。他們是一羣善良的人,會很樂意幫助你學習:-)。
[1]在實踐中,您不會想要使用[LogEntry]
而是使用DList LogEntry
來獲得性能。但改變很簡單,我建議你用[LogEntry]
,直到你對Haskell更舒適,然後切換到DList
。
[2]隨機數字的生成可以純粹完成,但它需要進一步的大腦重新連接,這個草圖已經有很多,所以我建議只是把它當作一個IO
效果來達到目的。
你是什麼意思的「功能圖」? GHC Haskell在運行時系統中將函數表示爲圖形(運行時是所謂的圖形縮減機器),但內部不能被用戶訪問。將函數存儲在數據庫中會非常複雜 - 通常這是「持久」語言的領域,例如Napier 88或Tycoon-2。在Napier 88的持久性商店(Persistent Haskell)上實施了Haskell,但這是一個早已完成的研究項目。 –
對不起,有點不清楚。我仍然與Haskell搖擺不定。我會用我認爲可能是我需要的策略來重述這個問題。我現在打字。 –
用戶與什麼樣的API進行交互? – Henrik