These are chat archives for ramda/ramda

29th
May 2017
Michael Zinn
@RedNifre
May 29 2017 18:19
Hi. Is it just me or is there no count(Predicate<? super A>, Array<A>) in Ramda?
Bijoy Thomas
@bijoythomas
May 29 2017 18:58
Isn't that compose(length, filter) ?
Michael Zinn
@RedNifre
May 29 2017 18:59
sure, but compose(length, filter)(isValid, entries) doesn't look as nice as count(isValid, entries)
Simon Friis Vindum
@paldepind
May 29 2017 20:34
@i-am-tom But (() => console.log(‘hi’)) is still an impure program. The outer expression doesn't have any side-effects. But subexpressions in the program do. So as a whole the code isn't pure. I think it's an important distinction to make. Otherwise one could just wrap an entire program in a function an call it pure. One way to solve the problem is seen in the IO library. It has a function called withEffects that turns an impure function into a function that returns an IO. Then one can write code where no sub-expression calls an impure function and thus where every part of ones program is pure and referentially transparent.
Tom Harding
@i-am-tom
May 29 2017 20:37
@paldepind No, it’s a perfectly pure program. It would only be impure if that function were executed. IO works with thunks in exactly this way; we “record” the computation as a closure, but don’t execute it. At the end, we execute the composed function, and the impurities are carried out. The exact program you pasted is totally pure as it has no side-effects (by the traditional definition), and IO uses this same definition of purity to compose IO. The program that actually logs to the console is a fundamentally different program, which, you’re right, is impure
Simon Friis Vindum
@paldepind
May 29 2017 20:49
@i-am-tom A program isn't pure just for having no side-effects. let a = 1; a = 2doesn't have any side-effects either but it's still an impure program. In a purely functional language every single expression in the program is pure and satisfies referential transparency. For (() => console.log(‘hi’)) that is not the case because the sub-expression console.log(‘hi’)is impure.
A pure program is composed of pure expression. If some sub-expression in the program isn't pure then the sum of it can no longer semantically be understood as a pure. When programming with the IO monad in Haskell there is never a single impure sub-expression. That makes a huge difference. Again, I just want to point out that the JS library IO neatly solves this problem by including a withEffects function that ensures that one can create pure descriptions of impure actions without ever writing a single impure expression.
Tom Harding
@i-am-tom
May 29 2017 20:53

There’s some conflation here; the a program isn’t necessarily impure (certainly “shadowing” is the clear answer). In any purely-functional language, IO must be a contained impurity for us to do anything useful. If we want to be totally strict with it, there are zero purely-functional languages. From Haskell:

Prelude> import System.IO.Unsafe 
Prelude System.IO.Unsafe> unsafePerformIO $ putStrLn "test" >> pure 2
test
2

Underneath, all Haskell does is store impurities in closures. It’s still an impure sub-expression, but it doesn’t get executed within the program’s body, so we call it “pure"

Simon Friis Vindum
@paldepind
May 29 2017 20:54
@i-am-tom The impure sub-expressions are hidden inside the IO structure in a way that never leaks into user code. I'm not sure what the point is regarding unsafePerformIO. That function is impure.

certainly “shadowing” is the clear answer

It's not shadowing. It's mutating a variable.

