These are chat archives for ramda/ramda
(() => 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
withEffectsthat 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.
IOworks 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
IOuses 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
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
withEffectsfunction that ensures that one can create pure descriptions of impure actions without ever writing a single impure expression.
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"
unsafePerformIO. That function is impure.
certainly “shadowing” is the clear answer
It's not shadowing. It's mutating a variable.
(() => 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.
IOis 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"
() => impurityis an irreducible unit :(
withEffectsfunction 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.loginstead I can call
logIOwhich is a pure function.
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.
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?
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.
runIOis an impurity that can’t be avoided, as it is in every language. “Pure" is a word we use for our best fit
Date.nowwill be called
runIOis impure. But as far I can tell
const currentTime = withEffects(Date.now)satisfies referential transparency?
() => console.log(‘hi’)does - it doesn’t actually do the thing it describes
() => console.log(‘hi’)contains an impure subexpression where as
withEffects(Date.now)violates referential transparency, but not in a way that is superficially visible to users
withEffectsis 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.
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
Date.nowis just a constant and
withEffectsis a pure function.
runIO. I think we might be splitting hairs at this point :p
() => console.log(‘hi’)is just a constant, after all
runIOthen that expression is impure. But the impurity is confined to that single expression.
runIOis the only impure part. Every single other expression will be pure and referentially transparent.
main()is the only impure part? It’s syntax
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
Indeed, this is exactly how PureScript accomplishes “purity"
They couldn’t compile it to imperative JS exactly for semantic reasons - it would change the semantics of the program.
Interesting. Why not?
unsafePerformEff $ for [1,2,3] logShowis 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
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: