5

we have mapOptional from the NICTA course:

mapOptional :: (a -> b) -> Optional a -> Optional b
mapOptional _ Empty    = Empty
mapOptional f (Full a) = Full (f a)

When matching f we obviously use that function that was passed, what about the Empty? and what about Full?

raam86
  • 6,785
  • 2
  • 31
  • 46
  • 2
    I'm not really sure what you're asking. – bheklilr Feb 07 '17 at 19:19
  • 1
    @bheklilr: I think the OP wants to know if some sort of singleton is used to reduce the memory burden. In other words if there are 500 `Empty` objects, are these are references to the same. – Willem Van Onsem Feb 07 '17 at 19:20
  • 3
    Apparently, but the very notion of distinct equal objects doesn't really make sense in Haskell. Generally speaking, there is no requirement for _any_ “objects” to ever occur in the program at all, just the result needs to be equivalent. But the actual result value of a function will often be inlined, there is no actual object on the heap there, and for a information-less constructor like `Empty` this would be wasteful too. In actual memory, I reckon the result will be just a single int on the stack, representing either a fixed value that means `Empty` by convention, or a pointer to `Full`. – leftaroundabout Feb 07 '17 at 19:25
  • 1
    @leftaroundabout `Empty` is not informationless, for the same reason `False` and `True` contain information. – Daniel Wagner Feb 07 '17 at 19:26
  • @leftaroundabout thank you, this is the information I was looking for, I hoped this question will have a very different answer than C or Java – raam86 Feb 07 '17 at 19:58

2 Answers2

7

There is nothing in Haskell that lets you observe whether the two Emptys are the same Empty or not, and no guarantees about what an implementation must do with that code in that regard.

That said, in GHC, nullary constructors for a given parameterized type are shared across all parameterizations; so there is just one Empty in the whole program, and just one [], and so forth.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • In fact, I *think* nullary constructors are even shared across types (not just across parameterizations), though I'm not confident enough of that fact to put it in my answer. e.g. I think `False` and `[]` and `Nothing` are all the same. – Daniel Wagner Feb 07 '17 at 19:23
  • If that were true, wouldn't False and True also be the same? That doesn't seem right; there has to be a tag still to identify *which* nullary constructor was used. But after the tag, i can imagine that they all point to the same informationless "object" payload. – amalloy Feb 07 '17 at 19:28
  • @amalloy No; but `True` and `'\1'` might be the same. – Daniel Wagner Feb 07 '17 at 19:28
  • Apparently you can't rely on the same nullary constructor being the same pointer: http://stackoverflow.com/a/24602066/1013393 – sjakobi Feb 07 '17 at 19:29
  • 2
    @sjakobi Fascinating. Answer fixed! I struck out the wrong part of my old answer to emphasize that this part is wrong (I wanted to emphasize it because I suspect I may not be the only person with this wrong belief!). – Daniel Wagner Feb 07 '17 at 19:36
  • 3
    @DanielWagner You're not wrong, you're just missing a nuance. Because of laziness, two suspended computations that yield the same nullary value may be represented by pointers to different thunks. After you force the computations the resulting values will indeed be shared – Benjamin Hodgson Feb 07 '17 at 20:08
  • What @BenjaminHodgson said. I think that text being crossed out is more misleading than it was originally. – Reid Barton Feb 07 '17 at 20:31
  • @BenjaminHodgson Now that I think the discussion on the linked answer has concluded, let me summarize: forcing the computation is not enough. It must be forced *and* a GC must be performed. So there will be times indeed where a program can have many copies of a nullary constructor (presumably with different closure environments but the same code or something like that). I was not missing nuance -- sjakobi was correct to make me change my answer! – Daniel Wagner Feb 07 '17 at 21:24
  • @ReidBarton It turns out what BenjaminHodgson said was not correct! See the discussion on the linked answer for more details. The crossed out text will stay crossed out: it is not correct. The fact that it can fool even seasoned Haskellers like you and I is exactly why I left it visible: to bring awareness to the fact that the claim is fragile and not quite right. – Daniel Wagner Feb 07 '17 at 21:25
  • 1
    It's still not incorrect. The thunk `a` isn't "a copy of a nullary constructor", it's an indirection that was left behind by the evaluation of a thunk. GHC won't ever allocate an `Empty` constructor; it uses the one that was compiled into the library. Why would it ever do otherwise? – Reid Barton Feb 07 '17 at 23:00
  • @ReidBarton Perhaps it won't allocate a new `Empty` constructor; but it has definitely allocated some space for an object that acts just like the `Empty` constructor. I think it's worth bringing attention to that; glossing over it could give the misleading impression that no space is wasted/allocated, which I think is part of the essence of the question being asked. – Daniel Wagner Feb 08 '17 at 00:35
  • The space was allocated for the thunk, and then overwritten with an indirection to the static `Empty` constructor. – Reid Barton Feb 08 '17 at 02:04
2

They can't be the same Empty, the argument has the type Optional a and the output has the type Optional b. When I try to force some sort of reuse, I will typically use something of the type

mapOptional _ a@Empty    = a

This won't compile, and I don't think that's implementation dependent.

trevor cook
  • 1,531
  • 8
  • 22
  • 3
    Well-typed programs can't go wrong. But that doesn't mean that ill-typed programs *must* go wrong. So this claim (not well-typed, therefore the implementation can't work that way) doesn't seem quite right to me. – Daniel Wagner Feb 07 '17 at 19:53
  • Nice way to illuminate the actual difference between the two values, I was looking for these kind of explanations too – raam86 Feb 07 '17 at 19:59
  • @raam86. Thanks. Yes, semantically they are not the same thing. However, as Daniel points out, what is actually going on in memory is another matter. I'll leave the answer as it is, but I think you get the caveat's that go along with it. – trevor cook Feb 07 '17 at 20:19