Can Programming in Julia Be "Pure"?

Hi all,

I am learning about pure functions. The definition I have is:

A pure function is one in which the same result is always produced with no side effects given the same input.

This comes from Category Theory for Programmers by Milewski.
The context of the discussion was about the difference between mathematical functions (i.e. how one value is mapped to another) and programming functions.
Particular to this discussion was also on Haskell’s position where all functions written within it are pure functions.

In that vein, what I was wondering is if Julia can be considered a “pure” programming language?
Edit: Can programming in Julia be “pure”?
Let me give an example:

In Haskell, one example of a pure function would be the factorial function:

fact x = product [1..n]

Which when translated to Julia is:

fact(x) = (*).(1:x...)

So, in this sense, are both of these functions pure?
To me, as the Julia implementation does not invoke side effects (that I am aware of), it appears to be pure.
Or am I misunderstanding how Julia computes functions?

Thanks all!

~ tcp :deciduous_tree:

1 Like

What I think I am missing in my understanding is what does “pure” even mean in the context of programming?
I was under the impression that pure functions was the name of a group of functions but it seems more like “pure” is a bigger definition than I was aware of.

Furthermore, in terms of side effects, was how I understood them was that if I used my fact functions, for example, the only thing that should happen is a direct mapping of, say, fact(3) to the value of 6.
Nothing else in this implementation should occur.

CC @kristoffer.carlsson and @Mason - trying to clarify a bit more on my understanding but I think the definitions I was provided were a bit too vague.
Is there better/reasonable language I can use here?

I don’t think in any of the definition of a pure language would Julia itself be considered a pure (or functional) language. It is an imperative language and makes heavy use of e.g. mutation. You can of course write pure (for some definition of the word) functions, say using e.g. map and filter.

8 Likes

Ah ok - that makes sense.
Just to make sure we are using the same vocabulary, in your answer here, how are you defining “pure”?
Would my example of fact (written in Julia) fall under your definition for a “pure” function?

Sorry for the ignorance, why map and filter have anything to do with this? Isn’t any function that 1) does not mutate input parametes; 2) does not use global variables, pure? What is the concept that must be further studied here to understand why you mention map and filter in this context?

2 Likes

Yes. It doesn’t mutate anything and doesn’t have any side effects.

map and filter (and reduce) etc are just very common building blocks in writing functional code. Here’s some article I just googled: https://www.analyticsvidhya.com/blog/2021/09/the-power-of-python-map-reduce-and-filter-functional-programming-for-data-science/

3 Likes

Wonderful!
Thanks for the explanation @kristoffer.carlsson - this was super helpful.
Also, thanks for asking the additional question @lmiq as I was also wondering the same thing.

As a result of this discussion and wanting to improve discoverability, I am slightly changing the discourse post title to better reflect what we discussed.

1 Like

You can write functions in Julia that are “probably” pure, like this:

myfunc(x, y) = x + y

But there’s no guarantee that myfunc is actually pure, because it depends on the various + methods that have been implemented. It’s possible to implement an unpure +(::Foo, ::Bar) and then if you call myfunc on a Foo() and a Bar(), then your myfunc is no longer pure.

8 Likes

Thanks @CameronBieganek for this perspective as well.
I am realizing that additionally, “pure” in a programming sense seems rather vague.
This is a really good perspective that I wasn’t really considering.

Programming purity seems quite complex the more you delve into it.
I like the notion of “probably” pure as I think it encapsulates a more pragmatic approach to the language internals of Julia.
It lets one get away without having to necessarily be completely aware of all the internals of Julia when discussing purity.

2 Likes

There is always an idealization going on. If you call pure Haskell but in such a way as the computation requires more RAM than is available the Linux kernel will kill the process, or maybe reap another process… This is a side effect. Because of that side effect perhaps your robot falls off a cliff and catches fire. Then the result of your Haskell code is not deterministic or Pure.

2 Likes

Referential transparency also helps the compiler (and user) reason about what exactly ‘=’ means, This is useful not only for various compiler optimizations, but also for implementing various types of ‘higher order’ semantics, mathematical or not.

The (free) book Program Design by Calculuation goes into this: https://www4.di.uminho.pt/~jno/ps/pdbc.pdf

In the section: 2.3 FUNCTIONAL EQUALITY AND COMPOSITION (page 18).

Note that you do not need a purely functional language to accomplish similar things, either by inference, lifting, or other language mechanics, implicit or explicit.

Idiomatic Julia style mixes functions with and without side-effects. The former are easier to reason about and lead to cleaner code, while the latter can be very convenient in practice for some problems.

The right question is not whether a practical nontrivial Julia program can be fully “pure”, but whether it would be advantageous. The answer is that in most situations it does not make sense, because you would be working against the language.

4 Likes

And yet, in situations where it is necessary to reason about a fairly complicated algorithm and referential transparency is needed, it is possible to program in such a style and avoid complexity as Julia fully supports this style.

Interestingly enough, clojure invented the concept of transient data, which is intermediately mutable, but presents itself as immutable afterwards .

1 Like