6 ms·
> Functional is OK. Imperative is OK. Both in the same program are a mess. I guess you're referring to languages that are not-quite-without-side-effects, but I
by stdbrouw 9y ago
> Functional is OK. Imperative is OK. Both in the same program are a mess.
I guess you're referring to languages that are not-quite-without-side-effects, but I'd say the biggest influence the functional paradigm has had on other (imperative) programming languages is actually the addition of higher-level data manipulation operations. The functional utility libraries you see for languages such as Python and JavaScript exist solely because sometimes functional idiom like "map this onto this" or "take that only if this holds" or "let's make a new function by pre-filling these function arguments" is more intuitive than having for loops all over the place. And it mixes just fine with other imperative code.
- Chris_Newton 9y agoI guess you're referring to languages that are not-quite-without-side-effects, but I'd say the biggest influence the functional paradigm has had on other (imperative) programming languages is actually the addition of higher-level data manipulation operations. I tend to agree. The two big wins from a more “functional” style, from my perspective, are the clear emphasis on the data and the way effects are more explicit and controlled. I want things like higher order functions and algebraic data types and powerful interface/class systems. With those I gain many useful ways to represent and manipulate data that I don’t have in most languages today. In a world where most mainstream languages are just discovering filter, map and reduce on their built-in list types, a language like Haskell gives me, out of the box, tools like mapAccumWithKey that work with any data structure as long as it provides the specific, clearly defined interfaces required for the algorithm to make sense. In a world where most mainstream languages are worrying about accidentally derefencing nulls or whether there’s a proper type for enumerating a set of values, functional languages routinely use algebraic data types and pattern matching, and some go much further. Arguably, these aren’t really functional concepts at all, in that you could have them just as well in an imperative language. However, in practice it is the functional-style languages that are far ahead in these areas, because they are a natural way to work in languages that emphasize composition of functions and careful, explicit handling of data. I also want to know that I’m not applying effects on resources unintentionally, or sharing resources without proper synchronisation, or trying to apply effects on resources in an invalid order, or failing to acquire or release resources properly, or leaving resources in a mess if something aborts partway through an intended sequence of effects. This aspect goes a lot further than just making data constant-by-default, but it certainly doesn’t require trying to remove state and effects altogether. These things aren’t so much about making my code more expressive but about stopping me from making mistakes. I want a language that will stop me from accidentally modifying a matrix in-place in one algorithm while some other algorithm has a reference to that matrix that it assumes won’t change. I don’t want a language that will stop me from ever modifying a matrix in-place. Sometimes modifying things in-place is useful. I want a language that will be explicit about the initialisation and lifetime and clean-up of a locally defined cache or temporary buffer. I don’t want a language that tells me I can’t cache a common, expensively computed result 15 levels deep in my call hierarchy without changing the signature of every function on every possible path to that point in the code, or a language that will let me do whatever I want but only if I use some magic “unsafe” keyword that forfeits most or all useful guarantees about everything else in the universe as well. In this respect, my personal ideal programming style for most tasks very much would be a hybrid of imperative/stateful and functional/pure styles, with the key point being that the connections between them should be explicit, obvious and deliberate.
- smaddox 9y agoRust doesn't require use of unsafe for cacheing 15 levels deep if you use the right primitives, i.e. a Mutex. If you dont use safe primitives, then yes you need to use the unsafe keyword to mark the code as unsafe. How is that unreasonable?
- Chris_Newton 9y agoI was commenting on the beneficial influences from functional programming on mainstream languages, and noting that a purely functional programming style with no effects isn’t (IMHO) particularly necessary or desirable. I don’t know much about Rust so I can’t comment much on that. The intended point of my example was that in a purely functional environment, you can’t have a local, low-level cache, because updating a cache is inherently stateful. So either you need something like a mechanism that lets you break the rules locally, like say unsafePerformIO in Haskell, or you need to infect your entire call chain with whatever mechanism you use to manage effects top-down. While that is perfectly reasonable, given the constraints you choose by adopting a purely functional language, I don’t think it’s particularly helpful. My conclusion was that I’d rather have a hybrid of imperative and functional styles, contrary to the suggestion in the original presentation and in general agreement with stdbrouw’s comment.
- SiVal 9y agoAre there any languages you know of that are somewhat close to what you describe?
- flavio81 9y agoYes, they exist and have been used for decades; two well known examples are Common Lisp and Scheme. For example you can do functional, imperative and OO programming in Common Lisp, and it brings extensive features for working in all these three paradigms. More recent examples, Racket and Julia.
- 9y ago
- theoh 9y agoPresumably animats is talking about things like "do notation" in Haskell, not innocuous cases of function composition or first-class functions in imperative languages.
- hood_syntax 9y agoI don't know, it's far from perfect, but is there a better way to do sequencing in a pure functional language? You have to be able to specify order of execution to tackle real world tasks unless you do callback/continuation passing style right? I find that a lot less intuitive for most applications.
- theoh 9y agoDo notation isn't about sequencing, it's syntactic sugar for the monadic 'bind' operator. Sequencing happens because of data dependencies. This is part of the confusing story about monads -- the fact that every monad is a monoid doesn't imply a straightforward linear sequencing, as something like the tardis monad illustrates. https://wiki.haskell.org/Do_notation_considered_harmful https://wiki.haskell.org/Do_notation_considered_harmful More constructively, it could be argued that IO just does not fall in the remit of functional programming. SPJ doesn't refer to Peter Landin much, but Landin's perspective is valuable. In 1964 he wrote "Is there some way of extending the notion of [arithmetic expressions] so as to serve some of the needs of computer users without all the elaborations of using computers?" (The Mechanical Evaluation of Expressions). IMO this is exactly what the Haskell tradition is pursuing. Landin, quite modestly, doesn't anticipate that entire computer systems will be expressible in functional form. Implicit data structures are just one example of a mechanism that is incompatible with the idea that everything is an expression whose internal representation is managed by the language runtime (itself presumably involving mutable data). I don't think Landin was experiencing a failure of vision--I think he saw clearly that some of the "elaborations" of computing are remote from a functional model.
- hood_syntax 9y agoFair enough; I knew do notation was sugar for bind, but I didn't realize/forgot about the time traveling monad. Follow up questions bc I'm still a Haskell noob: Do you know if async/wait from Control.Concurrent.Async ensure linear sequencing? Could the sequencing problem be solved if linear types make their way into Haskell? (They seem to mainly be about memory management, but I'm not sure about other potential applications)