Hacker News new | past | comments | ask | show | jobs | submit login

Is there a benefit if you're already familiar with writing functions like that? Is it wrong for me to expect that most programmers are already familiar with functions that only use their inputs, but treat that style as significantly more optional?

I wrote pure functions for a minute there but that's not the same, a function that only uses its inputs can modify an object while a pure function would have to return a new object. But, similarly, I bet that a lot more people know about pure functions than have any working knowledge of Haskell.




It seems you only focused on one of the conditions I mentioned.

You have to follow both rules: the one about inputs and the one about outputs.

This is like a contract. If you enforce it throughout your program, you gain some guarantees about your program as a whole.


I was looking at both rules, and specifically I was using the long version where you said "it doesn't assign them to some other global variable that you have to track down". If you pass in a mutable object then that's not "some other global variable".

If I interpret "It only outputs its results" in a very strict way, that still allows having output and in/out parameters. The latter of which can break purity.

Though you can break purity with just inputs:

  define f(o): return o.x
  let a = {x=1}
  f(a)
  a.x = 2
  f(a)
If you meant to describe pure functions then that's fine, that's why I addressed pure functions too, but I don't think your original description was a description of pure functions.


So, another definition of a pure function is that, for a particular input it will always return the same output.

Your example respects the rule:

    f({x=1}) == 1
    f({x=2}) == 2
But it's true that the two rules I gave are not enough to make a function pure. Because I didn't say anything about I/O. So, a function that follows the rules about inputs and outputs, could still do I/O and change its outputs based on that.

Starting from the question that gave birth to this whole thread: "What's the benefit of learning a PURE functional programming language..."

The other benefit is that such a language forces you to be explicit about I/O. It does it in such a way that even functions that do I/O are pure. The good part is that, if you use it long enough, it can teach you the discipline to be explicit about I/O and you can use this discipline in other languages.

For example, this is how I see this principles being used in Python:

https://elbear.com/functional-programming-principles-you-can...


> Your example respects the rule:

Every definition of purity I can find that talks about objects/references says that if you pass in the same object/reference with different contents then that's not pure.

Your version differs from mine on that aspect. It passes two unrelated objects.

> Starting from the question that gave birth to this whole thread: "What's the benefit of learning a PURE functional programming language..."

I interpret saying a language is "purely functional" as being more about whether you're allowed to write anything that isn't functional. I can talk about BASIC being a "purely iterative" language or about "pure assembly" programs, without any implication of chunks of code being pure.


I gave it some more thought.

I now believe that learning a language like Haskell (or Elm or PureScript) forces you to see your program as pipes that you fuse together.

It's not just functions. Haskell has only expressions and declarations. That means, for example, that you are forced to provide an `else`, when you use `if`. The idea is that you have to keep the data flowing. If a function doesn't provide a meaningful value (so it returns nil, None), you have to handle that explicitly.

And, btw

> Your version differs from mine on that aspect. It passes two unrelated objects.

Those two objects are not unrelated. They have the exact same structure (an attribute named "x"). So they could be considered two values of the same type.


I mean that the identity is unrelated. Yes, you can say they're the same type. But I'm actually passing the same object in. If f evaluated lazily, it could return 2 from both calls. Something like:

  define f(o): return o.x
  let a = {x=1}
  n = f(a) // n is not evaluated yet
  a.x = 2
  m = f(a)
  return n + m // returns 4


Ok, you're probably proving the point that purity also requires immutability. I'm not sure, as I haven't considered all the implications of Haskell's design.

My two rules about inputs and outputs are more like heuristics. They can improve code organisation and probably also decrease the likelihood of some errors, but they don't guarantee correctness, as you're pointing out. They're shortcuts, so they're not perfect.

Edit: If I remember right, it's laziness that requires immutability. I think I read something about this in the Haskell subreddit as an explanation for Haskell's design.


Even without laziness, you can get similar problems if f creates a closure or returns something that includes the parameter object.


Ok, the Wikipedia definition of pure function is more strict and than what I was saying and I think it covers the issues you mentioned:

https://en.wikipedia.org/wiki/Pure_function


Yeah, this is a common source of confusion with closures in Python. Example on Stack Overflow:

https://stackoverflow.com/questions/233673/how-do-lexical-cl...


Doesn't that example also show a kind of laziness?

I say this because the second solution to that question offers the solution of using `i` as a default argument when defining the function. That forces its evaluation and fixes the problem.


It's just a name shadowing.

Copying the code they wrote:

  for i in xrange(3):
      def func(x, i=i): # the *value* of i is copied in func() environment
          return x * i
      flist.append(func)
That could also be written "def func(x, foo=i): return x * foo". It's just copying i's value to another variable. In the next line, i's value is still 1, 2, or 3, so when the function is called during the next line of the body of the loop, the value held by i is bound to foo.

It's not evaluating a thunk representing i, which is how lazy variables are evaluated in Haskell.


Ok, I had a better look at the code and I realised that it doesn't follow the rule I was talking about, namely having the function only work on values it receives as inputs.

I think that's why I don't use closures, because they read values from the environment. Their only use case (that comes to mind) can be solved with partial application, which is safer.

Oh, and I wasn't using laziness in the Haskell sense, but more in the general sense of deferring evaluation.


Purely functional language is pretty universally taken to mean that the language enforces function purity for all functions [perhaps with some minor escape hatches like Haskell's unsafePerformIO].


> Is it wrong for me to expect that most programmers are already familiar with functions that only use their inputs

They'll experience no friction when using Haskell then. Haskell only refuses to compile when you declare "Oh yeah I know functions from other languages this is easy" but then do some mutation in your implementation.


> They'll experience no friction when using Haskell then.

The question was what benefit you'd get from learning a functional language, though. Existing knowledge making it easier to switch to a functional language is the inverse of that.

And there's no assumption they'll actually be making things in Haskell, so easy switching isn't by itself a benefit.


Yeah I can't really follow these threads.

I saw:

> What's the benefit of learning a PURE functional programming language, opposed to just using a language which has adapted the best bits and pieces from the functional programming paradigm?

I also saw:

  Though you can break purity with just inputs:

  define f(o): return o.x
  let a = {x=1}
  f(a)
  a.x = 2
  f(a)
I don't know if that's the tail-end of a reductio ad absurdum which is trying to demonstrate the opposite of what it stated. Either way, to be clear, the above would be rejected by Haskell (if declared as a pure function.)

I guess if you learn a functional language "which has adapted the best bits and pieces from the functional programming paradigm" then you might think that the above is broken purity, but if you learn a "PURE functional programming language" then you wouldn't.


The topic is what you would learn from a pure functional language.

A) You can learn and enforce full purity in other languages. B) You could also learn and adapt just the idea of clean inputs and outputs to those other languages.

Both of those are valid answers! It's very hard to be completely pure if you're not currently using Haskell.

The way they worded things, I wasn't sure which one they meant. They were describing option B, but I didn't know if that was on purpose or not.

So I responded talking about both. Complete purity and just the idea of clean inputs and outputs.

That code snippet is not some kind of absurd argument or strawman, it's there to demonstrate how the description they gave was not a description of purity. It's not aimed at the original question.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: