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

Suppose I have the following code. I want to store changes list in a json file. But I don't know how to do that with Aeson, aside probably from writing a custom parser and there must be a better way to do that obviously. Like maybe using language extension to derive (Generic, ToJSON, FromJSON) for addTask and removeTask etc...

EDIT. For all people that say "You can't serialize function".

Read the comments to an answer to this question.

Instance Show for function

That said, it's not possible to define Show to actually give you more ? detail about the function. – Louis Wasserman May 12 '12 at 14:51

Sure it is. It can show the type (given via Typeable); or it can show some of the inputs and outputs (as is done in QuickCheck).

EDIT2. Okay, I got that I can't have function name in serialization. But can this be done via template Haskell? I see that aeson supports serialization via template Haskell, but as newcomer to Haskell can't figure out how to do that.

Community
  • 1
  • 1
user1685095
  • 5,787
  • 9
  • 51
  • 100
  • You cannot `show` or serialize or compare or scrutinise functions. – n. m. could be an AI Mar 09 '17 at 17:44
  • @n.m Hmm... Well I obviously can serialize a function application and deserialize by hand, right? So why this should be impossible to do automatically? This is rather mechanic... You have a function name, you have it's arguments you know their types... – user1685095 Mar 09 '17 at 17:46
  • @n.m. You can't `show` function because of referential transparency etc. And what I want doesn't have to do anything with it. I want to see function name and it's arguments, that's all. – user1685095 Mar 09 '17 at 17:51
  • 1
    To be specific, you can serialize `1+1`, which is just `2`, but neither `(+)` nor `(+ 1)` which are both functions. `2` is not related to the type of either of these functions in any way. – n. m. could be an AI Mar 09 '17 at 17:55
  • If you want to see a function name, serialize a name (a string). You cannot get a function name out of the function itself. – n. m. could be an AI Mar 09 '17 at 17:56
  • @n.m. Okay, see my edit. – user1685095 Mar 09 '17 at 18:02
  • Of course you can return some constant value as the result of `show somefunction`, such as `""` (the same for all functions) but this doesn't seem to be what you want. This constant string may be different for different function types and include some information about its inputs and outputs, such as their types. However you cannot serialize two different functions of the same type to two different strings. – n. m. could be an AI Mar 09 '17 at 18:33
  • @n.m. "You cannot serialize two different functions of the same type to two different strings" is of course false (though admittedly rarely usefully so). But the real problem is that you cannot serialize two identical functions to two different strings; see my answer for details about why that causes a problem. – Daniel Wagner Mar 09 '17 at 18:59
  • @DanielWagner can you show `s:: (Integer->Integer)->String` such that `f == g` is equivalent to `s f == s g`? Your functions `foo` and `bar` could in principle be both serialized to `"\y->8+y"` or equivalent, don't see a problem here. – n. m. could be an AI Mar 09 '17 at 20:08
  • @n.m. "Can you write `s :: (Integer -> Integer) -> String`?" I think no with existing technology. "You could serialize to `"\y->8+y"`." I think a serious answer to this requires some deeper thought. What you can do depends a lot on your equational theory. Each additional equation requires additional normalization. Alpha equivalence requires a canonical choice for names in lambdas; beta equivalence requires evaluating before showing; you might want equations like `let x = foo; y = bar in x === let y = bar; x = foo in x` which would require a canonical `let` ordering (and naming!), etc. – Daniel Wagner Mar 09 '17 at 21:23
  • `allIntegers = [0..]; serializeMe f = fmap f allIntegers` – user2297560 Mar 09 '17 at 22:11
  • @user2297560 yeah, I should have said finite string, infinite strings are not very practical to store. – n. m. could be an AI Mar 10 '17 at 06:39
  • @DanielWagner of course `"\y->8+y"` is not really doable with any technology, but at least there's no problem with referential transparency. – n. m. could be an AI Mar 10 '17 at 06:40
  • @n.m. As I alluded to in my response last time you suggested it: referential transparency (or delta equivalence) is just one of many equational laws that Haskell respects. I don't know how much effort would be required to create something that respects all of the equational laws that people routinely rely on; though I'd be just as interested as you to see a thorough treatment of this idea. – Daniel Wagner Mar 10 '17 at 21:49

2 Answers2

3

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.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • I'm sure my `Typeable` instance exists somewhere on Hackage. If somebody knows, please leave me a comment or go ahead and replace my example instance with a link to it directly. – Daniel Wagner Mar 09 '17 at 18:45
  • Thanks for the explanation, but maybe avoiding boilerplate is possible with template Haskell? If so, than how to do that with aeson? – user1685095 Mar 09 '17 at 20:22
  • @user1685095 I encourage you to give it a shot and open a fresh question detailing where you get stuck. I also encourage you to carefully read through my links to make sure you don't duplicate work others have done already. – Daniel Wagner Mar 09 '17 at 21:25
  • Look, my question is specific. http://stackoverflow.com/q/42707231/1685095 why can't the approach from this answer be automated? With template Haskell or something. This should be possible. – user1685095 Mar 10 '17 at 06:47
  • @user1685095 I'm sorry you're frustrated. I understand that you think you have asked your question several times; but please accept that small variations in a question can lead to big variations in the correct answer, and that as a beginner it is not necessarily possible to predict when that will happen. – Daniel Wagner Mar 10 '17 at 16:45
  • @user1685095 Also, you seem to be under the mistaken impression that we will write your code for you with no effort on your part. That is a rude assumption on your part. If you want to explore using Template Haskell for this, great. We will help. But you have to put in a good faith effort yourself first and come to us prepared with a targeted, specific stumbling block. – Daniel Wagner Mar 10 '17 at 16:46
  • I just thought that if serialization data types is this simple maybe template Haskell approach is also easy. About effort -> I know basics about Haskell, monads etc, but things like Typable, Generics is very new to me. I have a specific problem to solve and I thought that I could learn by example from you guys instead of diving deep into all this right now. – user1685095 Mar 10 '17 at 17:08
2

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)
user2297560
  • 2,953
  • 1
  • 14
  • 11
  • Yeah, that's an obvious solution, which I don't want. eval is just `apply`. I don't see why do I need this boilerplate code. – user1685095 Mar 09 '17 at 17:51
  • 3
    Call me crazy, but I prefer boilerplate over complicated one-off magical solutions. This is explicit and easy to understand. – user2297560 Mar 09 '17 at 17:54
  • @user1685095 "I don't see why do I need this boilerplate code" -- Because you can't serialise a function, but you can serialise a suitable materialised representation of what it does. – duplode Mar 09 '17 at 17:56
  • @user2297560. I don't see anything magical in serializing function object (and function in haskell is object, right? It's called Arrow or something like that). – user1685095 Mar 09 '17 at 17:56
  • @duplode Yeah, okay. Why can't I serialize function object? It has a type right? It's called Arrow or something like that, right? Why function object should be different from any other object? – user1685095 Mar 09 '17 at 17:57
  • @user1685095 Showing some extra information about the function and its inputs and outputs is not the same as serialising it. Cf. [*Can Haskell functions be serialized?*](http://stackoverflow.com/q/17785916/2751851) – duplode Mar 09 '17 at 18:13
  • Yeah, okay. Can show function name and arguments that it's currently applied to? – user1685095 Mar 09 '17 at 18:15
  • @user1685095 The question is what do you do with the function name once you deserialise it. – duplode Mar 09 '17 at 18:16