[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)