How to serialize function type to json in haskell?

后端 未结 2 370
情歌与酒
情歌与酒 2021-01-28 06:06
data Task = Task
    { id :: String
    , description :: String
    , dependsOn :: [String]
    , dependentTasks :: [String]
    } deriving (Eq, Show, Generic, ToJSON, F         


        
相关标签:
2条回答
  • 2021-01-28 06:44

    Make a data type for your functions and an evaluation function:

    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 讨论(0)
  • 2021-01-28 06:46

    Reading between the lines a bit, a recurring question here is, "Why can't I serialize a function (easily)?" The answer -- which several people have mentioned, but not explained clearly -- is that Haskell is dedicated to referential transparency. Referential transparency says that you can replace a definition with its defined value (and vice versa) without changing the meaning of the program.

    So now, let's suppose we had a hypothetical serializeFunction, which in the presence of this code:

    foo x y = x + y + 3
    

    Would have this behavior:

    > serializeFunction (foo 5)
    "foo 5"
    

    I guess you wouldn't object too strenuously if I also claimed that in the presence of

    bar x y = x + y + 3
    

    we would "want" this behavior:

    > serializeFunction (bar 5)
    "bar 5"
    

    And now we have a problem, because by referential transparency

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

    but "foo 5" does not equal "bar 5".

    The obvious followup question is: why do we demand referential transparency? There are at least two good reasons: first, it allows equational reasoning like above, hence eases the burden of refactoring; and second, it reduces the amount of runtime information that's needed, hence improving performance.

    Of course, if you can come up with a representation of functions that respects referential transparency, that poses no problems. Here are some ideas in that direction:

    • printing the type of the function

      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 of the function (which can also be read back in)

    • creating a data type that combines a function with its name, and then printing that name

      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)
      

      (and see also cloud haskell for a more complete working of this idea)

    • constructing an algebraic data type that can represent all the expressions you care about but contains only basic types that already have a Show instance and serializing that (e.g. as described in the other answer)

    But printing a bare function's name is in conflict with referential transparency.

    0 讨论(0)
提交回复
热议问题