2017-03-09 42 views
0
data Task = Task 
    { id :: String 
    , description :: String 
    , dependsOn :: [String] 
    , dependentTasks :: [String] 
    } deriving (Eq, Show, Generic, ToJSON, FromJSON) 

type Storage = Map String Task 

s :: Storage 
s = empty 

addTask :: Task -> Storage -> Storage 
addTask (Task id desc dep dept) = insert id (Task id desc dep dept) 

removeTask :: String -> Storage -> Storage 
removeTask tid = delete tid 

changes = [addTask (Task "1" "Description" [] []), removeTask "1"] 

main = putStrLn . show $ foldl (\s c -> c s) s changes 

假設我有以下代碼。我想將changes列表存儲在json文件中。但是我不知道如何與Aeson做到這一點,除了編寫自定義解析器外,還有一個更好的方法可以做到這一點。喜歡也許使用語言擴展派生(Generic, ToJSON, FromJSON)addTaskremoveTask等...如何將函數類型序列化爲haskell中的json?

編輯。對於所有說「你不能序列化功能」的人。

閱讀評論回答這個問題。

Instance Show for function

這就是說,它不是可以定義顯示實際上給你更多 ?有關功能的細節。 - 路易Wasserman 12年5月12日在14:51

當然是的。它可以顯示類型(通過Typeable給出);或者它可以顯示一些輸入和輸出(如在QuickCheck中完成的那樣)。

EDIT2。好的,我知道我不能在序列化中使用函數名。但是,這可以通過模板Haskell完成嗎?我看到aeson通過模板Haskell支持序列化,但作爲Haskell的新手無法弄清楚如何做到這一點。

+0

您不能「顯示」或序列化或比較或檢查函數。 –

+0

@ n.m嗯......我明明可以序列化一個函數應用程序並手動反序列化,對嗎?那麼爲什麼這不應該自動執行?這是相當機械的...你有一個函數名稱,你有它的參數你知道他們的類型... – user1685095

+0

@ n.m。由於參照透明度等原因,你不能「顯示」功能。而我想要的並不需要做任何事情。我想看看函數名稱和它的參數,就是這樣。 – user1685095

回答

2

讓你的功能的數據類型和評價函數:

data TaskFunction = AddTask Task | RemoveTask String 
    deriving (Eq, Show, Generic, ToJSON, FromJSON) 

eval :: TaskFunction -> Storage -> Storage 
eval (AddTask t) = addTask t 
eval (RemoveTask t) = removeTask t 

changes = [AddTask (Task "1" "Description" [] []), RemoveTask "1"] 

main = putStrLn . show $ foldl (\s c -> c s) s (eval <$> changes) 
+0

是的,這是一個明顯的解決方案,我不想要。 eval只是'apply'。我不明白爲什麼我需要這樣的樣板代碼。 – user1685095

+3

叫我瘋了,但我更喜歡複雜的一次性魔法解決方案的樣板。這是明確而易於理解的。 – user2297560

+0

@ user1685095「我不明白爲什麼我需要這樣的樣板代碼」 - 因爲你不能序列化一個函數,但是你可以序列化一個合適的物化表示。 – duplode

3

這裏的字裏行間一點,重複的問題是,「爲什麼我不能序列化功能(容易)?」答案 - 幾個人提到但沒有明確解釋 - 是Haskell致力於參照透明度。引用透明度表示,您可以用定義的值替換定義(反之亦然),而不更改程序的含義。

所以,現在,讓我們假設我們有一個假設serializeFunction,在這個代碼的存在:

foo x y = x + y + 3 

本來這種行爲:

> serializeFunction (foo 5) 
"foo 5" 

我猜你不會反對過如果我還聲稱存在

bar x y = x + y + 3 

we woul d 「想」 這種行爲:

> serializeFunction (bar 5) 
"bar 5" 

而現在我們有一個問題,因爲引用透明

serializeFunction (foo 5) 
= { definition of foo } 
    serializeFunction (\y -> 5 + y + 3) 
= { definition of bar } 
    serializeFunction (bar 5) 

"foo 5"不等於"bar 5"

明顯的後續問題是:爲什麼我們要求參照透明度?至少有兩個很好的理由:第一,它允許像上面那樣的等式推理,從而減輕了重構的負擔;其次,它減少了所需的運行時信息量,從而提高了性能。

當然,如果你能想出一個尊重參照透明度的函數表示,那就沒有問題了。下面是在該方向上的一些想法:

  • 打印功能

    instance (Typeable a, Typeable b) => Show (a -> b) where 
        show = show . typeOf 
    -- can only write a Read instance for trivial functions 
    
  • 功能的printing the input-output behavior的類型(也可be read back in

  • 創建數據類型,它結合一個帶名稱的函數,然後打印該名稱

    data Named a = Named String a 
    instance Show (Named a) where 
        show (Named n _) = n 
    -- perhaps you could write an instance Read (Map String a -> Named a) 
    

    (也見cloud haskell這種想法更完整的工作)

  • 構建能夠代表所有你關心的表情,但是包含已經有了一個Show實例和序列化,只有基本類型代數數據類型(例如如在其他答案中所述)

但是,打印裸函數的名稱與參照透明性衝突。

+0

我確定我的'Typeable'實例存在於Hackage的某處。如果有人知道,請留下我的評論或繼續並直接替換我的示例實例與鏈接。 –

+0

感謝您的解釋,但也許避免模板Haskell模板是可能的?如果是這樣,比如何用aeson做到這一點? – user1685095

+0

@ user1685095我鼓勵你給它一個機會,並打開一個新的問題,詳細說明你卡在哪裏。我也鼓勵你仔細閱讀我的鏈接,以確保你不會重複別人已經完成的工作。 –