3 ms·
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 t
by elbear 2y ago
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.
- Dylan16807 2y agoI 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
- elbear 2y agoOk, 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.
- Dylan16807 2y agoEven without laziness, you can get similar problems if f creates a closure or returns something that includes the parameter object.
- trealira 2y agoYeah, this is a common source of confusion with closures in Python. Example on Stack Overflow: https://stackoverflow.com/questions/233673/how-do-lexical-closures-work https://stackoverflow.com/questions/233673/how-do-lexical-cl...
- elbear 2y agoDoesn'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.
- trealira 2y agoIt'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.
- elbear 2y agoOk, 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.
- elbear 2y agoOk, 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 https://en.wikipedia.org/wiki/Pure_function