Tom Harding
@i-am-tom
May 29 2017 20:56
Yes, and it’s a function that exists inside a language you’re considering pure. At the end of the day, the “IO” structure is syntactical sugar over a thunk. In fact, pure languages like PureScript use exactly a thunk to describe an IO action
That depends; if the change of value affects dependent computations, then there is a side-effect. However, you specified that this wasn’t the case, so this is simply updating a name, which has no effect on outside code, so it achieves the same thing, despite JS’ semantics
Simon Friis Vindum
@paldepind
May 29 2017 20:59
I agree that you can consider IO being sugar around a thunk. But that is just an implementation detail. The difference is that those thunks only exist inside the language implementation and not inside user code.
Tom Harding
@i-am-tom
May 29 2017 21:02
I… am not sure the distinction is worthwhile. The suggestion there is that purity is syntactical (if we’re agreeing that, eventually, pure programs will perform impure actions in order to be useful). I guess my question would be that, if I wrote a program to append “;main()” to my JS entry point file, would the unprocessed program be sufficiently pure to satisfy your definition?
Simon Friis Vindum
@paldepind
May 29 2017 21:09
I think the distinction is useful. Because it matters with regards to what properties the code has. If some expressions in the program are impure then those can't be reasoned about in the same way that a completely pure program could.
For instance if (() => console.log(‘hi’)) was completely pure and satisfied referential tranperancy I could refactor it into this const m = console.log(‘hi’); (() => m). But the refactor fails? Why? Because a subexpression is impure. This hinders how a program can be reasoned about because I have to be aware of the impurity to know that such a refactor isn't safe.
Tom Harding
@i-am-tom
May 29 2017 21:12
It’s worth reading some critiques of that; http://degoes.net/articles/modern-fp starts off with a reasonable motivation. IO is a black box that, in isolation, isn’t as safe as we’d like to imagine. Abstracting the IO-returning computation is where we can create code that invites reasoning; once stuff is in the black box, it’s impure and thus “unreasonable"
Ah, there’s the rub: as JavaScripters, we have to treat IO thunks as a special case. the IO library absolutely does not solve this problem; I should be able to call the impure function before wrapping it and get the same result, no? Either way, we’re still relying on the fact that () => impurity is an irreducible unit :(
Simon Friis Vindum
@paldepind
May 29 2017 21:15
@i-am-tom I've read it. It's a very good blog-post :smile:
Tom Harding
@i-am-tom
May 29 2017 21:15
^_^
Simon Friis Vindum
@paldepind
May 29 2017 21:18
@i-am-tom Did you look into the IO library I linked? I think the withEffects function mostly solves the problem. Because when using that function to wrap impure functions we never have to call the impure functions. For instance I can do const logIO = withEffects(console.log). Then I never have to write any code that calls console.log instead I can call logIOwhich is a pure function.
Tom Harding
@i-am-tom
May 29 2017 21:20
const currentTime = withEffects(Date.now); apply the same reasoning as the above refactor and see that the “purifying” isn’t referentially transparent; it’s still just a thunk.
Simon Friis Vindum
@paldepind
May 29 2017 21:20

the IO library absolutely does not solve this problem; I should be able to call the impure function before wrapping it and get the same result, no?

You may be right but I don't fully understand your point?

I don't see the problem in the currentTime example? :confused:
Tom Harding
@i-am-tom
May 29 2017 21:24
At some point, the effect will be executed and Date.now() will be called. This is not guaranteed to be the same result as me calling Date.now() back at the start, because the computation has been delayed. runIO is an impurity that can’t be avoided, as it is in every language. “Pure" is a word we use for our best fit
Simon Friis Vindum
@paldepind
May 29 2017 21:26
Yes, Date.now will be called runIO is impure. But as far I can tell const currentTime = withEffects(Date.now) satisfies referential transparency?
Tom Harding
@i-am-tom
May 29 2017 21:27
only for the same reason that () => console.log(‘hi’) does - it doesn’t actually do the thing it describes
Simon Friis Vindum
@paldepind
May 29 2017 21:28
Yes. I completely agree. But the key difference to me is that () => console.log(‘hi’) contains an impure subexpression where as withEffects(Date.now) does not.
Simon Friis Vindum
@paldepind
May 29 2017 21:29
Yes. But the thunk is hidden away inside the library. Not present in user code.
Tom Harding
@i-am-tom
May 29 2017 21:29
Unless the point here is that we hide the violation behind a library, which I do understand - it’s basically what Haskell does
Simon Friis Vindum
@paldepind
May 29 2017 21:29
Exactly :smile:
Tom Harding
@i-am-tom
May 29 2017 21:29
Aha. There’s my problem, apologies
So we’re agreed that withEffects(Date.now) violates referential transparency, but not in a way that is superficially visible to users
(i.e. there’s an impure sub-sub-expression, per se)
Simon Friis Vindum
@paldepind
May 29 2017 21:31
Hm. Can you give an example of how it violates referential transparency?
Btw, withEffects is supposed to be used to get rid of the references to the impure functions an "turn them into pure ones" so that those don't have to be mentioned in the rest of the program.
Tom Harding
@i-am-tom
May 29 2017 21:32

Yes. But the thunk is hidden away inside the library. Not present in user code.

Almost any execution of the thunk? The explicit withEffects(Date.now), as you say, hides the referential transparency - doesn’t mean it isn’t still there underneath

Simon Friis Vindum
@paldepind
May 29 2017 21:33
I don't think so? Date.now is just a constant and withEffects is a pure function.
Tom Harding
@i-am-tom
May 29 2017 21:34
But that expression also achieves nothing useful inside a purely-functional program, i.e. one without runIO. I think we might be splitting hairs at this point :p
The single atomic unit () => console.log(‘hi’) is just a constant, after all
Simon Friis Vindum
@paldepind
May 29 2017 21:35
Yes. When you call runIO then that expression is impure. But the impurity is confined to that single expression.
Tom Harding
@i-am-tom
May 29 2017 21:36
As any effectful thunk’s is confined to its execution
Simon Friis Vindum
@paldepind
May 29 2017 21:36
By using IO I can write an entire program where runIO is the only impure part. Every single other expression will be pure and referentially transparent.
Tom Harding
@i-am-tom
May 29 2017 21:37
By using thunks, I can accomplish the exact same thing, where main() is the only impure part? It’s syntax
Simon Friis Vindum
@paldepind
May 29 2017 21:37
The code inside your explicit thunks wont satisfy referential transparency?
Tom Harding
@i-am-tom
May 29 2017 21:38

The code inside isn’t separable; the whole thunk forms the atomic unit

const purify = f => (…args) => () => f(… args)

Still just thunks, however we dress it up

Simon Friis Vindum
@paldepind
May 29 2017 21:40
But PureScript wouldn't be pure if those thunk we're written inside user code and not inside the Eff implementation.
Tom Harding
@i-am-tom
May 29 2017 21:40
The only reason the user remains unaware is syntax. Mechanically, it’s just thunks. Whether your code exposes you to it or not, the impurity is there and it is just as difficult to reason about as any other IO implementation
PureScript isn’t pure - if it were, it would be totally useless
Simon Friis Vindum
@paldepind
May 29 2017 21:43
@i-am-tom It's not just syntax. I think it's semantics. PureScript didn't have to implement it with thunks. They could also compile Eff code down into imperative JS. It's an implementation detail.
Why do you think PureScript isn't pure?
I think we agree that at some point side-effects will happen. I just think describing them with pure code is nice than with explicit thunks that wrap impure code. And I think doing that is quite essential to the purity of PureScript.
Tom Harding
@i-am-tom
May 29 2017 21:46
They couldn’t compile it to imperative JS exactly for semantic reasons - it would change the semantics of the program. There are other options - the imperative code could be stored as a string, for example. Your use of the word “explicit” before thunks is exactly my point - neither approach is “purer”, but one is syntactically nicer. At the end of the day, code with side-effects is impure. Those impurities might be well-controlled, but they are impurities nevertheless
Simon Friis Vindum
@paldepind
May 29 2017 21:48

They couldn’t compile it to imperative JS exactly for semantic reasons - it would change the semantics of the program.

Interesting. Why not?

I think you are right that it's only syntactically nicer. But that niceness is important to preserve referential transparency in the code that describes the impurity.
Tom Harding
@i-am-tom
May 29 2017 21:52
For example, unsafePerformEff $ for [1,2,3] logShow is semantically different to for [1, 2, 3] (pure <<< unsafePerformEff <<< logShow), though the latter would compile down to for (i in [1,2,3]) console.log(i); I’ll dig out the JS denotational semantics for a more coherent example later in the week, if you’d like
(Crude example; trying to go for the batch-vs-series demo, but there’s too much ugly composition for it to be clear)
Simon Friis Vindum
@paldepind
May 29 2017 22:00

I don't understand the difference in the example :confused: I'll be heading to bed now. Thank you for the conversation. I think it's tricky to talk about purity in a language like JS but you had some good point. I apologize if I wasn't very good at getting my point across but I hope it's clear why I like hiding the thunks in a library.

I’ll dig out the JS denotational semantics for a more coherent example later in the week, if you’d like

I'd love to see that :thumbsup:

Tom Harding
@i-am-tom
May 29 2017 22:01
:) G’night. Yeah, always lovely to chat about these things, and I totally agree that hiding the ugly bits is better for everyone, hah! Talk soon :)