[racket] Why functional?

54 views
Skip to first unread message

Lawrence Bottorff

unread,
May 22, 2013, 12:24:47 PM5/22/13
to users
I posted the following question at stackoverflow and got 3 pluses and a star (favorite question) -- although only one response. I'd really like to clear these issues up in my mind. Here goes:

----

I've read some of the discussions here, as well as followed links to other explanations, but I'm still not able to understand the mathematical connection between "changing state" and "not changing state" as it pertains to our functional programming versus non-FP debate. I understand the basic argument goes back to the pure math definition of a function, whereby a function maps a domain member to only one range member. This is subsequently compared to when a computer code function is given certain input, it will always produce the same output, i.e., not vary from use to use, i.e.i.e., the function's state, bzw. its domain to range mapping behavior, will not change.

Then it get foggy in my mind. Here's an example. Let's say I want to display closed, block-like polygons on an x-y field. In typical GIS software I understand everything is stored as directed, closed graphs, i.e. a square is four "vectors," their heads and ends connected. The raw data representation is just the individual Cartesian start and end points of each vector. And, of course, there is a "function" in the GIS software that "processes" all these coordinate sets. Good. But what if we represent each polygon in a mathematical way, e.g., a rectangle in the positive x, negative y quadrant might be:

Z = {(x,y) | 3 <= x <= 5, -2 <= y <= -1}

So we might have many Z-functions, each one expressing an individual polygon -- and not being a whiz with my matrix math, maybe these "functions" could then be represented as matrices . . . but I digress.

So with the usual raw vector-data method, I might have one function in my code that "changes state" as it processes each set of coordinates and then draws each polygon (and then deals with polygons changing), while the one-and-only-one-Z-function-per-polygon method would seem to hold to the "don't change state" rule exactly -- or at least not change memory state all that much. Right? Or am I way off here? It seems like the old-fashioned, one-function-processing-raw-coordinate-data is not mutating the domain-range purity law really. I'm confused.... But the many individual polygon Z-functions method would seem to hold a perfect picture of state in memory -- no? Then I was reminded of my days (1980s) in Cartography when an object-oriented GIS package (written in Smalltalk) was being discussed. Everyone dismissed it as ludicrous because it tried to have exactly that: all the cartographic objects (roads, areal, symbols, polygons, etc.) in live memory at once. But isn't this ultimately what FP wants?

Another avenue of my inspiration came from reading about a new idea of image processing where instead of slamming racks of pixels, each "frame" would be represented by one big function capable of quasi "gnu-plotting" the whole image: edges, colors, gradients, etc. Now I ask, Is this germane? I guess I'm trying to fathom why I would want to represent, say, a street map of polygons (e.g. city blocks) one way or the other. I keep hearing functional language advocates dance around the idea that a mathematical function is pure and safe and good and ultimately Utopian, while the non-FP software function is some sort of sloppy kludge holding us back from Borg-like bliss.

But again, more confusing is memory management vis-a-vis FP versus non-FP. What I keep hearing (e.g. parallel programming) is that FP isn't changing a "memory state" as much as, say, a C/C++ program does. Is this like the Google File System (or my Smalltalk GIS example) where literally everything is just sitting out there in a virtual memory pool, rather than being data moved in and out of databases, bzw. memory locations? Somehow I sense all these things are related. Therefore, it seems like the perfect FP program is just one single function (possibly made up of many sub-functions like nesting Russian dolls) doing all tasks -- although a quick glance at any elisp code seems to be a study of programming schizophrenia on this count.

----

It would be nice to hear some of your thoughts on this.

LB

Matthias Felleisen

unread,
May 22, 2013, 1:57:29 PM5/22/13
to Lawrence Bottorff, users

Hi Lawrence,

your little essay is fascinating and intriguing from the perspective of 'ideals' -- as in ideal FP and ideal OOP etc. I am sure 'philosophers' of programming languages with a sufficient technical understanding of your example could have a field day.

The sentence concerning 'elisp' in your last paragraph worries me, however. Elisp -- like any Lisp, say Common Lisp or Racket -- is not a functional language in the purist's point of view. It supports direct, uncontrolled, and full-powered effects wherever and whenever a programmer wants it. Meaning, looking at Lisp code will not really help you tease out answers to your deep questions. Indeed, if we were to answer these questions here, I am sure we would soon get into the fine details of when to use side-effects in Racket.

