5 ms·
I don't want functional-flavoured programming, I want functional programming. Back when I was more into pushing Haskell on my team (10+ years ago), I pitched t
by mrkeen 2y ago
I don't want functional-flavoured programming, I want functional programming.
Back when I was more into pushing Haskell on my team (10+ years ago), I pitched the idea something like:
You get: the knowledge that your function's output will only depend on its input.
You pay: you gotta stop using those for-loops and [i]ndexes, and start using maps, folds, filters etc.
Those higher-order functions are a tough sell for programmers who only ever want to do things the way they've always done them.
But 5 years after that, in Java-land everyone was using maps, folds and filters like crazy (Or in C# land, Selects and Wheres and SelectManys etc,) with some half-thought-out bullshit reasoning like "it's functional, so it must good!"
So we paid the price, but didn't get the reward.
- hnra 2y agoUsing map, fold etc. is not the hard part of functional programming. The hard part is managing effects (via monads, monad transformers, or effects). Trying to convert a procedural inner mutating algorithm to say Haskell is challenging.
- mrkeen 2y ago> The hard part is managing effects You can say that again! Right now I'm working in C#, so I wished my C# managed effects, but it doesn't. It's all left to the programmer.
- codr7 2y agoI don't know, stacking monads is a comparable level of pain to me.
- jcadam 2y agoNever used monads with Clojure (the only Lisp I've done "serious" work in). Haskell introduced them to me, but I've never done anything large with Haskell (no jobs!). Scala, however, has monads via the cats or (more recently) the ZIO library and they work just fine there. The main problem with Monads is you're almost always the only programmer on a team who even knows what a Monad is.
- troupo 2y ago> I don't want functional-flavoured programming, I want functional programming. > you gotta stop using those for-loops and [i]ndexes, and start using maps, folds, filters etc. You mean what C# literally does everywhere because Enumerable is the premier weapon of choice in the language, and has a huge amount of exactly what you want: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable https://learn.microsoft.com/en-us/dotnet/api/system.linq.enu... (well, with the only exception of foreach which is for some odd reason is still a loop). > But 5 years after that Since .net 3.5 18 years ago: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=netframework-3.5 https://learn.microsoft.com/en-us/dotnet/api/system.linq.enu... > So we paid the price, but didn't get the reward. Who is "we", what was the price, and what was the imagined reward?
- mrkeen 2y ago> Who is "we", what was the price, and what was the imagined reward? Slow down and re-read. >> You get: the knowledge that your function's output will only depend on its input. >> You pay: you gotta stop using those for-loops and [i]ndexes, and start using maps, folds, filters etc.
- troupo 2y agoStill makes no sense. Once again: who paid, what was the price, what was the expected reward?
- tmtvl 2y agoWho paid: programmers switching to FP. What was the price: two things: - The programmers must stop using for-loops and [i]ndexes. - The programmers must start using maps/folds/filters/et cetera. What was the expected reward: the knowledge that their functions' outputs will only depend on their inputs. In short: programmers who change their behaviour get the benefit of certainty about specific properties of their programs.
- BeetleB 2y ago> You get: the knowledge that your function's output will only depend on its input. > You pay: you gotta stop using those for-loops and [i]ndexes, and start using maps, folds, filters etc. You're my type of guy. And literally none of my coworkers in the last 10 years were your type of guy. When they read this, they don't look at it in awe, but in horror. For them, functions should be allowed to have side effects, and for loops is a basic thing they don't see good reason to abandon.
- mrkeen 2y agoBut that's the irony of it, they did abandon the for-loops! Maps and folds and filters are everywhere now. Why? Because 'functional is good!' ... but why is functional good?
- zelphirkalt 2y agoStatistically most of ones coworkers will never have looked at and used to write actual code with a functional language, so it is understandable they don't get it. What makes me sad is the apparent unwillingness to learn such a thing and sticking with "everything must OOP" even in situations where it would be (with a little practice and knowledge in functional languages) simple to make it purely functional and make testing and parallelization trivial.
- BeetleB 2y ago> Statistically most of ones coworkers will never have looked at and used to write actual code with a functional language, so it is understandable they don't get it. I'm not against functional languages. My point was that if you want to encourage others to try it, those two are not what you want to lead with.
- tpmoney 2y agoOne struggle I’ve had with wrapping my head around using FP and lisp like languages for a “real world” system is handling something like logging. Ideally that’s handled outside of the function that might be doing a data transformation but how do you build a lot message that outputs information about old and new values without contamination of your “pure” transducer? You could I guess have a “before” step that iterates your data stream and logs all the before values, and then an “after” step that iterates after and logs all the after and get something like: ``` (->> (map log-before data) (map transform-data) (map log-after-data)) ``` But doesn’t that cause you to iterate your data 2x more times than you “need” to and also split your logging into 2x as many statements (and thus 2x as much IO)
- trealira 2y agoSo, do you mean like you have some big array, and you want to do something like this? (Below is not a real programming language.) for i in 0 to arr.len() { new_val = f(arr[i]); log("Changing {arr[i]} to {new_val}.\n"); arr[i] = new_val; } I haven't used Haskell in a long time, but here's a kind of pure way you might do it in that language, which I got after tinkering in the GHCi REPL for a bit. In Haskell, since you want to separate IO from pure logic as much as possible, functions that would do logging return instead a tuple of the log to print at the end, and the pure value. But because that's annoying and would require rewriting a lot of code manipulating tuples, there's a monad called the Writer monad which does it for you, and you extract it at the end with the `runWriter` function, which gives you back the tuple after you're done doing the computation you want to log. You shouldn't use Text or String as the log type, because using the Writer involves appending a lot of strings, which is really inefficient. You should use a Text Builder, because it's efficient to append Builder types together, and because they become Text at the end, which is the string type you're supposed to use for Unicode text in Haskell. So, this is it: import qualified Data.Text.Lazy as T import qualified Data.Text.Lazy.Builder as B import qualified Data.Text.Lazy.IO as TIO import Control.Monad.Writer mapWithLog :: (Traversable t, Show a, Show b) => (a -> b) -> t a -> Writer B.Builder (t b) mapWithLog f = mapM helper where helper x = do let x' = f x tell (make x <> B.fromString " becomes " <> make x' <> B.fromString ". ") pure x' make x = B.fromString (show x) theActualIOFunction list = do let (newList, logBuilder) = runWriter (mapWithLog negate list) let log = B.toLazyText logBuilder TIO.putStrLn log -- do something with the new list... So "theActualIOFunction [1,2,3]" would print: 1 becomes -1. 2 becomes -2. 3 becomes -3. And then it does something with the new list, which has been negated now.