3 ms·
> Mainstream languages are Java, JS, C++, Python, etc — languages one would hardly call “functional”. Even so, "functionalizing" mainstream languages is a thin
by dnautics 8y 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 8y 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 8y 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 8y agoOh yes, i meant that sometimes a paradigm is unused, not that one prevents the other.
- dnautics 8y 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 8y 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 8y agoI think actually Ruby is Alan Kay style but with an imperative surface implementation, itonically
- overgard 8y agoKinda. I think what Ruby is missing is how minimalist Smalltalk is and how many of the primitives in Smalltalk are implemented in itself. To give you an example of what I'm talking about, in Smalltalk an if statement is implemented by sending a message to a boolean, like: someBool ifTrue: [ "some block to execute" ]. In that context, either the "True" object receives the message and executes the block, or the "False" object receives the message and doesn't execute it. (Or even some user defined object that just happens to have an ifTrue: message handler). (Sorry I might be a little off on the syntax and exact details, my Smalltalk is rusty) Loops and such are also similar. Granted most of these things probably end up being implemented in native code on a practical level for performance, but conceptually it's a very small language. Most of the standard library and IDE is written in itself. Ruby definitely has a lot of flexibility, but it also has way more built-ins and much more separation between "interpreter code" and "user code" I think this is what people are missing when they bring up the point that messages and method calls seem very similar. In a practical sense yes, but the smalltalk style message system is more about messages being a fundamental building block of the entire system.
- mlevental 8y 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 8y 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 8y agoLanguages that have UFCS or extension methods also support this.