6 ms·
I find the authors definition of functional programming really insufficient. FP should not be defined in terms of immutable state, but as mathematical functions
by ilovecaching 8y ago
I find the authors definition of functional programming really insufficient. FP should not be defined in terms of immutable state, but as mathematical functions as the basic form of abstraction, and that programs are composed as an evaluation of a single expression in spirit with the lambda calculus. Immutable state is just a correlary.
With that in mind, you really can’t do FP without automatic currying and the function composition and application operators (. And $) in Haskell. Without these, you lack the key components needed to work with your only means of abstraction. Language author seem to think lambdas are enough, but if you can’t compose functions to build new functions, partially apply them to get specific versions of behavior, then you aren’t going to get very far.
- djcjr 8y agoI would guess most developers get "very far" without any of those. If you would please, perhaps give some examples to demonstrate what you mean?
- chii 8y agoThere's some very good example of functional programming in these videos: https://www.youtube.com/playlist?list=PLguYJK7ydFE4aS8fq4D6DqjF6qsysxTnx https://www.youtube.com/playlist?list=PLguYJK7ydFE4aS8fq4D6D... they are of a person doing the hacker rank questions, but in haskell. The last video (about the magic squares) is probably the most informative for your query.
- ilovecaching 8y agoSure, so to go back to my original point, functional programming is about building a single expression to perform your computation. Obviously this would be unweildly if you had to write your expression on a single line, so we build abstractions out of functions (hence the name FP). To combine these functions in arbitrary ways and stick with the single expression model, we need some way to create a pipeline of functions. So we have the function composition operator . program = (phase 3 . (phase 2 . phase 1)) The input of program is fed to the first phase, then the second phase, then the third phase which will produce the output of the program. Each phase can be replaced or the pipeline extended as long as the inputs and outputs match. This is an infinite composable and is the basis of how FPers architect applications, by splitting each phase in their own pipelines, smaller and smaller until you get to very basic functions. We like it compared to OO because it's straightforward to understand the flow of data. There is no need for a complicated object graph and any piece can be replaced if the types are correct. As in OO with inheritance (maybe not the best comparison), you also need a way to specialize functions in order to get their signature to match the input and output requirements of a pipeline. For that, we can partially apply a function. For example, if I need to connect the function: Int String -> Int (That is a function with two parameters, an int and a string that produces an int) to the output of the function * -> String (a function that takes some arbitrary value and produces a string), I need to be able to partially apply the Int, and get a function that is just String -> Int. In Haskell, all functions are single input, single output. Multiple inputs is just a shorthand for a pipeline of functions. The function Int String -> Int is really just the pipeline Int -> String -> Int. (You can see how this all starts to fit together). The function application operator is the most basic, it's just a way of forcing the order of evaluation. When you're building pipelines, you want to be able to construct a pipeline before you apply an input to the pipeline. To get the correct order of precedence between . and application, you use the explicit $ operator. What I'm trying to convey here is that these operators are akin to inheritance, composition, overloading, etc. in OO. They are ways of working with the basic building blocks of the paradigm to make larger and larger programs. As you can also see, being able to create pipelines is the basic piece of the puzzle, and building pipelines requires automatic currying to facilitate making the pieces fit, while the application operator allows us to control the order of evaluation.
- deleted 8y ago[deleted]
- claudiawerner 8y agoAlso lacking (from skimming the article) is functions being first-class when functional programming.
- skrebbel 8y agoThis is a generic "no true Scotsman" argument. Not everybody shares the same definition of FP and while "My definition of FP is what Haskell gives me, and the author's definition is more akin to what Clojure and Elixir give them" is a correct statement, it's also not a very insightful point. You can't deny that there are lots of languages and techniques that people call "functional" where the key ingredient isn't "programs composed as an evaluation of a single expression" but something else (such as "immutable state").
- ilovecaching 8y agoPlease see my explanatory reply where I described the key components of FP. I don't deny that there are lots of languages people call functional or multi-paradigm that are in fact not functional or are only borrowing some ingredients without providing enough to follow the paradigm. When we define a paradigm, we are defining the core abstraction of how programs are constructed. In FP, that would be composing functions, hence the name. We FPers borrow or share concepts with OOers. The State monad (our basic building block of holding state) is very much like OO method chaining for example. But those similarities do not make either equatable with the other.
- Qwertystop 8y agoElixir's got |> for composition and & for currying (as well as for lambdas); I'm not sure that's the best language example for differences (given the specific things grandparent listed of things languages are missing).
- christophilus 8y agoClojure's got `(->` and many other useful macros that are the equivalent of `|>` (well, more flexible, in fact), and it has support for currying, so I think it's probably relevant.
- skrebbel 8y ago|> isn't proper partial function application. It can only apply a single parameter and you can't store a piped-into function in a variable the way you can a partially applied function in eg Haskell. Eg the following won't work: splitname = name |> String.split splitname.(" ") Also, it's a stretch to call & currying. It's shorthand syntax for making an anonymous function. If & is currying, then the following JS code is also currying: const cube = a => Math.pow(a, 3) Elixir doesn't have currying or partial function application any more than JS or C# do. If your definition of "functional" requires these features then Elixir isn't functional (and that's fine).
- lkitching 8y ago> you really can’t do FP without automatic currying and the function composition and application operators (. And $) in Haskell But lambdas are enough to define these operators e.g. (defn comp [f g] (fn [x] (f (g x))) (defn ap [f x] (f x)) Operators and currying by default can use useful but it's a syntactic issue rather than a fundamental one.
- danharaj 8y agoProgrammers work with syntax. If syntax is cumbersome, programmers are inefficient. Syntax should be optimized for what the language wants you to do. Languages that just bolt on lambdas seldom are optimized to use them effectively. They're optimized for some other abstractions.
- yogthos 8y agoWe are talking about a Lisp here...
- danharaj 8y agoThe OP was making a semantic point. Take their example and write it in Python and imagine trying to actually work with it. I partially applied a function in Python once. Sucked.
- yogthos 8y agoSure, in a language where you can't easily extend semantics they have to be baked into the language to be practical. However, with Lisps you can trivially create new semantics as you need them, and whenever something becomes cumbersome it can be addressed by the user as the need arises.
- ilovecaching 8y agoWe can also create objects from closures and classes from functions, so why have syntax for it? Some languages have some of the feature I mentioned, JavaScript has bind for example, but it’s cumbersome and using it as a means of partial application for FP would be an abuse. These operators need to be syntactically easy to use, ergonomic, and recommended. Notice that in Haskell these are infix for a reason. Also, in typed languages with generics building those operators is more tricky.
- pacala 8y ago. and $ syntactic sugar that many will argue make the code easier to write but harder to read. One can compose and build new functions just fine with plain lambdas. Partial application is a mixed bag, as one has to memorize arguments and argument order by heart, plus deal with flip soup. Named arguments are usually more readable.
- yomly 8y agoThis is a genuine question - does having a statically typed language make flip soup and argument ordering slightly less of an issue? If your types are well labelled, can you push the complexity of your control flow and functional-glue (as I like to call it) into the compiler?
- phaedrus 8y agoIn the limit, if all of your types are well enough labelled, you wouldn't even need positional arguments - it could be inferred / would only have one answer. For example if (using C++ pseudo code) "double slope_of(double x, double y)" were instead "Slope slope_of(XCoord x, YCoord y)", the same function called with "slope_of(y, x)" could un-scramble those arguments. Just consider all permutations of the argument list as static overloads, and disallow having two parameters of the same type. If the language supports Currying, you could then Curry any argument of any position at any time, because the unique types guarantee the compiler knows which parameter is being bound.
- pyrale 8y agoTry this with (a - b).
- s4vi0r 8y agoYogthos is a pretty prominent clojure shill/shitposter tbf
- coldtea 8y agoWhat are you, 15 year old, in some imagined turf war? If, Yogthos, or anybody else, posts about clojure, that doesn't make them a "shill" (sic). Shill of what? In your world, somebody pays them to promote Clojure? You think Clojure has a "shill" budget? How about, they are someone who works with and likes the language, and posts whatever they consider an interesting article about it? Not to mention, where the fuck did you get with this whole reasoning? I've checked Yogthos profile, and he has posted like 3 submissions in the last 2 days, 1 20 days ago, 10-12 more 4-5 months ago, and then no post since 2017 or so. That's "too much" for you? To the point that you have a whole BS theory about it? Oh, and if you don't like someone's posts, that doesn't make them a "shitposter" (sic). In HN, what someone posts is irrelevant. Posts are voted to get to the front page. If you see this post here, it's because many people on HN like it.
- reacweb 8y agoFor me, FP is mainly support of closures in order to build higher order functions. I also think that without a GC and proper tail recursion, a FP is cripple. Above that, you have many families of FP. FP with/without strong typing, FP with lazy/strict evaluation (often mixed), FP that favours immutable state, FP with OO, FP with syntactic sugar (and/or macro) or homoiconicity, FP with continuations (scheme). It is a question of choice.
- ilovecaching 8y agoYou either are or you aren't using mathematical functions as the basic building block of your program. What you're listing is either FP with additional features, which is still FP, or features of FP used in OO. FP is paradigm, which is more about how you structure your program and how you think about solving problems.
- deleted 8y ago[deleted]
- sevensor 8y agoI think the author was fairly careful in drawing a distinction between functional programming and programming in the functional style. You can get a lot of mileage from writing pure functions and using immutable state regardless of what language you're in. It doesn't make your language a functional programming language, nor does it make you a functional programmer, but does that matter? If every pure function we write prevents another headache, why not seek out opportunities to write in that style, regardless of language?
- bunderbunder 8y agoJohn Backus's seminal paper, "Can Programming be Liberated from the von Neumann Style? A Functional Style and its Algebra of Programs" argues for this really well. http://worrydream.com/refs/Backus-CanProgrammingBeLiberated.pdf http://worrydream.com/refs/Backus-CanProgrammingBeLiberated.... Declarativity, compositionality, and referential transparency are all part of how he defines it. But he also takes a moment to contrast his idea of functional programming with a "lambda-calculus based system" (read: lisp), and one of the things he specifically criticizes is that, in those systems, functions aren't automatically curried. (His actual wording was that they are allowed to take multiple arguments, but the meaning is roughly the same.) The gist of the argument is that requiring functions to only be unary limits the number of operations you need to compose functions, which ultimately keeps the system simpler and easier to understand. He compares limiting function arity in functional programming to limiting the number of available control structures in structured programming: Strictly speaking, you lose some expressivity, but it's justified by all the other things you get in return.
- kazinator 8y agoFunctional programming is simply the obsession with functions: the use of the function call, and indirection on functions, as the only control mechanism. Functional programming emphasizes recursion, deferral of control decisions into functions (e.g. pass functional arguments to some function, and let it decide which are called and which are not), and the re-ification of all control mechanisms as functions (e.g. continuations). If it has loops, ifs and gotos, it is not functional. If it just has function calls, then it's functional, even if there is mutation going on of variables or array elements and such. Purity plays well into functional programming, because if we have structured the control flows in the program based on function calls, we can determine which functions are pure and which are not, and get a good grip on reasoning about and managing the side effects to avoid surprises.
- renox 8y ago> you really can’t do FP without automatic currying I find automatic currying by default quite weird.. If you don't provide the correct number of arguments to your function the errors generated won't be nice.. Would a 'curry' operator to provide currying be good enough?
- coldtea 8y agoTerms (including "functional programming") are defined by their use, not by their etymology or the original intention behind them. We know that since Wittgenstein at least [1]. Those who forget it, are doomed to lament about "no true Scotsman" distinctions nobody cares about. [1] http://existentialcomics.com/comic/268 http://existentialcomics.com/comic/268