7 ms·
Functional Programming Is on the Rise
- dnautics 7y ago> Mainstream languages are Java, JS, C++, Python, etc — languages one would hardly call “functional”. Even so, "functionalizing" mainstream languages is a thing. Gary Bernhardt was long advocating functional ruby. I deployed a functional Python module (to keep my sanity). React is becoming a progressively more functional dialect of JS, and it's relatively easy to deploy an "all functions" JS project. > Most developers who live today were raised on this x.one().two().three() syntactic tradition in which the code is fluent —it reads and works left-to-right a trend in functional programming is the pipe operator, at least F#, Julia, and Elixir have it to varying levels of flexibility, where you can apply the result of one function to the first parameter of the next start_with_value |> do_this() |> and_this(too) |> then_this() being equivalent to `then_this(and_this(do_this(start_with_value), too))` at least in elixir the power of the construct is really that you pipe into other things, like: value |> transform1() |> action_with_error_tuple |> case do <matches on error_tuples> end For developer ease, Elixir has a builtin IO.inspect(value, label: "label") which pretty prints as a side effect and returns the original value, which is invaluable for debugging, especially with modern IDEs like VS code you can add IO.inspect pipes (with line number annotation) to multiple lines in a pipeline, watch the dataflow, and then when you're done delete it with ease.
- snek 7y agoTo this point, JS has been very careful to not get in the way of functional programming, as it doesn't really try to stick to a specific paradigm. There is even a proposal to add pipielines: https://github.com/tc39/proposal-pipeline-operator https://github.com/tc39/proposal-pipeline-operator. Personally, I think forcing yourself to a specific paradigm is a waste of energy. I've tend to just write code that best expresses my intention. Sometimes the code turns out more oop, sometimes it turns out more fp, and often it's more of a mix.
- dnautics 7y ago> Sometimes the code turns out more oop I think it's misleading to think of FP as being necessarily opposed to OOP. While FP is very much in opposition to the stroustropian style of OOP in syntax and execution (which is what people mean these days), if you're going back to smalltalk-type message passing objects, arguably some FPs have the hands down best implementations of OOP.
- snek 7y agoOh yes, i meant that sometimes a paradigm is unused, not that one prevents the other.
- dnautics 7y agoI should give an example. Python: class MyClass: def __init__(self): self.name = "" def setname(self, new_name): self.name = new_name def getname(self): return self.name Usage: obj = MyClass() obj.setname("joe") obj.getname() #==> "joe" Elixir: defmodule MyAgent do def init(), do: Agent.start_link(fn -> "" end) def setname(obj, new_name), do: Agent.update(obj, fn _ -> new_name end) def getname(obj), do: Agent.get(obj, &(&1)) end usage: {:ok, obj} = MyAgent.init() MyAgent.setname(obj, "joe") MyAgent.getname(obj) #==> "joe" Honestly, superficially, the difference is minor (obviously, under the hood they're way different; ironically I think the Elixir form is far more "explicit" than the python form)
- mrdoops 7y agoFor sure. If there are two flavors of OOP: the Imperative OOP we're familiar with (Ruby, Java, Python, etc.) and the Message oriented variety that Alan Kay described, then Elixir/Erlang are more the Message Oriented flavor. While you can write either flavor in most languages (even Elixir if you abuse ETS), concurrency's requirements for immutability and run-time support start becoming distinguishing factors.
- dnautics 7y ago
- mlevental 7y agoI wish it were possible to do this in "native" python (I know about coconut). it would make numerical code (where you're changing data shapes a lot) so much nicer
- wodenokoto 7y agoAnother nice thing about the pipe is that you can pipe into any function, not just the methods associated with the object that I’m working with. If I write my own function four() I end up with Four(x.one().two().three() But with pipes you get to keep the left-right reading order: X | one() | two() | three() | four() (Sorry for poor formatting. Can’t find the backtick on my mobile keyboard.)
- apta 7y agoLanguages that have UFCS or extension methods also support this.
- msla 7y agoDepending on how you define "functional", it's been on the rise for decades. It's just that the goal is a horizon, not a point. For example: Does the language support recursion? It's functional by the old standard. Does it have first-class functions? Ditto, but by a newer one. And so on. These days, it seems to be about making functional composition a major theme, often with side-effect-free styles being favored. Maybe later it'll shift further, to languages derived from Coq or Agda being the things people mean when they talk about functional programming languages.
- incompatible 7y agoRecursion with tail-call elimination, so that you don't run out of stack, still isn't common in mainstream languages.
- phonebanshee 7y agoC++ and the dotNet CLR (so C# etc) support tail recursion elimination. That's a big percentage of widely used languages.
- incompatible 7y agoI didn't know about C++ (and not familiar with dotNet). I suppose it's not in the C++ standard, but apparently some compilers do it, perhaps as a side-effect of optimisation. So it may work in some cases, when using certain compilers, and not others. https://stackoverflow.com/questions/34125/which-if-any-c-compilers-do-tail-recursion-optimization https://stackoverflow.com/questions/34125/which-if-any-c-com...
- tempguy9999 7y ago> Does the language support recursion? It's functional by the old standard That's a strange claim, that recursion alone would suffice. I'd say first class functions are more central. Give me that, recursion and closures and I can do the rest. IMO anyway.
- incompatible 7y agoWhich functional language is the closest to being "mainstream"?
- runT1ME 7y agoScala
- seanmcdirmid 7y agoThere is a lot of merit in just using Scala as a better Java, in fact a lot of its original popularity was that kind of adoption. These days, however, Kotlin is eating that cake, so maybe it's true now?
- incompatible 7y agoI'd prefer something that compiled to machine code anyway. Scala also has type erasure unpleasantness due to its dependence on JVM.
- seanmcdirmid 7y agoType erasure isn't just a JVM implementation detail. Haskell, for example, has no JVM dependency and relies solely on type erasure (a running program can't introspect type information). Not reifying types at run-time (in this case type parameters) is a legitimate implementation decision as reification has non-trivial costs.
- deleted 7y ago[deleted]
- yen223 7y agoDepending on how loosely you want to define "functional", I'd argue Javascript is more popular.
- tempguy9999 7y agoI'm concerned about the use of functional languages. I like them but they seem to be treated as an implementation, not a model, and as such can be very inefficient. I've used scala and like it, but basic stuff with flatmap etc can unfold to be a gross mess of code which, had I written it non-functionally, would be vastly more efficient. I'd expect the compiler to do more work, perhaps with some additional hints from me, go get efficiency. SQL does this because it has to (not with total success) but with scala I have had to abandon 'nice' functional stuff, which I like, to get speed. It's disappointing. I shouldn't be doing the compiler's job. With a literal implementation, functional programming just seems inevitably expensive and that's morally wrong (just cos computers are fast doesn't mean cycles are free, and don't quote the programmer time vs computer time stuff at me, wasting hardware can become more expensive as the number of machines increases). Optimising FP to the extent I want is something I'm sure the compiler can't do alone; it would require hints and pointers from the human. The necessary optimisations would need to be from high-level functional transformations (eg. map f (map g x) -> map f.g x), down to eg. escape analysis so as to not produce so much garbage, down to data layout to stop the thrashing the cache. Also someone I worked with just went quite mad on it - FP should be used where it fits best, he forced us to use it exclusively, even when a procedural code would simply have been simpler and clearer. And faster too - the end result just crawled (it was scala btw). Not a fault of the paradigm but of overselling, and someone in dire need of common sense. Multiple disclaimers: I'm not a scala expert and haven't used it for a couple of years. I'm not an FP expert. What I've said above is a brief overview of a much larger and more nuanced criticism, which I don't have time to give; I hope you see what I'm getting at. I have little experience with other FP languages so what I say is based on scala and maybe eg. haskell does the clever stuff. Edit: tidied up.
- gizmo686 7y agoHaskell (or, rather GHC) does do clever stuff, and gets idiomatic code competitive performance with other languages [0]. The problem is that a lot of what GHC does require going full on functional in order to be practical. For instance, GHC can easily produce garbage of 1GB/s [1], which it can handle far easier than most languages because everything is immutable. GHC also does not have a stack [2]. You can get something resembling a traditional call stack, but it has a fair amount of overhead. > map f (map g x) -> map f.g x) This transformation only works with pure functions. I am currently using Scala, and it is (IMO) a very weird mix between FP and OOP. I is (IMO) much closer to Java style OOP than it is to Haskell style FP (not suprising considering its relationship with Java). It seems to inherit a lot of restrictions from Java that limit its ability to do the type of optimizations that are necessary for performant FP. If you actually look at the JVM call stack of a scala program, you will find an insane amount of indirection. More than even a naive implementation of FP in C (undoubtadly because of how it interacts with its object system and the JVM) [0] Although reasoning about performance is still pretty hard. [1] https://wiki.haskell.org/GHC/Memory_Management https://wiki.haskell.org/GHC/Memory_Management [2] Actually, it does have a stack, but the stack is no where near what you would expect.
- overgard 7y agoIt's interesting that functional programming is becoming more popular while functional languages are straggling behind a bit, but I think there's a contributing factor besides just imperative languages borrowing features: IO & error handling. What I find with functional languages is that once I start adding in a lot of IO and error handling things get gross really quick, ie things like print statement debugging are a "side effect", along with things like logging, and so on. If I was more of an expert in functional languages maybe I'd find a more elegant way around this, but I guess I find using something like python I get most of the benefits of a functional language without the aforementioned things becoming painful. From a practical standpoint, debugging by setting things like breakpoints and watching a value also becomes a bit more weird, because you have so many return values being pipelined through multiple functions without ever having an alias assigned to them, so how do you add a watch? Of course there's ways around that, but I wonder if dealing with practical problems like that turns a lot of people off. Especially since one of the things that draws people to write functional code is it's elegance, but it stops looking so elegant once you have a lot of these concerns in place.
- iamnothere 7y agoI think both models have their strengths. I use C# quite a bit (which itself borrows some functional paradigms for LINQ, Func, etc), but it definitely has some limitations, especially when you need immutability. I have started using F# to fill these gaps, and thanks to the CLR interop is pretty easy. Debugging is pleasant as well. Someone else mentioned Python/Coconut, which is another great combo that enables you to switch between functional and imperative styles as needed. I think this will probably become more common, especially in interpreted languages.
- nikonyrh 7y agoI have found great joy with Clojure (runs on JVM) which doesn't constrain you from side-effects, it just encourages you to do them on an appropriate functions. And all values are immutable by default. Debugging pure functions is quite a lot different than keeping watches on specific memory locations (or variables).