Let me suggest an alternative then. Haskell is the language that most people consider the purest functional language of all. It does not even allow real exceptions to ensure that programs have certain mathematical properties that a 'fundamentalist mathematician' might argue they should have. The kind of question that you raise will fascinate the Haskell purists, and so I propose that you ask your question on their mailing lists.

Good luck -- Matthias





On May 22, 2013, at 12:24 PM, Lawrence Bottorff <bor...@gmail.com> wrote:

> I posted the following question at stackoverflow and got 3 pluses and a star (favorite question) -- although only one response. I'd really like to clear these issues up in my mind. Here goes:
>
> ----
>
> I've read some of the discussions here, as well as followed links to other explanations, but I'm still not able to understand the mathematical connection between "changing state" and "not changing state" as it pertains to our functional programming versus non-FP debate. I understand the basic argument goes back to the pure math definition of a function, whereby a function maps a domain member to only one range member. This is subsequently compared to when a computer code function is given certain input, it will always produce the same output, i.e., not vary from use to use, i.e.i.e., the function's state, bzw. its domain to range mapping behavior, will not change.
>
> Then it get foggy in my mind. Here's an example. Let's say I want to display closed, block-like polygons on an x-y field. In typical GIS software I understand everything is stored as directed, closed graphs, i.e. a square is four "vectors," their heads and ends connected. The raw data representation is just the individual Cartesian start and end points of each vector. And, of course, there is a "function" in the GIS software that "processes" all these coordinate sets. Good. But what if we represent each polygon in a mathematical way, e.g., a rectangle in the positive x, negative y quadrant might be:
>
> Z = {(x,y) | 3 <= x <= 5, -2 <= y <= -1}
>
> So we might have many Z-functions, each one expressing an individual polygon -- and not being a whiz with my matrix math, maybe these "functions" could then be represented as matrices . . . but I digress.
>
> So with the usual raw vector-data method, I might have one function in my code that "changes state" as it processes each set of coordinates and then draws each polygon (and then deals with polygons changing), while the one-and-only-one-Z-function-per-polygon method would seem to hold to the "don't change state" rule exactly -- or at least not change memory state all that much. Right? Or am I way off here? It seems like the old-fashioned, one-function-processing-raw-coordinate-data is not mutating the domain-range purity law really. I'm confused.... But the many individual polygon Z-functions method would seem to hold a perfect picture of state in memory -- no? Then I was reminded of my days (1980s) in Cartography when an object-oriented GIS package (written in Smalltalk) was being discussed. Everyone dismissed it as ludicrous because it tried to have exactly that: all the cartographic objects (roads, areal, symbols, polygons, etc.) in live memory at once. But isn't this ul!
timately what FP wants?
>
> Another avenue of my inspiration came from reading about a new idea of image processing where instead of slamming racks of pixels, each "frame" would be represented by one big function capable of quasi "gnu-plotting" the whole image: edges, colors, gradients, etc. Now I ask, Is this germane? I guess I'm trying to fathom why I would want to represent, say, a street map of polygons (e.g. city blocks) one way or the other. I keep hearing functional language advocates dance around the idea that a mathematical function is pure and safe and good and ultimately Utopian, while the non-FP software function is some sort of sloppy kludge holding us back from Borg-like bliss.
>
> But again, more confusing is memory management vis-a-vis FP versus non-FP. What I keep hearing (e.g. parallel programming) is that FP isn't changing a "memory state" as much as, say, a C/C++ program does. Is this like the Google File System (or my Smalltalk GIS example) where literally everything is just sitting out there in a virtual memory pool, rather than being data moved in and out of databases, bzw. memory locations? Somehow I sense all these things are related. Therefore, it seems like the perfect FP program is just one single function (possibly made up of many sub-functions like nesting Russian dolls) doing all tasks -- although a quick glance at any elisp code seems to be a study of programming schizophrenia on this count.
>
> ----
>
> It would be nice to hear some of your thoughts on this.
>
> LB
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users


____________________
Racket Users list:
http://lists.racket-lang.org/users

Nick Shelley

unread,
May 22, 2013, 1:57:34 PM5/22/13
to Lawrence Bottorff, users
I'm sure you'll get more sophisticated responses soon, but this blog post by John Carmack helped me understand the ideas and tradeoffs a bit. I'm not sure everything in there is 100% correct or that I agree with everything, but I think it provides a pretty good overview.


