Hacker News new | past | comments | ask | show | jobs | submit login
The C language is purely functional (2009) (conal.net)
112 points by ingve on May 22, 2019 | hide | past | favorite | 31 comments



In case someone misses the point of the article -- I know I did, originally! -- the author explicitly lays it out in a comment:

> "Sadly–and here is the real intent behind my post–many Haskell programmers believe that IO is necessary to do “real programming”, and they use Haskell as if it were C (relegating lots of work to IO). In other words, monadic IO has proved to be such a comfortable “solution” to I/O in a functional language, that very few folks are still searching for a genuinely (not merely technically) functional solution. Before monadic IO, there was a lot of vibrant and imaginative work on functional I/O. It hadn’t arrived yet, but was still in touch with the Spirit of functional programming. With the invention and acceptance of monadic imperative programming, it’s like the Haskell community wandered into an opium den and are still lying there in a fog.

> [...]

> "By relating Haskell+IO to cpp+C, I’m not trying to lower the former or elevate the latter. Instead, I’m trying to waken functional programmers out of the opium fog and remind them how fun and creative genuinely functional programming can be."

I love Haskell but I'm not well-versed enough to know why other alternatives to monadic IO were discarded. I do know Haskell predates the IO monad.


Section 7 of "A History of Haskell: Being Lazy With Class" http://haskell.cs.yale.edu/wp-content/uploads/2011/02/histor... explains how Haskell IO was done in the pre-IO monad days.

This recent paper by Oleg Kiselyov ("Effects Without Monads: Non-determinism Back to the Meta Language" https://arxiv.org/abs/1905.06544 https://www.youtube.com/watch?v=fABeIG_HiyU) explores an alternative to monads for structuring effectful computations in OCaml. The effect used in the examples is nondeterminism, instead of IO.

The ICFP presentation "Automatically escaping Monads" is also interesting https://www.youtube.com/watch?v=wG8AErq6Bbo http://benl.ouroborus.net/talks/2016-HIW-Escape.pdf


Another functional approach to I/O is "effect systems". I'm not aware of "stable" functional language with them in it though. It's mostly academic at this point.

Here is some research on it: https://www.microsoft.com/en-us/research/publication/koka-pr...


You can take a look at Effects in Idris: http://docs.idris-lang.org/en/latest/effects/


Oh I didn't know Idris had an effect system. Thanks!


There is a language called Frank. I read a paper on it, called "Do Be Do Be", which was an epic read.

https://arxiv.org/abs/1611.09259


Conor McBride's primary research area seems to be puns, which he publishes via programming language theory :P

My favourite title is "I am not a number, I am a free variable"

https://scholar.google.com/citations?user=vO7qGKwAAAAJ&hl=en...


The quality of discourse in the comments there is amazing. I miss old-style blogging/commenting...


The paper "Imperative Functional Programming" https://www.microsoft.com/en-us/research/wp-content/uploads/... describes the IO Monad, and compares to two other approaches: Dialogues and Continuations


I don't think it's the io Monad. It's more do-notation sugar. Nobody would use that crazy free variable scoping pattern if it wasn't for that notation.


I've gotten good results annoying type theorists by mocking Scala and Haskell for being less pure than C++ template metaprogramming.


How many type theorists do you know?


A couple?


The Java language is purely functional.

“Java programmers” really program not in Java, but in a purely functional language called 'cat'. As with any purely functional language, cat consists only of expressions, not statements. Now here’s the clever part: when evaluated (not executed), a cat expression yields a (pure) value of type Java, which is an ADT (abstract data type) that represents imperative programs. That value of type Java is then executed (not evaluated) by the cat language’s RTS (run-time system). As a clever optimization, cat’s RTS actually includes a code generator, considerably improving performance over more naïve implementations of the Java abstract data type.



I wasn't using HN back in 2015 (let alone in 2009) so I'm glad this was re-submitted. It's been more than 4 years which is sufficient time for new users or for people to come back to it with new opinions.


It's interesting to see what people thought 4 or 10 years ago about the same article. Those links aren't to chide the submitter that it has been submitted before, HN explicitly allows reposts after a year or so.


Ah, thanks for explaining that. I'm more used to Reddit where re-posting links is more common but also more frowned upon, so I'm still unfamiliar with the culture here.


Sure. Reposts are fine after a year or so. This is in the FAQ: https://news.ycombinator.com/newsfaq.html. The purpose of linking to previous discussions is that many readers are curious to read them.


> (or char *, for you type theorists, using a notation from Kleene)

I chuckled.


10 years on and that one still gets a laugh from me.


#undef isn't impure. It isn't an assignment, but an unbinding; it asserts that in the remainder of the translation unit, the preprocessor symbol is not known. That symbol's status in the prior parts of the translation unit hasn't changed.

Consider that lambda or let will also unbind:

  (let ((x 3))  ; #define x 3
    )           ; #undef x, effectively


Not really, because in your lisp example, there is proper scoping. Variables will always be unbound in the reverse order they were bound.

You can do something like this in c:

    #define x 3
    #define y 4
    
    #undef x
    
    // more stuff
    #undef y
Which is essentially equivalent to [(])


> there is proper scoping

Rather, there is nesting. Nesting isn't the same thing as scoping.

> Variables will always be unbound in the reverse order they were bound.

Counterexample:

   (let* ((x 1) (y (* 2 x)) (z (+ x y))) ;; bound in sequence
    ) ;; unbound in parallel

We could have an unlet operator which introduces a new scope that explicitly removes a binding:

   (let (x)                      ; #define x
     (let (y)                    ; #define y
       (unlet (x)                ; #undef x
         ;; y bound, x free
         )))                     ; #undef y, #undef x
Basically, #define and #undef can be regarded as having proper nesting: each one creates a new scope that lasts unti the end of the translation unit, nested relative to the previous scope.


Interesting article indeed. If anyone was curious and wanted to drill down to the source of the "Scala is Not a Functional Programming Language" discussion, surprise, the link is dead. Here's an archive link: http://web.archive.org/web/20090517065705/http://enfranchise...


See how far you can get with:

    #define int const int
    #define float const float
    etc...
    
It makes for some interesting patterns


I was about to comment to snarkily refute the claim in the title, but then the author seemed to sarcastically do that in the article.


If you don't have that interrupt in your brain that fires when you read something so outlandish that it surely must be deliberately provocative, you're going to spend a lot of time very upset about articles from The Onion.


From a previous Obfusticated C contest winner- towers-of-hanoi purely in cpp (evaluated at compile time)

https://www.cise.ufl.edu/~manuel/obfuscate/vanschnitz


On a different note, the book "Functional C" teaches C programming to ML functional programmers. Quite interesting and gives a new perspective to C programmers on their favourite language.


Technically the C preprocessor is purely functional!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: