如何在haskell中将函数类型序列化为json?

时间:2017-03-09 17:33:40

标签: json haskell

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)addTask等导出removeTask一样......

EDIT。对于所有说'#34;你不能序列化功能"。

的人

阅读评论以回答这个问题。

Instance Show for function

  

那就是说,不可能定义Show来实际给你更多   ?关于功能的细节。 - Louis Wasserman 5月12日和12日14:51

     

当然可以。它可以显示类型(通过Typeable给出);或者它可以显示一些输入和输出(如在QuickCheck中所做的那样)。

EDIT2。好的,我知道我不能在序列化中使用函数名。但这可以通过模板Haskell完成吗?我看到aeson通过模板Haskell支持序列化,但是Haskell的新手无法弄清楚如何做到这一点。

2 个答案:

答案 0 :(得分:3)

稍微阅读一下这一行,这是一个反复出现的问题,"为什么我不能(轻松地)序列化一个函数?"答案 - 几个人已经提到过,但没有明确解释 - 是Haskell致力于引用透明度。参照透明度表示您可以在不改变程序含义的情况下用定义的值替换定义(反之亦然)。

现在,让我们假设我们有一个假设的serializeFunction,在此代码存在的情况下:

foo x y = x + y + 3

会有这种行为:

> serializeFunction (foo 5)
"foo 5"

如果我还声称存在

,我猜你不会过于强烈反对
bar x y = x + y + 3

我们希望"想要"这个行为:

> 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个实例并序列化的基本类型(例如,如另一个答案所述)

但是打印一个裸函数的名称与引用透明度相冲突。

答案 1 :(得分: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)