I think Neil Toronto originally posted this to the list a while back, which is how I came across it.


George Rudolph

unread,
May 23, 2013, 7:40:57 AM5/23/13
to us...@racket-lang.org

Lawrence Bottorff <borgauf@...> writes:

>
> I posted the following question at stackoverflow and got 3 pluses and a
star (favorite question) -- although only one response. I'd really like to
clear these issues up in my mind. Here goes:
> ----
>
>
> I've read some of the discussions here, as well as followed links to other
explanations, but I'm still not able to understand the mathematical
connection between "changing state" and "not changing state" as it pertains
to our functional programming versus non-FP debate. I understand the basic
argument goes back to the pure math definition of a function, whereby a
function maps a domain member to only one range member. This is subsequently
compared to when a computer code function is given certain input, it will
always produce the same output, i.e., not vary from use to use, i.e.i.e.,
the function's state, bzw. its domain to range mapping behavior, will not
change.

------------------------------
Lawrence,
Take a different perspective entirely. Forget the mathematical and
theoretical
stuff for the moment. Think about sources for bugs in imperative,
procedural or OO programming,
when left up to a programmer to manage:
1. pointers
2. memory management
3. assigning and reassigning values to variables/memory locations
4. breaking an interface (whether it’s OO or not)
Let’s just stop at those 4. People might mention others…

Also think about some other things that we do repeatedly in coding solutions
to many problems:
5. iterate over a list or collection to do something to each element of
that collection
6. interrupt computation at time t, restart from the same point at time
t+x.
7. use “potentially infinite” and possibly recursive data set

Pure functional languages do not allow (re)assignment. You can assign a
variable/identifier once,
then it is immutable. If you need a new value, it is a different identifier
(different memory location
under the covers). It removes a number of shared-memory bugs caused by
updating a location
incorrectly, whether your application is single-threaded or multi-threaded
or distributed.

Similarly a programmer can create new things, like lists, but is not
*required* to explicitly manage when
a list gets destroyed/deallocated. Typically, the system will do it.

Functional languages allow a clean way of passing functions (and closures)
as parameters and
returning functions as values from another function. This enables higher-
order functions such as map, filter,
fold and list comprehensions to work in predictable, sane ways. Again so
the programmer doesn’t have to explicitly manage, or even think about,
operating on a collection as explicit iteration. Functions with closures
allow you to cleanly stop execution of a function, “memorize it” (to use
some jargon) and restart it later, with the expectation that it will
generate the same result as though it had simply run from beginning to end.

And then there is lazy versus strict evaluation. When dealing with
potentially infinite data, the programmer’s
code does not have to change versus dealing with a collection that only has
10 items. Typically the programmer includes
a lazy version of the same code, or change a library import, or something
like that—which is much less likely
to introduce bugs in code than having to change a lot of function/method
names. Lazy versus strict evaluation
of code/data is a behavioral, semantic issue, usually not a syntactic one.

George

Greg Hendershott

unread,
May 23, 2013, 2:48:37 PM5/23/13
to George Rudolph, us...@racket-lang.org
> Pure functional languages do not allow (re)assignment. You can assign a
> variable/identifier once,
> then it is immutable. If you need a new value, it is a different identifier
> (different memory location
> under the covers). It removes a number of shared-memory bugs caused by
> updating a location
> incorrectly, whether your application is single-threaded or multi-threaded
> or distributed.

To me, that's the crux. The problem isn't assignment, it's REassignment.

Saying that "X is 1" is fine. The problem is later saying, yeah
actually, X isn't 1 anymore. What, you didn't get the memo?

Even a pure functional program is constantly writing to memory, or it
wouldn't be accomplishing anything. The "state" of interest here isn't
RAM, it's your names for things in RAM.

Also, any practical system will be non-functional at its "edges": At
some point, it has to earn its living by doing I/O of some sort.

My word cloud also includes:

WORM: Write Once, Read Many.

CR_D: CRUD with Create, Read, and Delete, but without the Update.

POST, GET, DELETE: HTTP without the P verbs. No PUT. _Definitely_ no PATCH. ;)

Finally I think it's important to walk the talk. My understanding of
functional programming, however poor, is itself purely functional.
Nothing anyone says will change it. :p
Reply all
Reply to author
Forward
0 new messages