News for hackers. Stuff that matters.
Re:Safe dead code removal in a pure functional language by Blikkentrekker on Thursday January 28, 2021 @03:39PM (#25948821)
It seems very bad design to begin with to have a function both return a value and be non-transparent in any other way than what it needs in order to return said value.

But in some cases, this is needed for efficiency.

Re:Safe dead code removal in a pure functional language by tsimionescu on Thursday January 28, 2021 @03:49PM (#25948979)
I think about it the other way. If you are doing something effectful, how often is it useful to also return some information?

To me, the answer is 'extremely often', so I don't see the value of what GP is proposing. It's nice to have pure functions and some kind of separation, but it's not worth it to give up returning values from effectful procedures.

Re:Safe dead code removal in a pure functional language by brundolf on Thursday January 28, 2021 @04:13PM (#25949302)
It probably varies by domain - most of my deep experience is on the front-end - though even outside of that, I can't think of many cases outside of errors where I fundamentally need to return something in tandem with a side-effect. The return value is either going to pertain to the state before the side-effect, the state after the side-effect, or the side-effect itself. The first two could easily be broken out into pure functions. The third, again, mostly comes down to error reporting.

I guess things get more complicated when you consider I/O. A function that gets a file handle or makes a GET request is technically impure and mutates the state of the macro-system, and yet is primarily concerned with what it returns. I tend to lump these into the "plain functions" bucket, but it's a gray area.

Re:Safe dead code removal in a pure functional language by magicalhippo on Thursday January 28, 2021 @05:17PM (#25950222)
We're in the procedural world, but a typical case we have is a variation on this theme:

A function takes some declaration id, generates one or several records based on the declaration data, commits them to the database and returns the record id's so they can be used in the UI or similar.

Either the record id's are displayed to the user directly, they're used for filtering in a grid, used for generating response files to external system, or something else.

Often this would get called several times for the same declaration id, usually generating new records, so it's easier to have the function return the id's rather than trying to reconstruct what it did.

Could be there's an easy way to map this to pure functions, I haven't thought about it. Would be interested to know though.

Re:Safe dead code removal in a pure functional language by brundolf on Thursday January 28, 2021 @05:58PM (#25950744)
Where my mind initially goes is to have a pure function going from id -> records, and then pass those records off to a separate procedure that persists them to the database. The same records can then also be passed around to the UI or whatever else.

> Often this would get called several times for the same declaration id, usually generating new records, so it's easier to have the function return the id's rather than trying to reconstruct what it did.

Are the "new" records meaningfully different in some way, or are they just new instances in memory of what is fundamentally the same value? In the former case, there's state somewhere that's causing each one to be different (there is not a pure relationship from id -> record set). In the latter case, you don't actually need multiple instances (unless you expect them to be mutated by other code down the line and are guarding against side-effects, in which case you could defensively clone the pure function's result as needed instead of calling it multiple times).

Edit: it's possible I misunderstood, and that what's happening is you're inserting data into a database which has its own auto-incrementing IDs that it then returns to you. If so, the coupling between value creation and state/mutation exists in the database's API itself, and so is outside of your control. There's not much you can do in that case because you don't own that code.

Re:Safe dead code removal in a pure functional language by magicalhippo on Thursday January 28, 2021 @06:19PM (#25950946)
Thanks for the response, I enjoy challenging my procedural upbringing.

> Are the "new" records meaningfully different in some way

Yeah usually. A typical example is something external added data to the declaration, and new records are generated to reflect this.

And yeah, auto-incs is the norm.

Re:Safe dead code removal in a pure functional language by brundolf on Thursday January 28, 2021 @06:41PM (#25951147)
Yeah the overall mindset is just to look for pieces of the form "convert values A..X into value Z", which can be lopped off into a pure function. Wherever you can do this, you probably want to.

But this generally only works if all of the information going into Z is under your code's purview. If the auto-increment were happening in your code instead of in the database then you could make the records a pure function of the domain data + the next auto-incremented ID, and the procedural code which calls the pure function would retrieve the ID and increment it, call the pure function with it to generate the records, and then call another impure function to persist them. But of course moving the auto-increment logic and state from the DB to your application code is a huge undertaking involving many other trade-offs, and is not necessarily recommended just for the sake of something like this.

Re:Safe dead code removal in a pure functional language by Akronymus on Friday January 29, 2021 @05:23AM (#25955899)
>...clone the pure function's result as needed instead of calling it multiple times

It is reasonably easy to do a caching function that takes arbitrary functions and data and caches their result as a map of tuples (data, function, timestamp for example). Definitely not always the right thing to do, but could eek out some performance in where you get the same data multiple times.

Edit: I found that it is called memoization: Looks to me to very easily be extensible to allow timestamps too, to check if the record is older than X.

https://stackoverflow.com/questions/833180/handy-f-snippets/...

Re:Safe dead code removal in a pure functional language by brundolf on Friday January 29, 2021 @10:37AM (#25958306)
Yes, but:

> unless you expect them to be mutated by other code down the line and are guarding against side-effects

Sometimes you hand off a mutable object to some other code and that code mutates it for its own purposes. If you hand the same object to multiple functions in this case, they'll all see each other's changes and if this wasn't intentional then voila, you've got a bug. This is one motivation for immutable data structures: you don't have to worry about whether an object might get mutated so you don't have to eagerly clone it. But in an existing codebase it may not be a trivial thing to simply introduce immutability. This is why, instead, I suggested making that defensive cloning explicit and decoupled from the rest of the logic.

Re:Safe dead code removal in a pure functional language by Akronymus on Monday February 01, 2021 @02:35AM (#25986455)
In that case, you could adjust the memoize function to return a copy. Altough that would lead to other problems such as shallow or deep copy and such.
Re:Safe dead code removal in a pure functional language by millstone on Thursday January 28, 2021 @10:40PM (#25953550)
Consider atomically incrementing a variable. You need to both perform the increment, and get the value.
Re:Safe dead code removal in a pure functional language by brundolf on Friday January 29, 2021 @12:43PM (#25960060)
Yeah, someone else pointed out the async case. I think I just haven't done a ton of async programming so it didn't register on my radar as a major example of this, but I can see how it intrinsically couples value-creation and value-mutation together.

The crux is that you can't deterministically know how the operation will go until you try it, and you therefore have to return some information about the operation itself which can't just be derived in a pure way from the state of things before or after the operation.

Re:Safe dead code removal in a pure functional language by Blikkentrekker on Thursday January 28, 2021 @04:08PM (#25949228)
Only to return information about the effect's success, which would again make it easy to see that the function must have some effect, if all it returns be true or false or another trivial value that could be constructed from æther.
Re:Safe dead code removal in a pure functional language by tsimionescu on Friday January 29, 2021 @01:56AM (#25954806)
I think a general case is concurrent mutation of a data structure. If you are adding some item to a list in parallel with others, you will usually need to return some token (e.g. index) that will allow your caller to retrieve that item later.

You MAY be able to get away with making this a pure function by returning a shallow copy, but I believe that depends on how central the sharing aspect is.

Re:Safe dead code removal in a pure functional language by brundolf on Friday January 29, 2021 @12:31PM (#25959894)
Yeah, so the problem here is that the value creation logic is non-deterministic; not only does it depend on hidden state, it doesn't even do so predictably because you don't know the order in which other threads might interplay. I established three categories of imperative-logic-return-values above:

> The return value is either going to pertain to the state before the side-effect, the state after the side-effect, or the side-effect itself. The first two could easily be broken out into pure functions. The third, again, mostly comes down to error reporting.

So this definitely falls into category three, which makes the coupling legitimate. It's possible I was wrong in my statement that that category "mostly comes down to error reporting"; it was just based on my own past experience.