[HN Gopher] The case for dynamic, functional programming
       ___________________________________________________________________
        
       The case for dynamic, functional programming
        
       Author : todsacerdoti
       Score  : 26 points
       Date   : 2022-11-22 19:09 UTC (1 days ago)
        
 (HTM) web link (www.onebigfluke.com)
 (TXT) w3m dump (www.onebigfluke.com)
        
       | z9znz wrote:
       | Unfortunately this toy example isn't even pure functional because
       | it raises an exception as a way of providing an alternative
       | return from the function.
       | 
       | A better example, if one wanted to keep a single function pure
       | and still keep (two) states would be to return a tuple of
       | (current_count, is_expired). Further, since it uses zero as the
       | implicit boundary to determine when the counter is complete by
       | counting down from the provided value, the function should be
       | named countdown_timer() or something more descriptive.
       | 
       | Honestly I'm not sure who this little article was written for.
       | The references to C++ were irrelevant. The OOP Python example,
       | however, was a pretty common example of how Python people write
       | code. It's a shame, really. We should (edit: not) have to
       | deprogram people to get them to choose something more like the
       | latter "functional" approach over the former pointlessly-OOPy
       | example.
        
         | nlitened wrote:
         | Why do you consider raising an exception not being pure
         | functional? In my opinion, it is exactly equivalent to
         | propagating error return value up the call chain until it is
         | handled (but is faster if no exception is thrown, and contains
         | a stack trace of where the original error value was produced
         | for programmer's convenience).
        
           | jerf wrote:
           | If you take that view, yes, it's functional.
           | 
           | However, if you take that sort of view, _everything_ is
           | functional. Writing to the screen can be viewed as having
           | returned instructions to write to the screen, combined with
           | an interpreter that performs it. Writing to the network is
           | like having returned an instruction to write the network,
           | along with an interpreter to do it. (And a certain _laissez
           | faire_ attitude about laziness, perhaps.)
           | 
           | This view is not necessarily _wrong_. There is a certain
           | truth to it; for one thing it 's not a terrible gateway to
           | understanding how "pure functional" languages interact with
           | the real world in practice, because this is pretty close to
           | how they do it.
           | 
           | However, it is _useless_ , because it means every language is
           | a pure functional language, and the utility of a term is its
           | ability to split the universe under discussion into multiple
           | pieces. Or to put it another way, a decision procedure that
           | puts 100% of the items under decision into the same category
           | conveys exactly zero bits of information.
           | 
           | So for a definition of "pure functional" to have any utility,
           | it needs to exclude some things. Exceptions are borderline.
           | Technically Haskell does indeed have them, but it has a
           | complicated relationship with them. It is certainly the case
           | that we are discussing something that is not in the function
           | signature anymore, which people generally consider not "pure
           | functional". But generally it definitely has the "well, we
           | could _pretend_ that we returned a special value and had a
           | special interpreter wrapped around it for every call " copout
           | feel to it in general.
        
             | nlitened wrote:
             | I am not trying to argue, rather being curious.
             | 
             | I've tried to look up what "pure functional" means exactly,
             | and failed to find anything better than Wikipedia[0]:
             | In computer programming, a pure function is a function that
             | has the following properties:         1. the function
             | return values are identical for identical arguments (no
             | variation with local static variables, non-local variables,
             | mutable reference arguments or input streams), and
             | 2. the function has no side effects (no mutation of local
             | static variables, non-local variables, mutable reference
             | arguments or input/output streams).
             | 
             | I don't see how that excludes exceptions. I agree that
             | exceptions kind of "feel" imperative rather than
             | functional, but could you maybe help me find an accepted
             | definition which excludes exceptions?
             | 
             | [0] https://en.wikipedia.org/wiki/Pure_function
        
           | z9znz wrote:
           | Raising an exception is not returning a value; it is
           | essentially a GOTO.
           | 
           | A pure function has no side effects. Maybe it takes an input,
           | and maybe it returns a result. But whatever it does
           | internally has no affect on the outside world. Raising an
           | exception is definitely a side effect.
           | 
           | Worse yet, in TFA example it wasn't even necessary to use an
           | exception in place of a returned state. I would argue that
           | the exception example was, irrespective of functional vs
           | imperative principles, a bad use of exceptions.
        
             | nlitened wrote:
             | Yeah, I agree that in that Python example, use of
             | exceptions is awful (and the entire example is awful, I
             | think).
             | 
             | > But whatever it does internally has no affect on the
             | outside world. Raising an exception is definitely a side
             | effect.
             | 
             | However, would you please elaborate, how do exceptions
             | affect the outside world, in your opinion? As far as I
             | understand, exceptions only "return exceptional value" up
             | the call stack, to the calling function. Of course, barring
             | total program crash in case of unhandled exception, but
             | unhandled exception is a programmer's error, like integral
             | division by zero or infinite memory allocation loop.
        
       | linguae wrote:
       | Judging by the title, I was expecting to read about the
       | advantages of dynamically-typed functional programming languages
       | like Scheme and Clojure over statically-typed functional
       | languages like Standard ML and Haskell. It seems to me that large
       | segments of the functional programming community love static
       | typing, and the research community has done (and continues to do)
       | a lot of work applying type theory to the design of programming
       | languages. However, there are still many advocates of
       | dynamically-typed functional programming languages, generally
       | from the Lisp family.
       | 
       | I don't think of functional programming when I use Python, even
       | though it's possible to program in a functional style. I'm also
       | unaware of any Python development environments that offer
       | Smalltalk or Common Lisp levels of interactivity, though this is
       | less of a criticism of the language and more of a matter of
       | tooling.
        
         | falcolas wrote:
         | Yup, I would love to see a bit more love for Lisp style
         | languages. They're incredibly powerful and it's a shame they're
         | relegated to the fringes of professional programming.
        
           | grumpyprole wrote:
           | Lisp evolved into ML and Haskell and these have influenced
           | professional programming enormously over the last few
           | decades. Or are you advocating we ditch all progress and go
           | back to Lisp? Have you tried an ML such as OCaml?
        
             | bslatkin wrote:
             | Why do you think so many Lisp fans (e.g., Norvig) went on
             | to use Python instead of those ML alternatives?
        
               | grumpyprole wrote:
               | Norvig is a teacher and author who wants to reach the
               | widest audience possible. So in his case, it was either
               | Java or Python.
        
           | bslatkin wrote:
           | 100% agree with both of these comments. I'd love to see a
           | resurgence of interest in this area.
        
             | kaveh808 wrote:
             | What we need are examples of Lisp-style languages leading
             | to big wins. Clojure did that to some extent, but
             | unfortunately in the Common Lisp world successful
             | commercial or technical projects are few and far between.
             | 
             | Our best bet is probably making interesting advances in the
             | open source field, until something takes off, which is
             | largely a factor of luck.
        
       | functional_pyth wrote:
       | Would have been a stronger case if Julia, Erlang/Elixir, or
       | Clojure/Racket was used.
       | 
       | Python is one of the worst dynamic/FP languages I've ever had the
       | displeasure to utilize.
       | 
       | I think the OP needs to step out of their comfort zone of Python.
        
         | qsort wrote:
         | The point of the article isn't to say that python is better,
         | all it's saying is that a more functional style leads to
         | cleaner code. This is irrespective of python's technical
         | merits.
         | 
         |  _With a dynamic, functional language you could enjoy all of
         | this simplicity. But you don 't even need that to benefit from
         | this perspective today! You can adopt the techniques of
         | functional programming right now in your favorite language
         | (such as Python, used above). What's important is your mindset
         | and how you approach problems, minimizing state and maximizing
         | clarity in the code you write._
        
           | z9znz wrote:
           | I suspect the point of the article was to be seen again and
           | remind people of his book.
        
         | bslatkin wrote:
         | I agree that another language that's better for functional
         | programming would make this more compelling. Python has a lot
         | of limitations in this area. I think all of those alternatives
         | you listed (Julia, Clojure, etc) include turn-offs for Python
         | programmers that are deal breakers. So I'm seeking another
         | language that appeals to Python programmers but has more of the
         | attributes that are amenable to functional programming.
        
       | mayoff wrote:
       | > dynamic languages don't require you to "emulate the compiler"
       | in your head
       | 
       | On the contrary. Your 'functional' timer function takes an
       | argument and applies the - operator to it.
       | 
       | You must be sure, every time you call the function, that you pass
       | an argument to which - can be applied. If you don't, your user
       | might see a TypeError. So you, the human author of the code, must
       | 'emulate the compiler' by performing type inference and type
       | checking in your head.
       | 
       | In my statically-typed language, the compiler ensures that my
       | program only ever passes arguments for which the - operator is
       | applicable. I don't need to 'emulate the compiler', because I'm
       | actually using a compiler.
        
         | bslatkin wrote:
         | I wouldn't do the type checking in my head, I'd just assume
         | it's going to work as intended. If that's not the case, then an
         | error will happen. What's wrong with the user seeing an error?
         | There's always a chance my program will have a bug and fail. So
         | I have to handle that case no matter what and properly deal
         | with unexpected errors, error reporting, etc.
        
       | bitwize wrote:
       | Functional programming can be attempted in any language that
       | offers higher-order functions that can close over free variables.
       | In other words, languages isomorphic to untyped lambda calculus
       | with additions. Python, Perl, JavaScript, and Go all qualify.
       | 
       | However, it seems to be generally accepted that you're really
       | only doing baby FP unless you also program with _types_. Types,
       | and the algebras that can be imposed over them, are where the
       | really interesting FP work is done that yields new control
       | structures (of which monads are just one), that are difficult and
       | cumbersome to express in untyped LC, or in a dynamic programming
       | language.
        
         | bslatkin wrote:
         | Author here. Part of my point here is 1) you get a lot of
         | benefit from dynamic languages, and 2) you also get a lot of
         | benefit from functional programming. And 3) you'd get the most
         | benefit from both together. When you add types into the mix
         | then you're taking two steps forward and one step back in terms
         | of simplicity and understandability.
         | 
         | What you said is also perpetuating a common reason that people
         | are turned off to FP in general these days:
         | 
         | https://twitter.com/haxor/status/1584725493513072640
         | 
         | "At some point functional programming became equivalent in
         | people's minds to complex type systems (e.g., monads), and that
         | caused the value of Lisp to be lost to history. It's a shame."
        
           | bitwize wrote:
           | I don't know what you're getting at. Static typing makes your
           | code easier to understand and reason about. It's pretty much
           | an unmitigated win.
           | 
           | Dynamic languages may have made some sense back when people
           | wanted to cruft something up and get it running without
           | declaring the type of each object before it is used (which
           | declarations can get verbose and cumbersome in the presence
           | of parameterized types). But today we have powerful computers
           | that can quickly check and infer types in ever more elaborate
           | type systems, and the risks of dynamic languages seem less
           | and less worth the development speed gains. You can see this
           | in perennial language popularity contests. Consider how
           | thoroughly the programming community embraced TypeScript over
           | JavaScript because of the guarantees static type checking
           | provides before your code even runs. And while Lisp used to
           | be the smart kids' favored programming language, it was
           | displaced by Haskell and later by Rust once those languages
           | matured to where people were building real world stuff in
           | them. (As it turns out, baking object lifetimes and ownership
           | into your type system means you don't even need a GC to get
           | Lisp levels of power and abstraction!)
        
           | grumpyprole wrote:
           | > "At some point functional programming became equivalent in
           | people's minds to complex type systems (e.g., monads), and
           | that caused the value of Lisp to be lost to history. It's a
           | shame."
           | 
           | This is nonsense. Types help us name, categorise, discuss and
           | share abstractions. These abstractions are useful in any
           | language, typed or otherwise. Lisp lives on in its
           | descendents ML and Haskell. If someone doesn't want to learn
           | anything new then they are themselves "lost to history".
        
       | revskill wrote:
       | Agree. But Python is not good example for me. I think Python
       | developers are genius because they could get things done in a
       | language with 1-line lambda !
        
         | Jtsummers wrote:
         | Single expression lambdas. Lambdas can span multiple lines but
         | they specifically contain a single expression, no statements.
         | And since Python isn't really an expression oriented language
         | this is still pretty constraining.
        
       | substation13 wrote:
       | > You can adopt the techniques of functional programming right
       | now in your favorite language (such as Python, used above).
       | 
       | No tail call optimization, no expression orientation, mutability
       | by default, no variable shadowing, single expression lambdas, no
       | do-notation, reference semantics everywhere, dog slow function
       | calls... functional programming in Python doesn't work in my
       | experience.
       | 
       | Now, there is a great case for functional lisps, like Clojure,
       | since they support all the fancy patterns in cutting-edge Haskell
       | etc. but without an extremely complex compiler. Of course you
       | sacrifice some type-safety.
        
         | bslatkin wrote:
         | Author here: I agree that another dynamic language with all of
         | those attributes would be great.
        
       | bjd2385 wrote:
       | Wait where is self.count set, and is it 0 or 1?
        
         | bslatkin wrote:
         | Fixed now, thanks!
        
       | xedrac wrote:
       | > With Python it's much easier to become proficient and remember
       | everything you need to know. That translates to more time being
       | productive writing code instead of reading documentation
       | 
       | Until your python program reaches a certain size where the lack
       | of type safety and other protections becomes a real liability.
       | Contrast that to something like Rust, where it's easy to have
       | confidence in very large code bases.
        
         | [deleted]
        
         | falcolas wrote:
         | As someone who's worked on a huge Python program (10M+ LOC),
         | type safety was the last thing on our mind when maintaining it.
         | 
         | Refactoring might not have been quite as simple, but there are
         | plenty of tools available for refactoring Python code, and we
         | were never afraid of doing it.
         | 
         | Though, working on a project right now in Java, the type system
         | isn't exactly saving us from refactoring failures, because the
         | type system doesn't ensure that the String or Integer or <low
         | level interface> being passed in is the right one.
         | 
         | Of course, I'm sure we're just "doing it wrong".
        
           | bslatkin wrote:
           | I've had a similar experience in a large Python codebase!
        
           | cosmic_quanta wrote:
           | > As someone who's worked on a huge Python program (10M+ LOC)
           | 
           | Holy moly. I can't imagine working on a codebase like this in
           | Python.
           | 
           | I maintain several small codebases (<10k LOC) at work. Even
           | at this size, I definitely feel the lack of type safety --
           | specifically, at the boundaries of logical blocks of code.
           | When refactoring, the lack of thorough type-checking means
           | that I need to mentally propagate my changes throughout the
           | codebase. This is exhausting!
        
           | harveywi wrote:
           | > As someone who's worked on a huge Python program (10M+
           | LOC), type safety was the last thing on our mind when
           | maintaining it.
           | 
           | This is true. Python dictionaries are extremely powerful, and
           | they obviate the need to use elaborate types throughout the
           | code. There is even a special syntax for making dictionaries
           | in Python. In a code base with lots of dictionaries, type
           | errors are a thing of the past.
        
           | yakshaving_jgt wrote:
           | There's a significant delta between Java types and Haskell
           | types.
        
       | bmitc wrote:
       | I'm not sure what Python has to do with functional programming,
       | even remotely. It basically contains none of the typical building
       | blocks for functional programming (immutable data, real lambdas,
       | pattern matching, sane scoping, etc.). Python has weird scoping,
       | weird lambdas, and fully on mutability.
       | 
       | Regarding static versus dynamic functional programming, I think
       | dynamic typing is probably oversold in that most dynamic programs
       | could be static programs with very little effort. Where dynamic
       | types really shine are at _boundaries_ of programs. Dynamic types
       | are very useful for handling messages, I /O, data formats, etc.
       | 
       | I actually think an ideal language would be one that is at its
       | core a statically typed functional language, but then it has
       | dynamic types allowed at the above stated boundaries. Then there
       | could be some explicit conversion layer or solution that moves
       | values from one side to the other.
       | 
       | Basically all other uses of dynamic typing can be solved in a
       | statically typed language that has anonymous types like anonymous
       | records and anonymous discriminated unions and some sort of
       | "umbrella" or "shared" type such as interfaces, generics, and/or
       | typeclasses, for example.
        
         | somewhereoutth wrote:
         | Indeed, and I would further argue that _business_ data be
         | dynamically typed, because business data is often messy and
         | changeable, whereas the plumbing that carries it can be
         | statically typed to ensure you are connecting the pipes
         | correctly.
        
           | grumpyprole wrote:
           | > I would further argue that business data be dynamically
           | typed, because business data is often messy and changeabl
           | 
           | Huh? Static types make change and refactoring _much_ easier.
        
       | whateveracct wrote:
       | Nix is my favorite dynamic functional language. So much so that
       | I'll probably use it (via hnix) as a general scripting language
       | in projects that benefit from that.
        
       ___________________________________________________________________
       (page generated 2022-11-23 23:02 UTC)