[HN Gopher] What's so great about functional programming anyway? ___________________________________________________________________ What's so great about functional programming anyway? Author : redbell Score : 334 points Date : 2022-11-16 08:34 UTC (14 hours ago) (HTM) web link (jrsinclair.com) (TXT) w3m dump (jrsinclair.com) | gehen88 wrote: | I read the whole thing waiting for the aha-erlebnis which never | came. I'm a full stack JS/TS engineer with a decade of | experience. I expected this article to be written for someone | like me. It didn't click, even though I already love and use | functional aspects like immutability and pure functions. I feel | like it's the whole new set of terminology that puts me off (and | I'm talking about `scan` and `Task`, not even `Functor` or | `Monad`). I have confidence I can learn and apply this in a few | weeks, but I can't realistically expect junior/medior devs to | quickly onboard into a codebase like that. | | Maybe I'm biased against "true" functional programming because | I've been on Clojure and Scala projects in the past (as a backend | dev) and both experiences have been good for my | personal/professional development, but a shitshow in terms of | long-term project maintenance caused by the enormous learning | curve for onboarding devs. The article talks about how great it | is for confidently refactoring code (which I bet is true) but | doesn't talk about getting people to understand the code in the | first place (which is still a requirement before you can do any | kind of refactoring). | | My only hope is for ECMAScript (or maybe TypeScript) to introduce | these OK/Err/Maybe/Task concepts as a language feature, in a way | which befits the language rather than trying to be "complete" | about it. We don't need the full spectrum of tools, just a | handful. | jerf wrote: | "I read the whole thing waiting for the aha-erlebnis which | never came." | | This is increasingly my go-to metaphor for this: This article | and many of its kind are talking about bricks. They really like | bricks, because they're square, and they come in several nice | colors, you can really bash a clam with them, they're cheap, | they're quite uniform, they make great doorstops and hold down | stacks of paper really well, and they have a personal | preference for the texture of bricks over other possible | building materials. These people think bricks are great, and | you should incorporate them into all your projects, be it steel | bridges, mud huts, a shed out back, a house, everything. Bricks | should be everywhere. | | Then they build you a tutorial where they show how it looks to | build a mud hut, and how nice it is to put some random bricks | in to it. Isn't that nice. Now your mud hut has bricks in it! | It's better now. | | But that's not what bricks are about. Bricks are not about | textures or being good at bashing open clams. Bricks are about | building walls. Walls that may not be the solution to every | wall, but certainly have their place in the field of wall | building because of their flexibility, easy of construction, | strength, cheapness, etc. Trying to understand bricks out of | the context of using them with mortar to build walls is missing | the point. | | Contra the _endless_ stream of tutorials that make it look like | functional programming is essentially mapping over arrays and | using Result /Option instead of error returns, that is not what | functional programming is about. That is a particular brick | functional programming is built out of. It isn't the only | brick, and if you scan a _real_ Haskell program, isn 't even | necessarily one of the major ones in practice. They turn out to | be a specific example of a very simple "recursion scheme". | These simple "bricks" show up a lot precisely because they are | so simple, but generally the _architecture_ layer of the | program is built out of something more interesting, because | "map" turns out to be a very small and incapable primitive to | build a real program out of. | | In my considered opinion and experience, if you spend time with | "functional programming" and come away thinking "oh, it's about | 'map' and 'Result'", the point of functional programming was | completely missed. | | And stop telling people that's what it's about! You're putting | a bad taste in everyone's mouth, because when all the | imperative programmers look at your so-called "functional" code | in imperative languages and say, "That's a nightmare. There's | all this extra stuff and noise and it's not doing anything very | useful for all that extra stuff."... _they 're completely | right_. Completely. It is a net negative to force this style in | to places where it doesn't belong, quite a large one in my | opinion. And especially stop being sanctimonious about they | "don't get it" when people object to this style. It is the one | advocating this style where it does not belong that does not | "get it". | | The worst thing that can happen in one's education is to | _think_ you 've been exposed to some concept when you in fact | haven't, and come away with a wrong impression without | realizing there's a right one to be had. I still encourage | curious programmers to clock some serious time with real | functional programming to learn what it is about. This style of | programming isn't it, and your negative impressions of this | style don't necessarily apply to real functional programming. | (It does some, perhaps, but probably not the way you think.) | Verdex wrote: | _Part 1: The request_ | | Do you have a writeup that you can point me towards that goes | into detail about why functional programming isn't about | map/reduce/filter and is instead about reconceptualizing your | entire program as consisting of recursion schemes[1]? | | I'm asking because I've been working with FP languages for 15 | years now and the first time I've seen this point of view is | from your comments. [Although, I suppose you sort of see a | half formed version of this in the little schemer and | seasoned schemer books. But just not enough so that I would | consider it the point they were trying to make sans your | comments.] | | _Part 2: Furthering the discussion_ | | Of course personally, FP isn't a single well formed idea or | philosophy any more than a hurricane is a well formed entity. | Just a bunch of dust and wind going in the same direction. As | with all other programming paradigms. I'm perfectly happy | with the true soul of FP being some reconceptualization of a | program into recursion schemes because my plan, as with all | paradigms, is to pick and choose the individual conceptual | motes and mix them together in a way that allows me to best | solve my problems. | | I actually dislike what I think you're saying recursion | schemes are for a similar reason as to why I dislike excess | shared mutable references and loops with excess mutation. It | places the programmer into a sea of dynamic context that must | be mentally managed in order to understand the meaning of the | program. Meanwhile, map/reduce/Result, places the programmer | into a static reality where all meanings have computer | verifiable proofs associated with them. | | My version of FP doesn't have recursion or loops. Just | map/reduce/ADT and functionality that allows you to convert | recursive data into lists and lists into recursive data. | Maybe that doesn't make it 'true' FP. Which doesn't bother | me. | | [1] - https://news.ycombinator.com/item?id=33438320 > | Reconceptualizing your entire program as consisting of | recursion schemes and operations that use those recursion | schemes, what I think the deep, true essence of functional | programming as a paradigm is | [deleted] | zmmmmm wrote: | Always makes me sad that Scala got sucked into the pure- | functional priesthood type culture rather than the "better | Java, by being mostly functional and immutable and then | practical as hell when appropriate" pathway. I _really_ like | coding using Scala but the way I like to do it feels totally | non-idiomatic. | piaste wrote: | > "better Java, by being mostly functional and immutable and | then practical as hell when appropriate" | | That's very much what Kotlin is aiming for. | gehen88 wrote: | So true. I was involved in two very different Scala projects. | One was the sensible "better Java" way, which was mostly | great. The other was a big enterprise project with a core | group of "hardcore" FP enthusiasts which was very stressful | because of imposter syndrome and troubles to onboard new | folks. I have been against Scala ever since, exactly because | of this FP cult. | wiseowise wrote: | > The other was a big enterprise project with a core group | of "hardcore" Spanish enthusiasts which was very stressful | because of imposter syndrome and troubles to onboard new | folks. I have been against Spanish ever since, exactly | because of this Spanish cult. | davedx wrote: | Scala tried to be too much. Too many paradigms. Too much | flexibility and power. Many people might think they want | that, but a subset are probably going to have an easier, | happier life choosing a less powerful language... | piaste wrote: | > I read the whole thing waiting for the aha-erlebnis which | never came. I'm a full stack JS/TS engineer with a decade of | experience. I expected this article to be written for someone | like me. It didn't click | | Don't worry, it's not about you. The article is genuinely | underwhelming. | | It walks you up the abstraction tree to the building of higher- | kinded types, but then just handwaves it with 'and now you can | do a lot of things!' but doesn't show them. | | It needs a final part where the flexibility is displayed. | Something like 'if (debugMode) | runpipeline(synchronousDebugWriter) else runpipeline(promise)'. | davedx wrote: | OK/Err/Maybe can be trivially implemented with TypeScript, _if | the project development team wants them_. We have it in the | current project I work on and it works well with GraphQL. | | For OK/Err, in my experience it kind of depends on "how happy | is your dev team with using exceptions for general purpose | errors"? The orthodox school of thought says "exceptions only | for exceptional errors", in which case things like OK/Err give | you a nice way to structure your control flow and its typings. | | `Maybe` is used by `graphql-code-generator` to explicitly mark | optional typings in generated TypeScript types for a GraphQL | schema. I don't think it's necessary (TypeScript has `?` after | all) but some people prefer it. | Cthulhu_ wrote: | I've used patterns like that in Scala; I see their value in | building a correct system etc etc etc, but only if it's | consistently used throughout the codebase. | | As it stands, most JS/TS projects aren't very consistent to | begin with; error handling is either not done at all (let it | fail), or a mix of exceptions, failing promises, error | responses / types / states, etc. | | But that's not really down to the language, more the | underlying culture. | substation13 wrote: | > My only hope is for ECMAScript (or maybe TypeScript) to | introduce these OK/Err/Maybe/Task concepts as a language | feature | | When using these concepts the need for do-notation comes up | pretty quickly. It would be like using JS promises without the | async keyword! | | Of course, follow this to it's conclusion and you will have a | functional language. | Cthulhu_ wrote: | I mean they could ADD it, just like nowadays individuals can | choose to implement it themselves, but it wouldn't supersede | any existing error / result implementations (success/error | callbacks, throw/catch, promises which use both, etc). | | To improve or change a language, I think you should get rid | of another feature if it solves the same problem, instead of | add another option. | substation13 wrote: | > just like nowadays individuals can choose to implement it | themselves | | I don't think this is possible with JS right now? | ravenstine wrote: | Maybe I'm wrong, but I think all the talk around weird ideas | like Functors, Monads, etc., are mostly red herrings and aren't | that applicable to most everyday software engineering tasks. | | Just use functions, avoid state, use map/reduce if it's more | readable, avoid OOP most of the time (if not all of it), avoid | abstracting every single damn thing, and what you're writing | seems functional enough even if it doesn't totally satisfy the | academic view of what functional programming is. | PartiallyTyped wrote: | > Maybe I'm wrong, but I think all the talk around weird | ideas like Functors, Monads, etc., are mostly red herrings | and aren't that applicable to most everyday software | engineering tasks. | | They are a red herring. In most cases, all you need to know | about a monad is that it defines some kind of transformation | of the enclosed data by applying a function onto it that | returns a Monad of the same kind. | | e.g. the list monad [a] says that if you bind it with a | function f: a -> [b] (ie a function that takes a value and | returns a list of b), the monad will transform to [b] by | concatenating the lists. | | the maybe monad Maybe[a] says if you bind it with a function | f: a -> Maybe[b], if Maybe has type Some(a), the data of the | monad is replaced by the result of the function. If the monad | has type Nothing, then it retains nothing. It's no different | to | | a = f(a) if a is not None else a | | So a monad is just an object that defines the transformation | of the underlying data when applying a function that returns | a monad of the same type, nothing more. | fleddr wrote: | I couldn't agree more. I feel that some thought leaders | debating intellectual concepts in computer programming have no | idea how real world software development takes place these | days. | | Developers are under enormous time pressure to deliver. They | face an exponential skill curve as their scope is massively | broad (i.e. devops). Things need to be shipped fast, time for | "proper" engineering is compromised. Team members working on | the codebase are of various skill levels and ever changing. | Finally, a lot of products being worked on have a limited shelf | life. | | For 80% of developers worldwide, the concepts discussed in the | article are too steep, and therefore unusable. | itronitron wrote: | I've occasionally watched colleagues give presentations on | functional programming over the years, and while I can see | why certain people are drawn to it the stated benefits of | functional programming have never seemed that significant to | me. The advantages that FP provides aren't likely to be | needed by developers that are capable of learning it. | wokwokwok wrote: | I've had similar experiences with scala and clojure | professionally. I now actively oppose people attempting to add | functional code to projects I work on. | | ...because when they say "more functional" most people mean: | | I want less code. | | I want the code to be shorter, because I'm lazy and I want it | to be all on one screen. | | ...but that's _actively harmful_ to almost any code base. | | You want simple code, _not_ dense complicated code. Dense | complicated code is for people who wrote the code, and a few | smart talented people. Other people have to work on the code | too. They cannot. | | Actual functional code doesn't strive for code density, it | strives for code _purity_ and algebraic structures. | | That's fine. Do that. | | Dense map reduce reduce flow reduce functions can die in a | fire. | rfrey wrote: | I think you're conflating "readable" and "uncomplicated" with | "familiar". I'm equally infuriated by OO code with | dependency-injected-everything from some hidden framework | configured by fourteen XML files somewhere in a different | file tree, interfaces for every single class even if only | instantiated once, factories to create builders that make | adapters. | | Maybe if I stared at it for twelve years it would become | familiar and I would begin to think it was simple, readable | and maintainable. | jghn wrote: | Yeah. Sure, "simple" and "complex" sorta have an objective | definition. But colloquially "readable", "simple", | "complicated", etc have a tendency to track with "things | with which I'm familiar/comfortable (or not)". | | Over the decades I've come to the conclusion that there's | no such thing as a one size fits all sweet spot on this | stuff. Different people are going to have different | experiences with what they find straightforward or not. | They will have different backgrounds, mental models, ways | of perceiving the world. It all adds up. As a profession we | need to understand this reality and find ways around it | instead of getting into dogmatic arguments as if there's | One Right Answer. | | Common example I give - the GP complained about FP | advocates wanting code to take up less screen space. I have | come across many devs who struggle with concise code, and | many others who struggle when code is _not_ concise. | Similarly, I have come across plenty of devs who start | having trouble when code is spread out (sometimes that | means within a file, across files, both, etc). I have also | come across plenty of devs who have trouble when it 's all | pulled together. | bigDinosaur wrote: | Pretty poor attitude to just adopt so generally. I've seen | 'actively harmful' qualities from all paradigms. Once peoples | start adopting attitudes like yours they've just become the | mirror of the condescending FP type and just kill outright | any of the really cool features that are useful, as well as | any discussion of them. | wokwokwok wrote: | Do whatever you want with your own code bases; my | responsibility is to make the ones I work on maintainable | by other people. | | /shrug | cpursley wrote: | Some of us don't find OOP code maintainable. | wokwokwok wrote: | It doesn't matter if it's FP or OOP. | | Here's the real question: do you think dense code is more | maintainable? | | Generally yes? More than a verbose multilayered | abstraction, probably? | | ...but where do you draw the line? Map statements instead | of for loops? Collapse all the white space onto a single | 200 character line? Make everything one big regex? | | Dense code means that every change has more impact, | because there's _less code_ ; it's unavoidable: less code | to do the same work means more impact from changing any | part of that code. | | That is why it's difficult to maintain; because you can't | touch it without making side effects; you can't reason | about a _small part_ of the code, because the logic is | dense and difficult to unpack into small parts. | | Certainly OOP can often be verbose and _annoying_ , but | that's a different thing. | | Code density and _being functional_ are orthogonal; some | OOP is too dense too. ...but _generally_ I've found that | inexperienced people see _density_ and strive for it, | believing this makes it functional; but the truth is the | opposite. | | Good functional programming is often naturally _concise_ | , but most people don't actually seem to understand FP. | | They just seem think it means to put more "reduce" | statements in the code, remove for loops and generally | make the code harder to debug and denser. | | ...in my, limited experience, working with lots and lot | of different folk at many different organisations. | cpursley wrote: | For me it's not density - it's the OOP class abstractions | and all that. I'm not smart enough to keep up with it vs | the FP approach of just doing data transformations. | CharlieDigital wrote: | I think of OOP done well at a high level as "structural | logic". Whereas in FP, one might use `map()` and `bind()` | to replace complex imperative logic flows, in OOP, this | is done with object hierarchies and structures. | | When you have a abstract base class or an interface that | defines some contract, it's creating a "shape" that | defines how an instance of the class can be used. | | I think that this might be why some folks have an | affinity for OOP and some have an affinity for FP. OOP | affinity might be tied to more visual thinkers. For me, I | see the "shapes" of the code defined by the contracts. | delta_p_delta_x wrote: | Perhaps the snark caused the down-votes, but your point is | legitimate. 'Pure FP' languages _encourage_ code that is | nearly unreadable and unparseable without any additional | context (and sometimes, unreadable even _with_ said context). | There is some strange desperation for extreme terseness in | pure FP languages like Haskell, Ocaml, Idris, etc. | | Single-character variables and functions, point-free style... | Coming from an imperative background, this just seems like | flexing for the sake of flexing. | | Why not make life a _little_ easier by clearly naming | variables? This isn 't maths or physics where variables (for | some equally-inane reason) _must_ be one character. We have | 4K screens today; I can accept lines that are significantly | longer than 80 chars. | BigJono wrote: | There's nothing wrong with single character variables if | you're not using them like a complete idiot. A line like | reports.map(r => whatever) makes it blatantly obvious that | r is a report. | toastal wrote: | When your code is sufficiently abstract, there often really | aren't better variable names than a or x. My experience is | that it's about the scope for that variable. If it's in a | one-line lambda, then it'll be one letter. If it is going | to be used in the next 10 lines or so, make an abbreviator. | And it's longer, or particular unclear, spell it all out. | Adding extra words don't make | BusinessAbstractFactoryIBuilder more readable. | delta_p_delta_x wrote: | > BusinessAbstractFactoryIBuilder | | While I understand and agree with this meme[1], I think | that's the other extreme, where everything is a Factory | Builder thing. | | Even so, I would rather too much information than too | little, which is what FP programs tend to do. Over- | abstraction is _also_ a problem, in my view. Even in a | LINQ lambda, for instance, I might write | someEnumerable.Select(what_is_actually_inside => | doSomething(what_is_actually_inside)) | | rather than someEnumerable.Select(x => | doSomething(x)) | | [1]: https://github.com/EnterpriseQualityCoding/FizzBuzzE | nterpris... | toastal wrote: | Funny example since currying requires no intermediate | variable to speak of someEnumerable |> | select doSomething | c-cube wrote: | Fwiw, OCaml doesn't chase extreme terseness or point-free | programming. It's not really equipped for that. | | OCaml is designed for straightforward code centered on | functions, modules, and a mix of imperative and immutable | data structures; all with a _really_ simple execution model | and a pretty nice type system. The core language has very | few quirks and it's easy to predict what it does, and how | efficiently. | | There's not really any comparison with Haskell, which | emphasizes heavy optimizations to make its terseness, based | on lazy evaluation, work well in practice. | sunwukung wrote: | I think this is about the level of abstraction. As React | component extraction is to tag soup, so named functions | composed are to fp primitives. In code reviews, if I see a | big swamp of pipe/fold/cond etc at the top level, I'd kick it | back and ask that to be wrapped in a named function that | explains what it does, rather than exposing it's guts. | | Writing concise, clear code is a skill that straddles any | paradigm. | substation13 wrote: | Alice: I don't need functional programming, I am very productive | in my imperative / OOP language. | | Bob: What about feature x? | | Alice: Oh yeah, well we can add that! It has proven useful. | | Bob: Agreed! But now do you see the need feature Y? | | Alice: Hmm good point. Feature X would be better with Y. Let's | add that too. | | (Repeat n times) | | Alice: Isn't my language pretty nice now? | | Bob: I have to agree, it's very productive | | Alice: See I told you, we don't need an FP language after all! | | Bob: ... | draw_down wrote: | gspencley wrote: | > To hear some people talk about functional programming, you'd | think they'd joined some kind of cult. | | I know that the chapter and book is coming at the topic pro- | functional, and there's nothing wrong with that. But boy does | that sentence ever ring true as a web application developer. | | Functional brings with it a set of core principles and values | that provide a lot of benefit. So does OOP. So does procedural. | I'm a fan of always picking the right tool for the job at hand. | | And JavaScript certainly brings with it a set of Functional-style | features. Use them liberally, by all means. Let them solve | problems for you. | | As a professional, learn as much as you can about Functional | programming. Also learn as much as you can about OOP, imperative | approaches, procedural and any topic that will be relevant to | your field. Be the best professional that you can possibly be. | | But don't, for the love of God, try and write a modern frontend | web application written in JavaScript (or TypeScript) from a | "purely" Functional-first point of view. Not only will you fail | and hate your life, but you will throw out lots of babies out | with tons of bathwater. | | JavaScript is not even close to a Functional language. It doesn't | matter if Brenden Eich wanted to bring Scheme to the browser, | that's not what he ended up creating. Not only is it far from | being a functional language, but I would argue that it is | downright antithetical to FP. | | - Everything is mutable, even functions themselves. | | - Not only does JS have a mutable global namespace but modern | frontend apps often have to deal with no fewer than FOUR shared | global states (the global namespace, state management a la redux | store, local storage and server-side persistence). If cookies | still count then that might count as five. | | - Functional programming favours primitive types. Simplicity is | the name of the game. Most client/server applications have to | deal with rich domain models. Functional is great for | "processes", while OOP is great for "things" (i.e: rich domain | modelling and grouping data structures with the logic that | mutates them). | | - The asynchronous event loop brings side effects as a core | feature of the language. Yes we have strategies for putting our | side effects in a corner, but the entire application is built | from the ground up around the concept of side effects. Instead of | taking simple input and producing simple outputs, we send things | off to the server and/or update a global state store so that | multiple things can update independently. Side effects are | integral to a complex web application, no matter how many pure | functions you write (and you should write pure functions whenever | you can, they are simple, easy to test and debug). | | None of the above should matter except that I have come across | many FP "purists" who come at the topic from a borderline | religious angle. | | I think this has to do with how trendy our industry is. OOP was | very hot in the 90s and 2000s. FP start to gain traction, at | least in web development, with the advent of Scala and Douglas | Crockford's JavaScript The Good Parts. There is a tendency in our | industry to think in binary terms. That we saw lots of problems | with OOP applications and FP is promising a solution therefore | OOP = evil and FP = a gift sent down form the heavens to save our | souls. Nothing could be further from the truth, and trendiness is | the root of all evil IMO. | z9znz wrote: | Following TFA example, I think there's one more step that could | be done (or could have been done earlier in the processes) - | shift from piped, hardcoded function calls to a data structure | which describes the transformations you would like to have | applied to your data. | | With just a few lines of mini-framework definition code, you can | define a Processor system which behaves like a pipe+reducer but | takes a simple data structure of initial value, functions to call | with that data (and optional additional params), with the result | of each step fed to the next. But in the framework you take the | output of the previous step and update the results key within a | process hash. Or if the function fails, you can accumulate errors | in an errors key-value pair. Steps can have flags, such as :hard- | fail, etc. which the Processor can use to affect special | behaviors (like a rollback or a termination) based on errors in | steps. | | This enables really dynamic programming options as the | transformations to be done on the data can be determined at | runtime based on formulas or process generators. | | It might seem that such an approach would make debugging and | troubleshooting more difficult, but because you can add | additional metadata and debug behaviors to the Processor steps | data, tracing exactly what has occurred in very easy. | | The only downside I've found from this approach is that IDEs may | not "see" your call to functions as they are just references or | strings/symbols (depending on the language). | Cort3z wrote: | As far as I can tell, with this async approach you lose what I | consider to be one of the main benefits of the native async | functions; If an error is thrown, it will show up in the "reject" | path of your promise. This is very useful and is great at | preventing "random code" in some Task from causing havoc. | | I'm not that great at functional programming, and perhaps this is | a non-issue, but I feel there could be some way to achieve the | same results without losing that ability. | onetom wrote: | omg, so much negativity here... | | you should read the previous articles from the author, eg. | https://jrsinclair.com/articles/2022/javascript-function-com... | | then | | https://jrsinclair.com/articles/2022/what-if-the-team-hates-... | | it would address some of the concerns and critiques ppl had on | this topic here. | zactato wrote: | Obviously the best part of Functional Programming is arguing | about the pedantics of whether a language or construct is | actually "functional" | germandiago wrote: | I think functional programming is great for scaling systems but | too restrictive since everything is immutable... | | So the model of the future, in my opinion, should look more like | https://www.val-lang.dev/ | | https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p26... | | This is less restrictive and also very scalable IMHO. Value | semantics, basically. | mrkeen wrote: | > I think functional programming is great for scaling systems | but too restrictive since everything is immutable... | | It's how things work at scale too. | | Source control? Git | | Money? Ledgers | | Distributed computing? Map-reduce, Raft | germandiago wrote: | Lol. True. Concurrency without stopping... | FpUser wrote: | >"What's so great about functional programming anyway" | | Well, nothing. It is one of many paradigms. It has its place just | as the others. | sbf501 wrote: | One downside is that you now have a much higher bar for hiring | talent to do trivial things. If you don't understand FP at a | fundemental level, you will have a hard time finding people to | work on your systems. Junior/Senior engineers are meant to be | fungible to an extent. This ups the bar to a new language: this | ain't JavaScript, it's an entire conceptual framework on top of | JavaScript. | revskill wrote: | Instead of | | const x = await promise1(someArgs); const y = await | promise2(someFunction(x)); | | we do | | function processor(monad, args) { return | monad.unit(promise1(args)).map(someFunction).then(promise2) } | | That's all about monad. | dna_polymerase wrote: | Just a quick heads-up, if you want to truly write functional code | in the browser there is js_of_ocaml available in the OCaml realm. | With Bonsai [0] a somewhat usable framework for webapps is | available now, too. There are drawbacks like filesize, but if you | don't have to serve the webapp on the first pageload it shouldn't | be a problem. | | [0]: https://bonsai.red | davidgl wrote: | Or https://fable.io in the F# world, which is production ready | and excellent | koonsolo wrote: | This post actually confirmed my opinion about functional | programming :D. Probably not in the way the author intended. | valtism wrote: | When talking about writing lodash V5, jdalton (the creator) said | "No FP wrappers. That fad is over. RIP your co-workers if you | introduced that headache into your codebase. Definitely not team | or human friendly." [1] | | To me, this sort of FP seems to have been tried and have failed. | I don't know how much of this is due to the language and how much | is due to the pattern not working well in the minds of | developers. I wonder if JS had an implementation of something | like C#'s LINQ if it would be easier to write code this way. | | [1] https://twitter.com/jdalton/status/1571863497969119238 | neonate wrote: | What does FP wrappers mean in this context? Someone asked that | in the Twitter thread and got a snarky response, but I have the | same question. | rahkiin wrote: | I think they mean what in lodash was _() and .values(): You | could not execute map or filter on an array. Instead you | needed to wrap it in a lodash object using _(arr), that you | can call .filter() on. To get the filterd values out, you | call .values() on its result. You could also do _.filter(arr, | ...). To me it became confusing when I needed to use .values, | and when nesting it got worse. | | LINQ has instead methods on its types that partially returns | builders. I find that easier to use, and using C# type system | it calculates the result types for me. | throwaway0asd wrote: | I am not a zealot as the book might describe. My personal goals | are portability and predictability of code. Use of functions with | defined return types in TypeScript, as opposed to other | containers by reference, allow me to achieve those goals. | | Most of my functions though are void functions (returning null or | undefined). It's not essential to me that functions must return a | value, in defiance to functional programming, so long as like | instructions are grouped in a single container that may be called | by reference. | SPBS wrote: | `sanitizeMessage` is literally just `message.replace(/</g, | '<')` but it has to go through a ton of abstractive | scaffolding just so that it can fit into a single call to map(). | This is like jumping through a ton of generic classes in Java | only to find a single one-liner implementation at the end of the | call. | z9znz wrote: | I believe that his example for sanitizeMessage() was just to | give an example. An actual sanitizer would be much more | complex, and thus having a separate function defined to do that | one thing would be more obviously reasonable. | | But you DO want these single responsibility methods like | sanitizeMessage()! Having single responsibility functions like | this means you have more Legos you can use where you need. You | get improved code reuse. Perhaps more importantly, you also get | functions which are easy to test and test more thoroughly than | if you have a big procedure which does a lot of things. | allenleein wrote: | For example, Haskell is optimized for developer efficiency. You | can get a lot done, have a high degree of confidence that it runs | reasonably without having to do too much thinking or ass- | covering. We move fast & need things to be reliable. Rather than | hand optimizing Doom 2 for ___. | toolslive wrote: | In the end, it boils down to the insight that functions compose | better then classes, interfaces and objects. Also, OO and | relational databases don't like each other. On the one hand you | have trees as basic weapon for composition while on the other | hand you have something where a tree is taboo. | dmitriid wrote: | Before you come at me with pitchforks: I built commercial | software with Erlang. I use functional(-style) programming in all | languages that support it. | | I skimmed through this, and came across this gem: | | > If there happens to be an error, we log it and move on. If we | needed more complex error handling, we might use other | structures. | | Yes. Yes, we _always_ need more complex error handling. So, show | us the "other structures". It may just turn out to be that the | regular chain of `.map` calls with a try/catch around it is | easier to understand and significantly more performant than any | convoluted pile of abstractions you've built. | | And then you'll need to handle different errors differently. And | then you'll need to retry some stuff, but not other stuff etc. | | > this is a sample chapter from my upcoming book: "A skeptic's | guide to functional programming with JavaScript." | | The problem is: it does a poor job of convincing skeptics why | this is great, or even useful. _I_ use, or used to use, | functional programming, and _I_ am not convinced by this chapter. | wruza wrote: | _show us the "other structures"_ | | "Left as an exercise to the reader". Reminds me those well- | known tutorials with todo lists and other trivial nonsense, | because a fully-fledged example would seed doubt of selling | points instantly. | | Sometimes I think that our area is one big IKEA store. You look | at nice pictures, buy into the crap, and still feel okay | because you've built most of it yourself. Not getting that this | built-yourself part makes you relate to it much more than the | initial advertisement or the practical value. | dmitriid wrote: | > "Left as an exercise to the reader". | | Or "draw the rest of the owl" :) | zelphirkalt wrote: | One thing FP, learned with a suitable language at hand, teaches | is, how often we unnecessarily reach for classes, when simple | pure functions will do just fine and will be better testable, | easier to understand, more reusable, and more composable. | eurasiantiger wrote: | In my opinion, FP is one tool in the toolkit, and is best suited | for expressing math-oriented things such as set operations, | analytics pipelines, and other kinds of data transformations. | | For any kind of I/O, though, FP is not even applicable in the | pure sense of a function, as disk/network access is inherently a | side effect. JS can of course mask much of this with its | Promises. | | Any kind of business-oriented logic, especially handling of | different kinds of data entities, is still good to encapsulate in | classes in OOP fashion. Classes are a good impedance match for | entity types, as instances are to entities. | | Sometimes, though, data transformations are still more clearly | expressed in imperative style. At the very least they're more | accessible to juniors, unlike the innards of declarative | transformation engines. | | Imperative, declarative, object-oriented, functional -- these are | all tools for different jobs. | mrkeen wrote: | > For any kind of I/O, though, FP is not even applicable in the | pure sense of a function, as disk/network access is inherently | a side effect. | | If you meant to do it, it's an _effect_ , not a _side-effect_. | eurasiantiger wrote: | That's not what is meant with side effect in this context. | valenterry wrote: | Pure FP is most useful for IO. Without IO, the concept of pure | FP doesn't make any sense. | eurasiantiger wrote: | That doesn't make sense. Any function doing I/O is not pure | by definition, and while using an IO monad can shift the | impureness a bit to make a function behave as if it was pure, | it is not pure and cannot ever be pure. Can you explain? | valenterry wrote: | Yeah - I think from your explanation I can only deduce that | you don't know the actual definition and concept of pure | functional programming. Note that the term "functional | programming" has been watered down over time and now pretty | much means "use .map and .filter instead of a loop" etc. | Historically the meaning was different though, see: | https://en.wikipedia.org/wiki/Purely_functional_programming | | With pure functional programming you essentially treat IO- | effects as a first class concept and make them explicit in | your code. You never "execute" IO directly but compose | small "blueprints" for doing IO into bigger blueprints and | so on until you end up with one big blueprint which is your | application. You then pass it to the runtime ("main() { | ...; return bigBlueprint; }") and then it gets executed. | | In other words, pure functional programming treats IO as | more important compared to "regular programming" and | without any IO there would be no need to even do so. But | without any IO, a program wouldn't have any meaning, | because you want _at least_ expose the result of a | computation to the outside world. | eurasiantiger wrote: | I do understand the concept of pureness in the | appropriate context. However, it appears that you do not. | | This is a good example, since it ticks all the boxes: | there is imperative code run as a side effect of | executing the code output by the compiler based on | declarations in functional style, including those | _impure_ IO-using functions. | | Slapping IO on your function is making it explicit that | it is impure. | alexmolas wrote: | > One of the people I showed this to had an interesting reaction. | Their response was something like: "Hey! I like functional | programming because I'm lazy and incompetent. It's about all the | things I don't have to think about." | | I can perfectly imagine the most intelligent and smart people | I've ever meet saying this same thing. | googlryas wrote: | Maybe a key part of intelligence is merely knowing your own | limitations. | masklinn wrote: | Or offloading so you have more room for the useful stuff. | EnKopVand wrote: | There is a Danish scientist called Morten Munster who's done | research on behavioural science. He's written a book for | management that's gotten very popular in my country, and this | is how I became acquainted with it back when I did a stint in | management (which around here includes getting a MBA type | education in management). | | Aaaaaaaanyway, in it he talks about two ways of how we function | as human beings, and I'm likely presenting this wrong, but one | is basically the best practice theoretical mode of being and | the other is how we operate at 17:00 on a Thursday after a week | where both our children have been sick, and the overlaying | message is that everything that isn't designed for the second | mode of being is likely going to fail. Now the way it's | presented in the research and the educational material, this | has nothing to do with programming. It's more along the lines | of companies needing to formulate missions and goals that | aren't corporate bullshit, because nobody understands corporate | bullshit when they are faced with an angry customer some late | Thursday afternoon. | | After a few decades in SWE, however, I've become sort of a fan | of designing software for that 17:00 Thursday EnKopVand | mindset, and functional programming helps a lot in that regard | because it kills soooo many of the complexity pitfalls that you | really don't want to deal with when you're tired, lazy and | incompetent. Of course the other side of this is that I'm not | religious about functional programming either, I rarely write | classes these days, but if there is a good reason to write one, | I will. | jiggawatts wrote: | Some systems require eternal vigilance to not blow up in your | face, others shepherd people towards the pit of success. | jsrcout wrote: | So true. Same with languages IMO. Not naming any names :-) | gehen88 wrote: | Wow, I was really expecting this to be an argument against | FP, until it wasn't. I love the concept of designing for | Thursday 5pm though. My approach to achieve that is simply | different (it doesn't include FP). | danieldk wrote: | _After a few decades in SWE, however, I 've become sort of a | fan of designing software for that 17:00 Thursday EnKopVand | mindset, and functional programming helps a lot in that | regard because it kills soooo many of the complexity pitfalls | that you really don't want to deal with when you're tired, | lazy and incompetent._ | | It's so funny, because I thought your comment would lead to: | when it's 17:00 on a bad day, I'd rather debug some Go code | that is perhaps mundane but easy to follow than a chunk of | Haskell code of a colleague that drank too much category | theory kool-aid. | | Which goes to show that what one wants to debug at 17:00 on a | bad day is very personal? | jolux wrote: | > Which goes to show that what one wants to debug at 17:00 | on a bad day is very personal? | | It really depends, it's possible to write mundane, simple | functional code (though I think more common in OCaml and | Erlang than Haskell) but much of the community is sort of | very excited about all this higher-order stuff that might | be great but is not quite as useful and obvious as the core | primitives of algebraic data types and pattern matching. I | imagine a lot of people probably felt similarly about the | Design Patterns craze with OOP: it's not that OOP isn't | useful, just that inheritance is maybe not what you want | most of the time and not everything needs to involve design | patterns. | | I'd rather be debugging an OCaml program than a Go program | for sure. | danieldk wrote: | Right, but I think (a combination of) certain | abstractions invite abstractionitis. OCaml and Erlang | avoid that to some extend by being more restrained about | what they add to the type system. On the other hand, | these languages allow side-effects, removing the need to | rely on monads, monad transformers, monad transformer | stacks, etc. | | I agree that algebraic data types and pattern matching | lead to better code. But even though they were introduced | (?) by ML, there is nothing holding imperative languages | from adopting them (see e.g. Rust). | EnKopVand wrote: | I mostly write Typescript these days, and being lazy, I'll | just quote wikipedia, but I'd much rather debug: | const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | .filter(n => n % 2 === 0) .map(a => a * 10) | .reduce((a, b) => a + b); | | than: const numList = [1, 2, 3, 4, 5, 6, 7, | 8, 9, 10]; let result = 0; for (let i = 0; i < | numList.length; i++) { if (numList[i] % 2 === 0) { | result += numList[i] * 10; } } | | Maybe it doesn't make so much sense in this simple example, | probably even less so if you're not familiar with | Javascript, but it mostly comes down to the state of what | you're working on. In FP you know what you get, how it | looks while you're working with it and exactly what to | expect as the outcome, in OOP, well, you sort of don't. | Reading Wikipedia, though, maybe what I like is called | Functional Programming with Higher-order functions and not | just Functional Programming? | | Like I said. I'm not extremely religious about it, and I do | think a lot of OOP design principles and code practices are | slowly heading toward a more FP way of thinking. In that | way I think it's sort of interesting that you mention Go, | because with Go you seem to mostly work with immutable | states and functions, rather than mutable objects, which is | more functional than imperative programming, but maybe I | just haven't worked enough with Go to know better. If you | ask me, everything should frankly be immutable by default, | but retrain the ability to become mutable like they do it | in Rust with the "mut" keyword. I really, really, enjoyed | working with that for the brief period I did. Anyway, I'm | not sure I'm ever going to get into religious FP, I may | very rarely use classes, but it's not like an abstract | class can't be healthy for your 17:00 afternoon self once | in a while. | | But basically every best practice in regards to OOP that I | was taught at university back 20+ years ago, the stuff they | still teach today (I'm an external examiner a few times a | year), has proven to be sort of useless in the real world | for me. Maybe it works in more professional or competent | organisations but it sure hasn't worked well in any place | that I've ever worked, and yes, it does feel sort of dirty | to examine people in theories I disagree with, but it's | good money and a good way to keep up with both the CS world | and possible hires. | float4 wrote: | Can confirm. I took Advanced Functional Programming for my | master's. Of the ~15 people who took the class, the majority | scored <=65% or gave up, a few people got pretty good grades | because they were reasonably intelligent and willing to put in | the work (me), and then there were a few people who scored | >=95% because the high levels of abstraction genuinely made | things easier for them. | | That was when I learned that for some people, a monad _really_ | just is a monoid in the category of endofunctors. | gizmo wrote: | When people call themselves "lazy and incompetent" they're not | sincere. It's the equivalent of an obviously fit person saying | "oh I'm so fat". | | It's just fake humility. The same people would get offended if | you _actually_ suggested they were lazy and incompetent. | mejutoco wrote: | In a programming context lazy does not need to be a bad | thing. | | https://thethreevirtues.com/ | | Incompetence, I agree, sounds either self-deprecating or | fake. | mfru wrote: | incompetence is most likely just the impostor syndrome | speaking | [deleted] | rowanG077 wrote: | I don't think this is true. How fat you are can be pretty | objectively measured and it won't change in the short term. | | Being lazy and incompetent however is always fluctuating. | There are days I'm lazy, there are days when I can work 12 | hours and feel energized. There is also days where everything | in my mind aligns and all the problems melt away. There is | also days where I ram my head into the wall on some | relatively simple problem. | | The point being is that programming is something that no | human can do without errors. So you want a language that | provides as much low overhead guard rails as possible to stop | yourself from shooting yourself in the foot. Even on the days | you're lazy and incompetent. | | That's how I see the statement they make. | hutzlibu wrote: | "The same people would get offended if you actually suggested | they were lazy and incompetent. " | | Maybe, because it is all relative. | | I know my flaws and compared to my (often unrealistically) | high standards, I feel incompetent quite a lot. So I can say | the above and mean it. | | But if I get criticized as incompetent by someone way below | my level, then yes, I might take offense and depending on the | mood, also hit back (or ignore it). | taneq wrote: | I think it's often Socratic laziness. Not fake, but "humans | are dumb and weak, let's make this easy for ourselves." | | For most of my coding work I take it a step further than this | with the simple premise that the code you wrote | professionally _is not for you_. It's for whoever next has to | work on it, no matter their level of expertise. Sure, maybe | _you_ 'just know' that the equality operator comes between | bitwise-OR and logical-OR in precedence, but the code isn't | _for_ you so maybe just use the brackets anyway. | [deleted] | [deleted] | DavidSharff wrote: | While there are areas where my functional convictions have | greatly diminished, my mid career zeal had the tremendous benefit | of illuminating new architecture and data design principles. | | Storing data as discreet changes and relying on pure function | selectors to calculate values is wonderful. | | It's not always a viable approach at scale (at least not for my | ability in certain circumstances) but, when it is, testing is a | breeze and I love being able to debug forward/backward in time | with consistent results guaranteed. | waltbosz wrote: | I really dislike when programming books are overly terse with | their code examples. This is a problem I've struggled with since | I was a child learning to code. | | When I'm learning a new concept, I need to be able to clearly see | the steps. Even if I do understand how to read and write in the | abbreviated coding style, the extra step of mentally decoding it | into it's more verbose form takes mental energy away from the | absorption of new knowledge. | | Clearly, this book is written for an advanced audience, but at | the same time it's trying to teach an unfamiliar concept to that | advanced audience. | | Does anyone else here share my sentiment? | | It makes me think of this software I saw once that would take a | math-problem, and solve it for you, showing you the steps along | the way to the solution. I want something like that for this kind | of code. | aeonik wrote: | I completely agree with you. | | This was the main thing slowing me down when I started to learn | Clojure last year. | | I'm now learning Emacs and and Elisp and I find the | documentation even more difficult to comprehend somehow. | | Two examples: Literate programming with org-babel: almost none | of the documentation that I can find shows "real" examples with | multiple header properties or variables being set for an org- | babel file. I don't know how to mix and match them. The docs | tend to show the most minimal example and I've been spinning my | wheels for days trying to figure out how to integrate into my | workflow. | | Another elisp example: using auth-sources.el for handling | credentials. I'm trying to enhance a few major modes to add | support for storing server credentials, and none of the | examples show how to actually prompt a user for a username and | password to save it for later in the secure key store. I've | checked other projects, and people do it in different ways, and | they all seem needlessly complicated, with core auth logic | embedded directly in unrelated function calls. | | Compare this: | https://www.gnu.org/software/emacs/manual/html_mono/auth.htm... | | To this: https://pypi.org/project/keyring/ | | Edit: I'd like to add, I am loving Emacs and Clojure. I think | it's worth the slog, but there is room for improvement for | making a lot of these systems more approachable to the complete | newcomers. | photochemsyn wrote: | The code examples shown here are highly confusing, and would | benefit greatly from code comments. One of the most nonsensical | trends I've seen in programming is the notion that 'code should | explain itself' and if it's well-written, extensive comments | are unnecessary. | | The best way to improve this post would be to append comments | to each discrete code section, explaining clearly what other | code sections it interacts with, what it's intended to do, and | any other notes needed to clarify the functionality. | EVa5I7bHFq9mnYK wrote: | I always thought the benefit of FP is thread safety, at the cost | of a lot of data copying. | alexmolas wrote: | (Off topic comment) With the font you're using to write code it's | sometimes difficult to read some of the characters. For instance, | I had some problems to read the `getKey` method, since the `K` | char is pretty blurred. | rob74 wrote: | It's supposed to look like an old typewriter with partly broken | types, but it looks like they overdid it on the "K"... plus no | typewriter I know of can write with light color on a dark | background, so that kind of breaks the illusion. | joshcrowder wrote: | I came to write the same thing. Fine for reading text, not | ideal for code | macawfish wrote: | What's great about it for me is helping me stay focused on one | thing at a time. Composing and writing pure functions is easier | for me to reason about as someone with an easily distracted mind. | gizmo wrote: | GOOD: having large chunks of code that runs (mostly) without | side-effects. As projects get bigger global state can trip you | up, so try to keep it in check. That's where the real value of | functional programming is. | | BAD: copying data needlessly and turning trivial stuff into a | functional pipeline that is impossible to debug. You want your | code to read top to bottom like the problem you're trying to | solve, if at all possible. If your call stack looks like map, | fold, reduce you've introduced a ton of complexity, and why | exactly? | | Every programmer should understand functional programming. Higher | order functions can be super useful. Immutable objects are | invaluable for preventing subtle bugs. But if you feel the need | to transform simple linear code into a map of higher order | functions because of some abstract benefits you're probably doing | stuff that's too easy for you and you should get your mental | challenge from solving a more difficult problem instead. | piokoch wrote: | 100% agree, the monstrosity that author proposes is really hard | to swallow. Simple made hard. | | The idea to handle validation (we get HTML instead of expected | JSON) by passing some Nothing object down the flow is horrible, | if we expect JSON and get something unparsable, well, the best | strategy is to fail fast and let know client side that we have | a wrong data. Instead we show off with some fancy code | structure with zero value for the user of the software. | mejutoco wrote: | Agree, especially with having as much part of the code as pure | functions. | | I think where functional programming really shines is in | combination with a good type system, that can enforce those | restrictions. In languages like js I find it not so useful | (even if using map, filter, etc.) because I have no guarantees | that there is a hack somewhere in the function that I did not | see. | | When there are a lot of pure functions the cognitive load is | reduced so much I can use the "extra capacity" to focus on the | task. | gizmo wrote: | In Javascript Object.freeze() and Object.seal() are wonderful | to prevent accidental mutation. | mejutoco wrote: | I did not know that. Another one is const to declare simple | variables. There are some features, I agree. But how do I | declare that a function returns a specific type and have it | enforced? IMO, only by switching to Typescript or similar. | gizmo wrote: | VSCode can enforce return types with jsdoc annotations, | even when using plain javascript. VSCode uses typescript | internally for all type inference, so you get that for | free. Type inference works flawlessly 95% of the time, | even for iterators/generators/inheritance/containers. | chriswarbo wrote: | > simple linear code | | If you're talking about a sequence of statements, I've rarely | come across such a thing. | | Firstly, the mere _existence_ of statements in a language is an | incredible complication: | | - It forks the grammar into two distinct sub-languages | (expressions and statements) | | - Expressions compose with each other, and with statements; but | statements don't compose with expressions. | | - It introduces a notion of (logical) time into the semantics | ('before' a statement is executed, and 'after' a statement is | executed) | | - The times of each statement need to be coordinated. Chaining | one-after-another is easy (usually with ';'), but concurrency | gives an exponential explosion of possible interleavings. | | - The computer often cannot help us spot mistakes, and | compilers have very little ability to optimise statements. | | This is especially silly in Python, where many common tasks | require statements, which often requires rewrites (e.g. we | can't put statements in lambdas; hence we often need separate | function defs (which themselves are statements); pulling those | out may lose access to required lexically-scoped variables, | requiring even more plumbing and wrapping; urgh.) | | Compare this to functional code, where there's only a single | language (expressions) where everything is composable; where | there is no control flow (only data dependencies); there is no | notion of time; mistakes are more obvious (e.g. 'no such | variable' errors at compile time); compilers have lots of scope | to optimise; and code is trivial to parallelise. | bazoom42 wrote: | Worth noting Haskell ended up reinventing the | statement/expression distincion in the do-notation. So | apparently the distinction do have value. | | "Logical time" actually matters when you have side-effects. | Of course we can agree code without side effects is simpler | to reason about. Unfortunately you need side effects to do | anything useful. | chriswarbo wrote: | > "Logical time" actually matters when you have side- | effects | | "Time" doesn't matter; causality/dependency is what | matters. That is modelled quite well by passing around | values. For example: let hostname = | lookup("hostname") username = lookup("username") | password = lookup("password") connection = | connect(hostname, username, password) in | query(connection, "SELECT foo FROM myTable") | | The 'query' call depends on the result of the 'connect' | call, so it must "happen afterwards". The 'connect' call | depends on the result of the three 'lookup' calls, so it | must "happen afterwards". The 'lookup' calls don't depend | on each other, so they're concurrent (note that concurrent | does not necessarily imply parallel). | | This form of data dependency is no different than, say, | arithmetic: to calculate '3 x (2 + 4)', the multiplication | depends on the result of the addition, so it must "happen | afterwards". (The imperative equivalent would be 'push 2; | push 4; +; push 3; x;') | | > Worth noting Haskell ended up reinventing the | statement/expression distincion in the do-notation | | Yes, do-notation "desugars" into plugging return values | into arguments, like above. This gets a little tedious for | things like 'IO', where we pass around a unit/null value to | represent "the real world". | | Still, a nice lesson from Haskell is that there's value in | making things opt-in; e.g. many languages have | Maybe/Option, but it's less useful in languages which allow | 'null' anywhere; many languages have IO/Effect/Task/etc. | but it's less useful in languages which allow I/O anywhere; | etc. | garethrowlands wrote: | The real world type is a bit of an implementation detail | in GHC. As a user, you shouldn't really have to deal with | it. IO could be implemented differently under the hood, | though real world works well in GHC. | dmitriid wrote: | But then the real world rudely intervenes because | connections are flaky, services are down, and you need to | add logging to weird and unexpected parts of your program | because a call on device A is resolved in a weird state | on device B, and the backend reports that everything's | a-okay. | tsimionescu wrote: | Still, you're showing the simple case of code without | (global) side-effects. | | What if instead we said let connection = | connect(hostname, username, password) _ = | query(connection, "INSERT INTO t1 VALUES ('abc')") | data = query(connection, "SELECT * FROM t1") in | print data | | What is the order of the two calls to `query` actually | matters, but how could the language know? | | The order of execution of code is actually important | every time you have such a side effect, and languages | that don't strictly define it make this type of thing | very error prone. | chriswarbo wrote: | > What is the order of the two calls to `query` actually | matters, but how could the language know? | | The language can't "know the order" of these calls, since | _they are not ordered_. No information is passed from one | call to the other, hence neither is in each other 's past | light cone. | | If you want to impose some order, you can introduce a | data-dependency between the calls; e.g. returning _some_ | sort of value from the "INSERT" call, and incorporating | that into the "SELECT" call. Examples include: | | - Some sort of 'response' from the database, e.g. | indicating success/failure | | - GHC's implementation of IO as passing around a unit | value for the "RealWorld" | | - Lamport clocks | | - A hash of the previous state (git, block chains, etc.) | | - The 'connection' value itself (most useful in | conjunction with linear types, or equivalent, to prevent | "stale" connections being re-used) | | - Continuation-passing style (passing around the | continuation/stacktrace) | | > languages that don't strictly define it make this type | of thing very error prone | | On the contrary, attempting to define a total order on | such spatially-separated events is very error prone. | Attempting to impose such Newtonian assumptions on real- | world systems, from CPU cores to geographically | distributed systems, leads to all sorts of | inconsistencies and problems. | | This is another example of opt-ins being better than | defaults. It's more useful and clear to have _no_ | implicit order of calculations imposed by default, so | that everything is automatically concurrent /distributed. | If we want to impose some ordering, we can do so using | the above mechanisms. | | Attempting to go the other way (trying to run serial | programs with concurrent semantics) is awkward and error- | prone. See: multithreading. | | See also | https://en.wikipedia.org/wiki/Relativistic_programming | | Note that you haven't specified the database semantics | either. | | Perhaps the connection points to a 'snapshot' of the | contents, like in Datomic; in which case doing an | "INSERT" will not affect a "SELECT". In this case, a | "SELECT" will only see the results of an "INSERT" if we | query against an updated connection (i.e. if we introduce | a data dependency!). | | Perhaps performing multiple queries against the same | connection causes the database history to branch into | "multiple worlds": each correct on its own, but mutually- | contradictory. That's how distributed systems tend to | work; with various concensus algorithms to try and merge | these different histories into some eventually-consistent | whole. | | PS: There is a well-defined answer in this example; since | the "INSERT" query is dead code, it should never be | evaluated ;) | | PPS: Even in the "normal" case of executing these queries | like statements, from top-to-bottom, against a "normal" | SQL database, the semantics are under-defined. For | example, if 'query' is asynchronous, the second query may | race against the first (e.g. taking a faster path to a | remote database and getting executed first). This can be | prevented by making 'query' synchronous; however, that's | just another way of saying we need a response from the | database (i.e. a data dependency!) | tsimionescu wrote: | In most programming languages, the order of the two | statements would be well defined, and neither would be | dead code. | | Trying to make all statements implicitly concurrent | unless they have explicit dependencies is a terrible way | to complicate your life. That in some cases you can | optimize the code (or the CPU will do it for you) by | executing it in certain other orders where it is safe is | supposed to remain an invisible optimization. | | It is obvious to everyone that distributed code, eventual | consistency, and other similar non-totally-ordered | examples are much harder to get right than procedural | code. Even simple print-based debugging/logging becomes | excessively complex if you get rid of the local total | ordering. | | Even most network-based computing is done over TCP (or, | more recently, QUIC) exactly because of how enormously | useful having a total order is in practice (even if it's | just an illusion/abstraction). | | > PS: There is a well-defined answer in this example; | since the "INSERT" query is dead code, it should never be | evaluated ;) | | It's only dead code if you assume Haskell's bizarre lazy | execution model. In virtually every other language, | unless the compiler can prove that query() has no side | effects, the INSERT will be executed. | t43562 wrote: | It might be easier to explain things to oneself in terms of | time though - we are not necessarily functional in thinking. | e.g. I imagine trying to explain f(g(x)) versus g(f(x)) | without using the word "first" or "then" | chriswarbo wrote: | That's data flow, which is fine: the dependency is | explicit, checked by the compiler, and maintained | regardless of where the code lives. | | For example, if 'foo = g(x)' is defined in one module, and | another module does 'f(foo)', then the data flow is | preserved. If we try to force things to be the wrong way | round, we'll get compiler errors like 'No such variable | foo'. | | Compare that to temporal ordering of statements; if one | module executes 'g(x)' and another executes 'f()', how do | we ensure the latter occurs after the former? How could the | compiler tell us if they're the wrong way around? Very | difficult. | naasking wrote: | > e.g. I imagine trying to explain f(g(x)) versus g(f(x)) | without using the word "first" or "then" | | Use "from" and "to", which talks about data dependencies | rather than temporal dependencies. Most people assume | dependency order implies temporal order, and then you | introduce them to lazy evaluation to decouple even that. | chronial wrote: | > pulling those out may lose access to required lexically- | scoped variables | | The only situation for this that I can think of is inside a | list comprehension / generator expression. You are aware that | you can define functions at any scope in python? | chriswarbo wrote: | > You are aware that you can define functions at any scope | in python? >>> lambda myVar: (def foo(): | return myVar + 1, foo)[-1] File "<stdin>", line 1 | lambda myVar: (def foo(): return myVar + 1, foo)[-1] | ^ SyntaxError: invalid syntax | chronial wrote: | I didn't mention lambdas because this is obviously | transitive def f(myVar): def | foo(): return myVar + 1 return foo | chriswarbo wrote: | Here's some made up code: pruned = map( | # Remove all elems whose foo is less than the number of | elems lambda elems: list(filter( lambda | elem: elem.foo < len(elems), elems )) | ) | | Now let's say we want a running total of all the foos. We | can insert an expression to do this: | total_foos = [0] pruned = map( # Remove all | elems whose foo is less than the number of elems | lambda elems: list(filter( lambda elem: ( | # Add elem.foo to our total_foos accumulator | total_foos.append(total_foos.pop() + elem.foo), | elem.foo < len(elems) )[-1], elems | )) ) total_foos = total_foos.pop() | | This is rather awkward and "non-Pythonic"; ideally we | would use 'total_foos += elem.foo', but that can't exist | in a lambda. Hence: total_foos = 0 | # Have to define this outside the map, since it's a | statement def checkAndAccumulate(elems): | """This checkAndAccumulate function is just a wrapper, | for closing-over the elems variable (since it's | not in-scope outside the map call). Returns the | actual checking+accumulating function.""" def | checker(elem): """The actual function we want | to filter with""" total_foos += elem.foo | return elem.foo < len(elems) return checker | pruned = map( # Remove all elems whose foo is | less than the number of elems lambda elems: | list(filter( checkAndAccumulate(elems), | elems )) ) | chronial wrote: | I can only quote OP on this: | | > But if you feel the need to transform simple linear | code into a map of higher order functions because of some | abstract benefits you're probably doing stuff that's too | easy for you and you should get your mental challenge | from solving a more difficult problem instead. | | This code is only complicated because you insist on | following some abstract ideal. The actual way to solve | this in python is: total_foos = | sum(elem.foo for elem in elems) pruned = [e for e | in elems if e.foo < len(elems)] | | Which is shorten than even your first code sample. If you | directly translate your last example into sensible | python, you get a nice example of some "simple linear | code": total_foos = 0 pruned = [] | for elem in elems: total_foos += elem.foo | if elem.foo < len(elems): | pruned.append(elem) | | The existence of statements in python clearly stands in | the way of achieving ideals of pure functional | programming. But I think aiming for such ideals is the | exact opposite of the point OP was making. | brap wrote: | Some solutions are much easier to express in an imperative way. | | You can still write pure functions and avoid state in | imperative code. Use consts, immutable objects, etc. You get | some of the benefits of functional programming while still | writing readable code. | WastingMyTime89 wrote: | > copying data needlessly and turning trivial stuff into a | functional pipeline that is impossible to debug. You want your | code to read top to bottom like the problem you're trying to | solve, if at all possible. If your call stack looks like map, | fold, reduce you've introduced a ton of complexity, and why | exactly? | | Hard to follow code is mostly a consequence of point-free style | in Haskell which is an heresy to be avoided at all cost. | | ML typically uses a piping operator to compose code instead and | that leads to top to bottom code which is extremely easy to | read. | | There is zero difference then between a map and a loop. Loops | are not read top down anyway nor are function calls. It seems | to me you are just more used to these indirections than other | ones and are therefor blind to them. This leads to an argument | which I consider a straw man personally. | lixtra wrote: | I tried googling for an example[1] and had difficulty finding | a good one. | | Do you have a link to a good one? | | [1] https://elixirschool.com/en/lessons/basics/pipe_operator | dpwm wrote: | Here is an example in ocaml, where |> is the pipeline | operator: | | https://cs3110.github.io/textbook/chapters/hop/pipelining.h | t... | Chris2048 wrote: | > Loops are not read top down anyway nor are function calls | | But their scope can be limited. | christophilus wrote: | I'm reminded of the Kernighan quote / trope: "Everyone knows | that debugging is twice as hard as writing a program in the | first place. So, if you're as clever as you can be when you | write it, how will you ever debug it?" | | This phenomenon seems to happen more frequently with FP or | related tools and languages (RxJs abstraction soup comes to | mind). | wiseowise wrote: | This sounds really cool, except it was written in 1978 and | has nothing to do with modern realities of functional | programming or tools. | | > RxJs abstraction soup comes to mind | | Merely a side effect of being implemented in a language | that wasn't really designed for it. | jimbokun wrote: | In my opinion it's just as true today as in 1978, if not | more so, but functional programming can make code simpler | to reason about and therefor debug. | [deleted] | samsquire wrote: | I would rather inherit someone's stateful JavaScript or Java | than a complicated codebase by a seasoned Clojure, Haskell, | Scala developer. | | I stand a better chance understanding the sequential code than | a complicated recursive (edit: nested) monad. Depending how it | was written. | | I can probably understand Kafka pipeline's or a functional map, | reduce pipeline. | | My parser for my programming language which I'm building is a | recursive Pratt parser but it's written in Java. | | I want to understand functional programming better. I am | interested in software transactional memory and parallelism. I | am aware Erlang and Go use message passing but only Erlang is | functional. | | In theory functional code can be parallelised due to | immutability but you need some kind of communication to | indicate transference of values. Imagine a pipeline where you | need to share a value to a few other threads. I'm aware Haskell | has software transactional memory | epgui wrote: | Is there even such a thing as a "recursive monad"? That | sounds made up. | WastingMyTime89 wrote: | No there is no such thing as a recursive monad. A monad is | a _type_ for which strictly defined operations are | provided. It can't be recursive by definition. | wiseowise wrote: | But hey, any strawman in the storm to make FP look bad. | [deleted] | throw827474737 wrote: | Also all "functional" programs have their state somewhere.. | just usually much stricter isolated and manipulated - so no | difference in statefulness. | | > I would rather inherit someone's stateful JavaScript or | Java than a complicated codebase by a seasoned Clojure, | Haskell, Scala developer. I stand a better chance | understanding the sequential code than a complicated | recursive monad. Depending how it was written. | | True, but you add here the "complicated" attribute | deliberately .. usually it is the other way round. The | functional approach code is simpler to read and reason ( and | usually not with much recursive monoids), while the believed | sequential program has so many "concurrent" paths (even if | there is no real concurrency, but just in the many hidden | ways how the super distributed statefulness is manipulated).. | not? | amelius wrote: | > I would rather inherit someone's stateful JavaScript or | Java than a complicated codebase by a seasoned Clojure, | Haskell, Scala developer. | | Isn't that simply because you're a Java developer (as I | understand from the rest of your post)? | Philip-J-Fry wrote: | Functional programmers are just as likely to make overly | complicated code the same as a developer in a conventional | language. | | I'd rather inherit a complicated Java program than a | complicated Scala program. But I'd take a well written Scala | program over a well written Java program any day of the week. | lmarcos wrote: | Agree. The first version of the example (the one using only | `map`) is way easier to understand and maintain than the | rigmarole the author ended up with (writing their own | functors, pipes, peakerror and what not). It's like the | author didn't find the first version "complex" enough, so | they had to make it more complicated for the sake of | "functional programming". | galaxyLogic wrote: | Part of the problem with "functional pipelines" as in the | article is that you are in essence creating a domain-specific | language to describe the problem your program is modeling and | solving. | | Problem is that is not a programming language supported by | anybody else than you. It is not part of the standard library. | If it was it would probably be of high quality, highly tested- | in-practice code. | | But if you constructed your pipeline-framework (as in the | article) by yourself you now need to understand not only the | solution in terms of your created framework, but also how the | framework itself exactly works. If this is a one-off problem- | solution what looks like simpler code in the end hides its | complexity inside its framework-code. And as you keep | programming your framework is a moving target. You will improve | it from time to time. Understanding it becomes a moving target | too. It is a real problem for maintenance for you or anybody | else who wants to use and adapt your existing code in the | future. | | Think about having the problem and a solution and being able to | describe both in English. But then you say hey if I invent this | new language Klingon and express the problem and solution in | Klingon, then both the problem and solution become simpler. | Cool. But now you must also program the Klingon interpreter for | yourself. And be aware of which version of Klingon your code is | assuming is used. | | This is the tendency and cost of over-engineering. It is fun | and natural to look for the "ideal solution", but it comes with | a cost. | Gordonjcp wrote: | I don't get the point of "immutable objects". Why have an | object at all? Why not just hardcode the values if you want | them not to change? | spc476 wrote: | It's about trusting a function call. Imagine the following C | functions: extern result fubar(struct foo | *); extern result snafu(struct foo const *); | | Just by looking at the function prototype, I know that | calling snafu() won't change the struct foo I pass it. I | don't know what fubar() will do to it. Maybe it won't change, | maybe it will. I'd have to check the implementation of | fubar() to find out. | jimbokun wrote: | The idea is that a given object never changes, but it's | straightforward and efficient to create a new object from the | original one with some of the values changed. | | This is "safer" for any code still operating on the original | object, as it will not be changed unexpectedly. | i_no_can_eat wrote: | immutable means that you set them only once, typically at | runtime. _Not_ that they have the same values always, | everytime you run. | [deleted] | dopidopHN wrote: | Those are often records from a database. There is a way to | change it, it's to make a copy of it. | | I like being able to trust that something is what I think it | is because it cannot be something else. Meaning: if I know | that something can't change, I don't have to check for | eventual accidental change. | Gordonjcp wrote: | But now you've got two versions of it that aren't the same. | Why is that supposed to be good? | naasking wrote: | Because of aliasing. Some other functions/objects/threads | may have a reference to the old version. If they were in | the middle of a computation using that object, they don't | have to add a whole bunch of checks to ensure that the | data they're holding didn't suddenly change from | underneath them. This happens a lot in concurrent | programming, but even in single-threaded programs it | makes reasoning about the current state of the system | easier. | andrekandre wrote: | "immutable" is kind of overloaded term, but what it means in | this context is an object itself doesn't mutate when you want | to change a value (basically value semantics[0] instead of | reference semantics[1]) | | for example (pseudocode) var newUser = | Person(name: "some guy", age: 0) // newUser is | replaced with a new object that is // initialized with | field.name and previous age (0) // the old object is | discarded newUser.name = field.name // | object is passed as copy-on-write, assuming a 1 sec delay | // it will print the objects values at this point in time | // (age: 0) even if altered later async(after:1) { | print(newUser.age) } // prints 0 // age was | changed to 32 a nanosecond later, // but now a new | object again is initialized // instead of mutated | newUser.age = 32 print(newUser.age) // prints 32 | ---------- output: 32 0 | | [0] https://en.wikipedia.org/wiki/Value_semantics | | [1] https://stackoverflow.com/questions/27084007/what-is- | value-a... | TeMPOraL wrote: | The point is that if you have a reference to such object, you | know for sure it'll stay looking the same way at all times, | no matter what happens in the program. E.g. take this | pseudocode: let foo = MakeMeAnObject(); | let bar = MakeSomethingElse(foo); | DoSomethingForSideEffects(foo, JoinThings(bar, baz)); | return foo; | | With immutable objects, you can be certain that neither foo | nor bar were modified by any of this code. So, when e.g. | debugging a problem with that DoSomethingForSideEffects() | call, you don't have to worry about values of foo and bar | having been changed somewhere, by someone, between their | introduction and the point you're debugging. | | Neither of them _can_ be modified by it in the future - e.g. | someone else can 't change MakeSomethingElse() to also modify | its inputs, thereby accidentally breaking your code that's | using this function. | | Another way of looking at it: a lot of problems with | reasoning about code, or parallelism, can be drastically | simplified by assuming the data being passed around is only | copied, and not modified in-place. "Immutable objects" as a | language feature is just making this assumption the default, | and enforcing it at the language/compiler level. | | In terms of use, it isn't that much more inconvenient over | mutable objects. You can always do something like: | foo = DoSomeTransformation(foo); | | It's just that DoSomeTransformation() is not _modifying_ the | object, but instead returning a new instance of the same | objects, with relevant fields having different values. The | obvious drawback here is, for large data structures, there | will be lot of copying involved - but that 's where the | languages with immutable objects are usually "cheating", e.g. | by using sophisticated data structures masquerading as simple | ones, as to only ever copy things that have actually changed | (i.e. "distinct" objects end up sharing a lot of their data | under the hood). | Gordonjcp wrote: | > It's just that DoSomeTransformation() is not modifying | the object, but instead returning a new instance of the | same objects, with relevant fields having different values | | I can't think of anything where I'd want that. Don't you | end up needing infinite amounts of memory? Isn't it | absolutely slow as balls copying all that stuff around? | bongobingo1 wrote: | Not really. You can copy the new parts and reference the | old, garbage collect dead things, etc. At least in | languages where immutability is a core tenet vs stuck on | as an after thought. | | Is it as fast mutable everything? No, _some_ copy must | happen, but is a mutable-standard languages as fast as | raw hand tuned asm? Also (probably) no, but the trade | offs are worth it. It likely matters a lot less than you | think unless you 're writing _actually_ performance | critical tight loop code vs just thinking about | performance. | dmitriid wrote: | If immutability is a part of the language, then the | compiler and the runtime know about it. | | This way: | | - passing an object around can always be by reference, | since no one can change it | | - depending on structures used, for changed bits you | don't need to copy the entire structure, but simply shift | pointers to old data or new data | | - garbage collection can become trivial, and granular, | since you know exactly what's new and old, and shifting | data around becomes just moving pointers around (again, | depends on implementation and optimisations) | | There _are_ downsides if you are not careful, of course: | | - creating a bunch of new data that references old data | _will_ run out of memory, but this doesn 't happen as | often as you would think. | | - sending data between processes/threads _may_ result in | copying that data (depends on implementation) | | However, the upside is quite good: your data never | changes under you. That is a call to func(data) doesn't | sneakily change data. _And_ all data becomes trivially | thread-safe without mutexes or race conditions. | idontpost wrote: | ankurdhama wrote: | It makes code easy to understand as you can easily identify | what is input and what is output and can easily figure out | the flow of data i.e what is being computed based on what. | Without immutable objects a function taking object can mutate | those objects and now they are acting as input and output, | which leads to complexity. | blauditore wrote: | Avoiding side-effects is usually lumped toghether with async- | style interfaces into the term "functional programming". | However, the two are separate properties with separate benefits | and downsides: | | - No side-effects makes reasoning about logic easier, code more | deterministic, and tests more reliable. However, certain | problems become harder to solve (e.g. simple caching or | memoization). | | - Async-style interfaces allow for waiting on slow operations | in a non-blocking fashion, meaning those don't occupy threads, | thus a more economic use of resources. However, async-style | code can be harder to read, write, and debug, though this | varies heavily between languages and frameworks. | kalekold wrote: | I've just left a startup company who's entire backend was | written in a functional style using Typescript by an agency. | The only reason I can fathom why is 'why not, functional | programming is cool right!'. A new dev team was created to take | over and it was a disaster. It was an absolute mess of piped, | mapped, reduced functions everywhere and completely unreadable | and unmaintainable. I remember getting lost in a hierarchy of | about 30+ piped (non db framework) functions to write a JS | object to a database. I didn't stay long. | | Since I quit, the entire new engineering team quit and it looks | like the company is going under. Functional programming is a | big mistake for some real-world code. | atraac wrote: | > Functional programming is a big mistake for some real-world | code. | | Generalizing much? I write C# for a living, Elixir in my free | time, I would take Elixir codebase any day of the week. If | you try to write C# in a strictly functional fashion it's | going to be shitshow as well. Moderation is the key, using | immutable data structures, getting rid of side effects if | possible etc. | | You had a bad experience because you and/or people you worked | with simply tried to fit a square peg into a round hole, you | didn't get it in, threw a tantrum and now you're blaming all | the squares for being bad. | | I on the other hand, after learning functional language, have | trouble looking at most code written by pure 'OOP | developers', most of it is a spaghetti shitshow of hierarchy | classes, dependencies and jumping across 20 different files | of factories and providers because DUHH sOlId and ClEaN. That | doesn't mean that OOP is a 'mistake for real-world code'. | eckza wrote: | Inexperienced developers that don't understand what they're | doing, armed with a language known best for being worst-in- | class at everything except for "running everywhere", is a | recipe for disaster, no matter how you spin it. | | It feels egregious to implicate "the entire surface area of | functional programming" here, when there are other obvious | issues at play. | lolinder wrote: | > I've just left a startup company who's entire backend was | written ... by an agency. ... A new dev team was created to | take over and it was a disaster. | | I honestly think these are the important bits of this story. | The startup outsourced their backend to an agency, _then_ | tried to replace the agency with a brand new internal dev | team. | | There's no way that story ends well, regardless of the | paradigm the agency chose or how skilled they might have been | (probably not very). _Every_ codebase is unmaintainable when | the team that built it is suddenly replaced. | | Peter Naur had a lot to say about this in Programming as | Theory Building [0]: | | > The extended life of a program according to these notions | depends on the taking over by new generations of programmers | of the theory of the program. For a new programmer to come to | possess an existing theory of a program it is insufficient | that he or she has the opportunity to become familiar with | the program text and other documentation. What is required is | that the new programmer has the opportunity to work in close | contact with the programmers who already possess the theory, | so as to be able to become familiar with the place of the | program in the wider context of the relevant real world | situations and so as to acquire the knowledge of how the | program works and how unusual program reactions and program | modifications are handled within the program theory. | | [0] https://gist.github.com/onlurking/fc5c81d18cfce9ff81bc968 | a7f... | mhitza wrote: | > Functional programming is a big mistake for some real-world | code. | | The emphasis on "some" should be stronger in your comment, | otherwise it reads, on a quick pass, as a broad dismissal of | functional programming. | | Functional programming concepts and ideas have been steadily | incorporated in most mainstream languages in the last 10+ | years. However, when people move past the language's | functional programming primitives, it's when the project | enters potentially dangerous territory. Your, and the | article's, example of pipes, for one. | | Personally, I'd like more languages to incorporate ADTs | (algebraic datatypes) for me to be able to "de"layer programs | back to a mostly procedural + functional programming. And | based on the current adoption rate of FP concepts, I'm not | sure we're that far away from having proper ADTs and pattern | matching in the most popular imperative programming languages | of today. | bigDinosaur wrote: | You can make code in any paradigm suck. You can do horrible | unmaintainable things in any language in any paradigm: Java | with twenty layers of abstractions, Python with immense | amounts of spaghetti, C with its hard to control safety. You | can also do awful, abysmal imperative or OOP code in | Typescript. So I just don't really see how you can single out | FP here at all. Your codebase sucked, and whoever was hired | to write it in a FP style just sucked at doing so. Sorry. | WA wrote: | > So I just don't really see how you can single out FP here | at all. | | Not OP but imho, it's because FP is "sold" as the perfect | solution for readability and code maintainability. Just use | FP and nothing can go wrong. That's at least the impression | I get when I read about FP. | | The fact that one can write abysmal OOP code is nothing | new. | wiseowise wrote: | > it's because FP is "sold" as the perfect solution for | readability and code maintainability. Just use FP and | nothing can go wrong. That's at least the impression I | get when I read about FP. | | That's because it is. FP is not immune to incorrect | implementation. Both statements are true. | biorach wrote: | > Just use FP and nothing can go wrong. That's at least | the impression I get when I read about FP. | | To be fair that was the kind of nonsense that was being | talked about OOP in the late 90's and early 2000's. | | There are no silver bullets, and anyone who claims | otherwise is flat wrong. | | However most techniques have their advantages, when used | well - and I'd say FP has more to offer than OOP in this | context. | Sohcahtoa82 wrote: | > Java with twenty layers of abstractions | | Only twenty? That'd be a reduction. | | If your stack trace doesn't have at least 30 calls of | "thingHandler.invoke()", you're not abstract enough. | wiseowise wrote: | Map, reduce and fold are much easier to reason about than | imperative loops, what's your problem exactly? | nonrandomstring wrote: | > (great? (programming(functional))) (allows | (ideas (elegantly(expressed)))) | ludston wrote: | (-> except appropriate use of macros makes it easier to both | write and read) | ouid wrote: | functional programming has the same use to a programmer as | category theory does to a mathematician. | | It is very good at describing the kind of objects we tend to care | about, so it is sensible to consider the converse and ask what | kinds of things is it good at describing. | | how naturally we can express our code is a very strong measure of | how well we understand it. Both in terms of what it can and cant | do. | | Imagine having blocks that stick together like legos, but you | cant see the dot lattice because your vision is too poor. FP is | about seeing the dots. | StreamBright wrote: | Using JS to try to prove or disprove anything is pointless. I | think a much better comparison is C# / F# for the exact same | problem. I think Scott Wlaschin's Railway oriented programming is | a prime example of a great OOP vs functional programming content. | jve wrote: | > I think a much better comparison is C# / F# for the exact | same problem | | As a current reader of fsharpforfunandprofit.com, I'd like to | see modern C# comparison with F#. C# has introduced decent | pattern matching, records, nullables. LINQ was introduced long | time ago - many things that make F# possible. Here is some | comparison, but its 10 years old without the goodies C# offers | today: https://fsharpforfunandprofit.com/posts/designing-for- | correc... | piyush_soni wrote: | Yeah. I'm one of those who are not convinced yet about functional | programming being _so great_. In most cases what people call | functional programming are inefficient shortcuts to lengthier | functions, so I never got all that hype about it :\ | spoiler wrote: | The nice thing about functional programming is that it allows | you to shift some of the "mundane" domain thinking onto the | type system. A classical example of that might be to mix units | in physics code. So, nonsenses like adding up lengths and time | is no longer possible. | | Things also generally compose together more easily than in OO. | There's other benefits too, but these are my main ones. | | So, once you set up this invariant groundwork, you free your | mind from thinking about certain problems, as the compiler | catches them for you. | | This isn't to say these things can't be done in OO (they have | been done), but they're usually not enforced to the same level, | as it's normally a library, not native functionality | tsimionescu wrote: | The benefits of type systems are either entirely orthogonal | or at best are themselves a prerequisite for the benefits of | FP. Your example of measurement units in particular is | applicable to the most imperative goto laden code we could | think of. | | In fact, some of the more popular functional languages are | untyped - those being Clojure, Erlang and Scheme. | piyush_soni wrote: | Exactly my point, I'm sure there are benefits, but this | example is more suited to Typed languages whereas many | functional languages like you say are untyped. And man, | I've worked with MIT Scheme in a major commercial software | for 5+ years. What a nightmare it was. :(. I mostly | understood the functional aspect of the language, but the | sea of misplaced brackets and a complete lack of any | debugger (at least for our version of it) whatsoever made | it one of the worst languages to work with. _The biggest | irony was, since Scheme is apparently so extensible, they | had created a home-grown "Object Oriented Scheme"_, which | implemented only _some_ aspects of OOP and omitted some | other very important ones, and it was upto the developer to | find out the hard way which ones were omitted! :D | booleandilemma wrote: | The other day I watched a self-described functional programmer | implement a while loop using this custom-written conditionally | recursive function. | | There was nothing more elegant about it and it wasn't obvious | from just looking at the function call what the function was | doing. It wasn't any simpler than while(condition){}. | | All because he didn't like while loops (and statements in | general). It seemed tortuous and unnatural. | | FP has some good parts but so does imperative programming, and I | think throwing away the good parts of imperative programming and | OOP for the sake of "pure" FP is a serious mistake. | larsonnn wrote: | JavaScript is a bad example. We rely on so many libraries which | are not functional. React one of the biggest is not functional at | all. | | Also JavaScript biggest advantage is that you can mix up. And | it's biggest disadvantage is that non experience developers will | mix up too. | | Also you need to cloning multi level objects. | | To work 100% FP in JavaScript you need the libraries and you need | to have a flat data structure every where. | | I don't know how many JSON APIs you see who work like that. | | JavaScript has also a rich set of language features you can use | to save your time and still have quality code. | weatherlight wrote: | I'm a huge fan of writing code in an FP style in a language | where that's the convention. | | Erlang, Elixir, OCaml, Clojure, (Hell, even Ruby.) | | The problem with the current state of JS and Its popular | libraries, is that the convention is to write OOP and | Imperative code. Unless your entire team is onboard, its causes | a lot of issues. | | I feel like js-transpiled languages like Purescript, Rescript, | ClojureScript, ReasonML, etc do a really good job of allowing | Devs to write functional code on the frontend, while leveraging | the JS-lib ecosystem. | | I personally have a hard time reasoning about while and for | loops, etc, and mutable variables, my monkey brain can't keep | track of that many spinning plates. | kriro wrote: | I basically write code in any paradigm but (loosely defined) FP | feels very natural to me. Immutable data and thinking about how | functions get some input and provide some output is the most | natural way of thinking about a domain for me. | | I have no good way of describing what I mean but I tend to think | about functionality not things/items first I guess. | petilon wrote: | Worth noting: the author of this articles does not believe React | is functional: | https://twitter.com/jrsinclair/status/1398780972506619907 | horsawlarway wrote: | To be fair - I mostly agree with that take. React with hooks is | _not_ functional. I think it 's fair to say that React itself | acknowledges this by making a distinction between a | PureComponent and a Component. | | React itself certainly can be functional, but... (hot take) | functional programming without the part where you actually make | state changes is - drumroll - useless. | | If it ain't changing state and there are no side effects - no | one gives a fuck. Because it literally isn't doing anything. | | Turns out computers are fundamentally state machines. | Functional programming certainly has some nifty ideas, and it | can be a helpful way to reason about code that will lead to | state changes - but the part people want is the state change. | superasn wrote: | Just watched a video(1) about functional programming and this is | the gist of it (for those like me who don't know). | | Three main things of functional programming: | | 1. Keep your data and function independent | | 2. avoid changing state of variables, instead declare others | | 3. accept function/reference as a param | | (1) https://m.youtube.com/watch?v=dAPL7MQGjyM&t=3s | Jorengarenar wrote: | Unfortunately, it didn't help to convince me either. | | For me the question still remains: how is any of this better than | good ol' reliable `for` loop? | em500 wrote: | John Carmack on Twitter: "My younger son said "coding is | basically just ifs and for loops." | | Which inspired this great cartoon: | https://twitter.com/nice_byte/status/1466940940229046273 | mrkeen wrote: | How is the for-loop any better than good ol' GOTO? | wruza wrote: | In numbered-lines code you can't see the start of a goto | loop. In labeled-lines code it's not that hard to infer that | it is a jump target, but having a condition/iterator in one | place is an obvious benefit. | | That said, I believe most people who "boo GOTO" never | actually used it to make sense of what could be wrong with it | and how it feels really. | | Anyway, I think that this analogy(?) is confusing and makes | no sense. | koonsolo wrote: | Indeed! I started programming in GW-BASIC when I was 13. IF | and GOTO was all I needed! FOR, FUNCTION, ... why was it | there? | Jorengarenar wrote: | Conceptually, `for` loop is just syntactic sugar - it | simplifies certain action, not makes it more difficult. | pedrovhb wrote: | Conceptually, the `map` function is just syntactic sugar | for `for` loops, and it's also meant to simplify actions. | There are functional equivalents to many small patterns of | code that occur often - for instance lst | = [] for element in range(10): | lst.append(element * 2) | | Is a very common pattern, that can be expressed with less | typing (and less mental overhead, when you become used to | it) by lst = list(map(lambda x: x * 2, | range(10))) | | Similarly, another very common mini-pattern is | total = 0 for el in range(10): total += | 3 * el | | Which can be more swiftly expressed by | total = reduce(lambda acc, el: acc + 3 * el, range(10)) | | These examples are trivial, but once you start using these | higher level syntactic sugar-like constructs, you often | find that code tends to fit together more nicely and in | ways that make more sense, even if only because you used a | filter operation before the map instead of writing another | level of nested `if` statements inside a loop body. Code | gets easier to follow, not unlike how it's easier to reason | about the behavior of a `for` loop than it is to keep a | bunch of `goto`s in your mental model of what the code does | while at the same time thinking about whether the business | logic part of it makes sense. | wruza wrote: | Tbh, I get your imperative examples instantly but my mind | struggles to autograsp these multilevel functional parts | like list(map(lambda. Too much noise, I have to think | through it to get it. | | I'd prefer a loop with ifs and continues/breaks. | | It may be a lack of experience or habit, but then I _can_ | write code like that (and worse), and it doesn't make any | sense to me. Don't get me wrong, not that I refuse to use | maps or filters or folds, but not that I want to make a | whole program of them either. They have their place where | factoring-out a block of code feels stupid, but if I had | something like: [1, 2, for (x of xs) { | if (cond) {emit x} }] | | I'd never bother with these function-al things. It's not | FP that is rich, it's a traditional imperative syntax | that sucks. Paradigm shift is not equal to usage out of | [in]convenience. | mrkeen wrote: | [1, 2, for (x of xs) { if (cond) {emit x} | }] | | What are 1 and 2 doing? What is emit? Where does x _go_ ? | | Is the above code just: filter cond xs | ? | wruza wrote: | It results into [1, 2, ...(some xs satisfying cond)]. | Emit does just that - emit another value into a context | where for-expr was used. It emits some of x-es into that | array literal. | | _filter cond xs_ | | More like 1:2:filter cond xs. | atraac wrote: | > Tbh, I get your imperative examples instantly but my | mind struggles to autograsp these multilevel functional | parts like list(map(lambda. Too much noise, I have to | think through it to get it. | | That's exactly why most FP languages have pipe operator, | so it's incredibly easy to read. lst = | range(10) | map(fn x -> x * 2 end) | | map(etc...) | | or total = range(10) | | reduce(0, fn elem, acc -> acc + (elem * 3) end) | wruza wrote: | That doesn't change much for my read ability. It even | reads more imperatively, like: I take a range of 0 to 10, | map it over x * 2, map it over... What do I get? A | mapping? Maybe? | | Meanwhile, a for loop is straightforward, you go from 0 | to 10 and append a double or add a triple of x to an | accumulator. I appended/added them, that's what I get. | It's like my brain somehow follows {} scopes and | understands their local envs with no effort. | | If this syntactic style works for you without this | friction, nice. But it doesn't work for everyone and I | suspect that this FP/PP is biased by this effect at both | sides. | atraac wrote: | > What do I get? A mapping? Maybe? | | A mapped list/enumerable it was originally given. You | don't think what you get when you add two numbers, don't | you? Language just works certain way. Not understanding a | simple building block of a language isn't a valid | argument against it. All you essentially say is that you | got so used to OOP concepts that anything else is hard to | read. And it's ok, it's the same way for everyone... But | it's not a valid argument to say that "fUnCtIoNAL bAd". | The whole thing here boils down to what you already said | - lack of experience. | | My honest advice is - try to learn one functional | language, like honestly learn and understand it, try | writing something with it. It really does expand | horizons. | jve wrote: | > I can write code like that (and worse), and it doesn't | make any sense to me | | I'm learning FP and I see value with writing code with | map, reduce etc as those are expressions instead of | statements. Expressions guarantee you have a result of | particular type (with possible side effects if you | prefer), but statements DO side effects. The loop may or | may not insert some value into resulting list because | some branching happened or you forgot some else | condition, with map you guarantee you get same number of | objects of particular type back. | | Plus that enables composition (like in C# LINQ) - by not | extending some loop with different kind of | responsibilities but just passing result to next function | that modifies/filters the result. | | https://fsharpforfunandprofit.com/posts/expressions-vs- | state... | im3w1l wrote: | For what its worth, the pythonic solution is | lst = [x * 2 for x in range(10)] | Gordonjcp wrote: | Why is anything better than GOTO? | | You use GOTO all the time, even if it's wrapped in an | abstraction that makes its intentions less clear. | _rm wrote: | Its greatest strength I see is its simplicity. OOP gives a lot of | latitude to overcomplicate solutions, and I've found it tends to | obscure more obvious solutions to certain problems, e.g. reaching | for inheritance to modify behaviours instead doing it by passing | functions as arguments, especially among less experienced team | members. | | The biggest problem in software development is the slow down of | value output over time caused by accumulating complexity. | Anything that can help keep complexity down is a big win for long | term value output. | kalekold wrote: | If OOP is over-complicating your code, you're using it wrong. | OOP is a tool to manage complexity. | | Functional programming, in my experience, created much more | complicated code because it seems like the authors like to be a | clever as possible. | epgui wrote: | Are you confusing "complicated" with "unfamiliar"? | prmph wrote: | Indeed. Although I have mostly stopped using OOP in most of | my code, I sometimes encounter certain classes of problems | that are exceedingly complicated to solve in a purely | functional way, but that are a breeze to solve using OOP, | believe it or not. | | One can mix functional and OOP code well by | compartmentalizing the parts of the code that are in OOP | style, and situating it in an overall functional context. For | example, the methods of a class can certainly be written in a | very functional style, and classes themselves can be | instantiated within functions. | | The mark of a skilled developer is to know which paradigm is | the best to solve a particular problem, just like the choice | of a data structure can greatly affect simplicity of code to | address a problem. | yen223 wrote: | > I sometimes encounter certain classes of problems that | are exceedingly complicated to solve in a purely functional | way, but that are a breeze to solve using OOP, believe it | or not. | | What kinds of problems, if I may ask? | _rm wrote: | Once you zoom out to the bigger picture of dozens of devs | writing the code rather than yourself, its more a matter of | "statistically, if I recommend functional over OOP, how | simple will the code be a year from now". | | But granted it needs to be part of a broader pressure to keep | things simple, otherwise devs will like you say, find a way | to overcomplicate. | pmontra wrote: | I agree. As an example having to create a class for bags of | methods that don't fit naturally anywhere is bad. I also | despise all the manager, factory, service, command things that | emerge more often in OOP projects than in functional ones. I | know that they denote well known patterns (if used in that way) | but it's clutter, boilerplate, noise around the code that does | the job. However functional only is not necessarily better in | my experience. | | Some context. I'm sure I'm biased by a number of factors. One | is that I started programming a long time ago in C and I was | passing around structs with data and function pointers, so | objects. Another one is that I used a number of different | languages, currently Elixir, Python, Ruby, some JavaScript, | often in the same week for different customers: I like easy to | understand code because I this kind of projects with sparse | teams is easier to maintain than layer over layer of heavily | engineered one (so factories, metaprogramming, etc.) Third, I | end up using a minimum common denominator of all those | languages plus inescapable and/or great features: I wish all | languages had Elixir's pattern matching. This approach works | and it reduces the context switch between projects. | | So, when I'm coding in Elixir sometimes I have to create large | data structures and pass them around, piping them to | Module1.fn1, Module2.fn2, etc. It's more or less the same than | composing methods in OOP, just different syntax. Sometimes | those modules become bags of functions too. What's more | difficult to do in OOP is to engineer a class to accept | arbitrary meaningful methods. In Ruby I often code methods that | return a hash (Python dict, JS object) and create a pipeline of | .map.map.map in their caller, like a |> pipeline in Elixir. | | I prefer mixed paradigm approaches, where one can create the | few classes needed to represent the few data types that are | objects (Elixir's and Erlang's GenServer) and code everything | else as functions. I have a couple of Django projects where the | only classes are the mandatory database models and management | commands. Everything else is functions in modules. That would | be much more pleasant to code in Elixir (if it had classes | instead of those noisy and uninformative handle_call, | handle_cast, start_link.) Ruby has modules too but for some | reason customers really like to create classes for everything, | even two method ones like class Command | def initialize(args) ... end def run | ... end end Command.new(args).run | | which are quite funny to look at. That should be | command(args) | Tainnor wrote: | > Ruby has modules too but for some reason customers really | like to create classes for everything, even two method ones | like [...] | | Encapsulation and extraction of complex functionality (yes, | you should not do it for trivial one-liners). | | In Ruby, classes get used a lot for this because, uh, why | not, but you might as well just have a module method - it's | just that the way modules are used in Ruby makes this | _slightly_ awkward because Ruby is after all biased towards | "everything is an object". | | But in, say, Haskell I might write instead: | module Command run :: [String] -> IO () run | = ... -- some other file import | qualified Command ... Command.run args | | (not that "Command.run" looks like super idiomatic code in | Haskell or anything like that, probably you'd structure your | code differently, but it's the general principle) | epgui wrote: | Every FP advocate I know comes from an imperative/OOP | background... None of them are juniors. None of the "FP skeptics" | (or people parroting an obvious variant of uninformative "right | tool for the job") I know come from an FP background. | | People only seem to convert one way and not the other. And is it | really a surprise that the vast majority of people resist change | / doing things differently? | gjulianm wrote: | Without going into the core of whether FP is better or not, if | OOP/imperative is what 99.9% of people start with, you'll | expect to have both FP advocates and skeptics coming from that | background. It's just that FP skeptics have tried it and gone | back. | | In other words, people only convert one way because it's the | only way most people can convert. | epgui wrote: | I understand what you're saying, but my point is that the | near totality of FP criticism comes from people who don't | know what they're talking about. | | ie.: you need to be very familiar with something before you | can reasonably criticize it. | nyanpasu64 wrote: | When I go back to my old map/zip/method-chaining Rust code | from a year or two ago, I regret doing so and wish I had | written code as imperative sequential operations (looping | over numeric ranges and container items, branching, and | matching) like I write and understand today. | mrkeen wrote: | Hear, hear. | | When I program, I like to use: { functions, | types } | | When FP gets bolted onto a mainstream language after the fact, | you get to program with: { classes, mutability, | side-effects, nulls, functions, types } | | Then the FP skeptics think it's too complicated, and just want | to go back to: { classes, mutability, side- | effects, nulls } | epgui wrote: | Why not just use COBOL and Gotos while we're at it? It was | "the right tool for the job" for decades until the OOP | religion took over! /in_jest | mrkeen wrote: | Sure /s. | | But seriously I dislike the notion that FP is somehow a | _successor_ to OOP, like you have to keep piling | abstractions onto OOP until it becomes FP! | | If you do it that way, you end up with map() | implementations like: public final <R> | Stream<R> map(final Function<? super P_OUT, ? extends R> | mapper) { Objects.requireNonNull(mapper); | return new StatelessOp<P_OUT, R>(this, | StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | | StreamOpFlag.NOT_DISTINCT) { Sink<P_OUT> | opWrapSink(int flags, Sink<R> sink) { | return new Sink.ChainedReference<P_OUT, R>(sink) { | public void accept(P_OUT u) { | this.downstream.accept(mapper.apply(u)); | } }; } }; | } | | Ain't nobody got time to read that. Step _back from_ OOP | before putting in the FP and you can get map() looking | like: map _ [] = [] map f | (x:xs) = f x : map f xs | epgui wrote: | Personally I do see FP as a "successor" to OOP, but not | in a strictly constructive or chronological sense. | | Building FP functionality with classes sounds just as fun | as building java classes with assembly. | _old_dude_ wrote: | The problem of the Java code above is that it tries to | serve many masters, one of them being the JIT, i.e. the | code is written that way so the JIT can fully inline the | whole code. | | The first "final", the "StatelessOp", the | "StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT", | the "int flags" are all here because of performance. | teh64 wrote: | Well your second example relies for speed and efficiency | on Haskell being lazy. If you did e.g. a map -> filter -> | sum chain, the performance is not that great without lazy | evaluation guaranteed by Haskell's implementation, which | can complicate code in other scenarios or in the | complexity of the compiler. OCaml is also an FP language, | and the second example would be almost the same text, but | with much worse performance. | | Also, Python is an OOP language, but a simple map | implementation there is not much more verbose: | def map(function, items): if len(items) != 0: | return [function(items[0]), *map(function, items[1:])] | return [] | epgui wrote: | I think the more pythonic way would probably be to use a | list comprehension: def map(f, items): | return [f(i) for i in items] | | These differences are all more about specific languages | than about FP per se, however. | _old_dude_ wrote: | Yes, this rings a bell :) | | But usually the issue is that people talk about OOP vs FP | without specifying the scale, by example the Java stream API | hide the mutations / side effects (not unlike Haskell monads | do), so you can use { record, stream, | functions, types } | | But internally it's still { mutability, | side-effects } | light24bulbs wrote: | How are the types not totally screwed up in this kind of pattern? | I get that this is JS, but since most of us at the level to be | reading this post actually write TS, isn't it true that most of | those transformations actually modify the type of the object, and | therefor each would need to have a slightly different | intermediary type? | | When I actually have this problem, I usually define the input as | an interface and the output as another interface(maybe extending | the input interface). | | Then I write a SINGLE function that calls those other functions, | collects the data into variables, and then builds a new object of | the new data type, and then returns that. | | Maybe I'm missing the point here. | substation13 wrote: | Excuse me sir, do you have a moment to talk about _Functional | Programming_? | | I love FP and I think it makes writing robust software easier. | However, I don't think the benefits can be explained well. The | only way to see it is to give it an honest try - in a true | functional language. This is too much of an upfront commitment | for the unconvinced. | sidlls wrote: | "Instead, the most intelligent coders I knew tended to take it | up; the people most passionate about writing good code." | | Ah, yes, the most intelligent people tended to take it up, | therefore if I take it up I, too, am intelligent. | | That single line in the article encapsulates everything wrong | with FP zealots' approach to their advocacy. | dsnr wrote: | The gazillion of lines of imperative C/C++ code out there that | every OS runs on, are probably written by less than intelligent | people. | goatlover wrote: | Like Linus Torvalds. Lispers tend to make similar claims. So | does Alan Kay about Smalltalk. Yet most of the world runs on | top of C/C++, or COBOL (or Java) for financial transactions | and Fortran for heavy duty numerical calculations. Not that | FP, Lisp and ST don't have their advantages. They clearly do. | But their supremacy is overstated when it comes to code that | runs the world. | senand wrote: | That doesn't mean the imperative languages are "better", | though, just more popular. I recommend the following video | titled "Why isn't Functional Programming the Norm?": | https://www.youtube.com/watch?v=QyJZzq0v7Z4 | npc12345 wrote: | gpanders wrote: | Ah yes, OS development, a domain famous for attracting stupid | people. | bordercases wrote: | didibus wrote: | Do you mean that talent is required to leverage FP effectively? | | Kind of like how a faster car in the hands of a less talented | driver won't make them faster, but might just cause them to | crash? Whereas a talented driver would pick a faster car and | actually benefit from it? | | Personally I think it's not so much about intelligence or | talent, but passion. In think the developers who demonstrates | that they've learned and tried more development techniques and | styles, that shows a passion and interest towards software | development, engineering and comp-sci. That passion is often | key to success and long term performance. | | There's probably a cutoff between talent and passion, where | passion alone probably has some limits, but also where talent | alone has limits as well. If you find someone with both though, | that's a pretty good sign. | | The question is, are talented developers drawn to FP, or is it | passionate developers that are? Or those that have both? | | And the other question is, can FP make someone more talented? | Can it make them more passionate? | galaxyLogic wrote: | People often confuse intelligence with complexity. | | Yes you have to be intelligent to understand complex problems | and complex solutions. But just because you are intelligent | enough to understand a complicated solution, does not mean the | solution is "intelligent". | | True intelligence is about coming up with solutions where | complexity is minimized. | | Even though an intelligent person can understand a complicated | solution the complexity of a solution strains even the most | intelligent programmer. It takes time to ensure yourself that | the solution is correct. Whereas with a simple solution that is | easy. It takes intelligence to find a solution that is simple | and thus easy. | kaba0 wrote: | That's a great take, but I have to mention that I disagree | with a statement I may have only read into the thread: FP is | not more complex just for the sake of it. | yCombLinks wrote: | I think FP's niche popularity is primarily because of it's | complexity. The complexity of it makes certain types of | people feel smart, so they love it. | NaturalPhallacy wrote: | Completely agree. My favorite example of this is Douglas | Adams' description of flight (or orbital mechanics): "Throw | yourself at the ground and miss." | jasmer wrote: | It's a bit of a canard. | | The benefits of FT are mostly things like statelessness, no side | effects. | | And it's almost better to talk about it in those terms, and | introduce concepts into imperative programming. | | When people say 'functional core (aka libs) and imperative for | the rest' it's ultimately what they mean. | | Applying FP universally creates weird code and it becomes | pedantic and ideological. | | I wouldn't even use the example in the article almost nothing is | gained. | | Try to reduce state, use immutable objects as much as possible / | where reasonable, make reusable function which naturally have | fewer side effects etc.. | | I think that the material benefit of FP is in partial application | of some elements of it, and unfortunately that doesn't create | nice academic threads for people to talk about. It's more like | applied know-how, than theory. | canadianfella wrote: | What do you mean by "canard"? | zabzonk wrote: | false report | rvalue wrote: | i will say one thing about functional programming. it does feel | amazing to write all those stateless functions and using all | kinds of operators. | | here is where one gets into trouble, reading functional code. | while it may look better when you write it, try reading your own | code after a few weeks. you will appreciate it much more when | your control flow in a significantly large enterprise application | is through bunch of ifs and imperative loops than scattered | across various functions and operators. | | another problem, debugging. when your inputs cross various | function boundaries, no matter how smart IDEs you have, you end | up messing around adding impurities to your pure functions and | adding watches with conditions all over the place. | | lastly, all that lux code does hide a significant problem. | Performance. I don't know about javascript but all those | temporary objects one keeps creating for the pursuit of | statelessness is maddening. i don't know if there is anything | characteristically different about a language like Haskell which | handles this problem. Does it end up creating new objects every | time for e.g. a map is called like in this example to update all | the attributes? | tome wrote: | That's interesting, because Haskell is the first language I | have found in which I can write code, come back to it six | months later, and still understand it. I never managed to do | that in C, C++, Java, Perl or Python. | mrkeen wrote: | > Does it end up creating new objects every time for e.g. a map | is called like in this example to update all the attributes? | | No. This is optimised away. | | It's yet another reason why it's frustrating to see FP-like | things bolted onto mainstream imperative stuff. You can't | optimise away 'mostly functional' code. Wrapping a Set with an | UnmodifiableSet isn't the same thing as (f x) + (f x) == 2 * (f | x) | gtirloni wrote: | I didn't understand what was the point of the introduction with | so many words like that. | | Author describes functional programming zealots and seems to | imply it's all annoying because of all the abstractions, then | proceeds to do exactly that while calling it "the good parts". | commandlinefan wrote: | The main problem with functional programming is that most | programmers are too incompetent to use it. It's complicated, the | way calculus and music are. While it's sort of a safe assumption | that anybody outside of the actually learning disabled, with | enough concentration, could buckle down and learn it, most people | - so most programmers - won't. Most programmers won't advance | past procedural code - that's why Java programmers love the | singleton pattern and the Spring "framework" so much: it lets | them write and think in simplistic procedural form but pretend | they're using an object-oriented programming language. (And if | you can't even comprehend OO, you _definitely_ can 't comprehend | FP). | 62951413 wrote: | I'm still waiting for a FP equivalent of the original GoF book | to learn it from. | | * https://leanpub.com/sofp is impressive but is in a different | category | | * https://www.manning.com/books/functional-and-reactive- | domain... is the only one I know which was trying to discuss | building real life software | dgunay wrote: | An aside that I've been wondering about for a while with FP. I | tend to use a lot of tools that do things like cyclomatic | complexity analysis, test coverage metrics, etc. | | It seems like one goal of this style of FP is to abstract away | control flow itself - why bother with if-statements when you can | compose automatically short-circuiting monadic APIs like Option | and Result? An admirable goal that I am 100% in favor of. However | I feel like it might also obfuscate untested branches in source | code and artificially minimize computed cyclomatic complexity if | the tools are not aware of these control flow abstractions. | | I guess it's not super important in the grand scheme of things | (I'm well aware of cyclomatic complexity's dubious usefulness as | a metric). | BerislavLopac wrote: | When it comes to programming, pretty much everything boils down | to two key elements: - Some kind of | data/information, in some (digital) form of one structure or | another. - Methods, processes and operations that | transform, transport and do other things to that data. | | For example, in OOP, those two elements are generally combined | together in organisational units ("objects"); on the other hand, | in functional programming they are strictly separated. | | Each of those paradigms - and many others - are just tools in our | arsenal to achieve our goals, and neither is intrinsically better | than the other. Depending on the context and environment that we | are operating within, each has its advantages and disadvantages; | some are better suited than others _in certain cases_. | thaway34453 wrote: | You can marry FP with object oriented notation, see UFCS or | Scala, or lenses. | BerislavLopac wrote: | Of course you can, and that just strengthens my point. :) | quickthrower2 wrote: | > What we care about is "can we deliver better code, faster" | | Strawman! Most coders, even staunchly "imperative" ones care a | lot about tech debt, tidyness and code quality. That is why | things like SOLID and design patterns are popular. For better or | worse good programmers are proud of their code, and I don't meet | too many who feel they need to "ship faster". Product people | might want that though. | kyberias wrote: | Please fix the font used for code examples. Unreadable. | ergonaught wrote: | I have grown impatient with writing which purports to educate or | inform people who do not understand a topic yet is completely | incomprehensible unless one understands the topic. | | Practice some "Feynman technique" on your subject, or resign | yourself to producing a suggested reading list of works that | actually explain the topic, or please for the love of cognition | stop masking signal with your noise. | roenxi wrote: | If you start with a program, see it as a block of logic that can | be separated into parts with cuts in many different places. | Choose the cuts so as to leave it a composition of functional | programming abstractions. Then it is easy to reason about and it | is possible to teach others to code in the same way. This is a | benefit for functional programming - a promise of consistency in | thought and style that is easy to debug. | | The argument I read in this article seems to be going in that | direction, but I'm not sure it is being presented clearly. The | semi-monoid functors are a fair way down in the weeds and not | really what people should focus on when deciding why functional | programming is great. Unless they really like abstractions in | which case more power to them. | inawarminister wrote: | I'm having a good time doing Data-Oriented Programming (which is | applying Clojure-style programming to other languages, in my case | Python). | | IMO, chaining functions together rather than mutating objects and | variables seem to be the most Pareto efficent takeaway from FP | (at least in the Scheme/Clojure style, haven't tried F#/OcaML or | Haskell). And HOP, though Python has native support for map and | reduce and filter in the last few years, so great enough. | | And to be honest when I try to do something complicated in | Clojure I end up blowing the stack anyway. Recursion is a bit too | hard to grasp for non-programmers like me. | fleddr wrote: | I'm sure this is a wonderful intellectual exercise in computer | science and math, but if this is to be the advertisement in favor | of functional programming, I wouldn't consider myself a customer. | | You start out with code that will not win any beauty awards but | it gets the job done. It's easy to understand by programmers of | any seniority, it's simple to debug and reasonably easy to put in | an automatic unit test. | | Next, you add all kinds of vague, poorly named meta utilities | that don't seem to solve any tangible real world problem. You | even mess with built-in JS methods like map. The code is now | harder to understand and more difficult to debug. | | A massive pain is added for some theoretical purity that few even | understand or will benefit from. I'll double down on my rebellion | by stating that the portability of code is overrated. | | Here we're writing code to format notification objects. In our | overengineering mindset, we break this down into many pieces and | for each piece consider reusability outside this context to be of | prime importance. | | Why though? Why not just solve the problem directly without these | extra self-inflicted goals? The idea that this is a best practice | is a disease in our industry. | | Most code that is written prematurely as reusable, will in fact | never be reused outside its original context. And even if it is | reused, that doesn't mean that outcome is so great. | | Say that the sub logic to format a user profile link turned out | to be needed outside this notification context. Our foresight to | have made it reusable in the first place was solid. Now two | completely different functional contexts are reusing this logic. | Next, the marketing department goes: well actually...you need to | add a tracking param specifically for links in the notification | context, and only there. | | Now your "portability" is a problem. There's various ways to deal | with it, and I'm sure we'll pick the most complicated one in the | name of some best practice. | | After 20 years in the industry, you know how I would write the | logic? A single method "formatNotification". It wouldn't weirdly | copy the entire object over and over again, it would directly | manipulate the object, one piece at a time. Error checking is in | the method as well. You can read the entire logic top to bottom | without jumping into 7 files. You can safely make changes to it | and its intuitive to debug. Any junior would get it in about 2 | minutes. | | Clear, explicit code that requires minimum cognitive parsing. | foobiekr wrote: | One of the things that you imply is another issue: inheriting | someone's functional code is brutal. I'd rather inherit | mediocreville php than decipher the "clever" code that someone | who got excited by scalaz polished for weeks. | oldboyFX wrote: | Thank you for writing this. I've come to exactly the same | conclusion after a decade of building and delivering complex | tangible applications which others have to maintain after I'm | gone. | fleddr wrote: | Thanks for the support. | | I used to live all the best practices, but over time learned | that the number one enemy in a codebase is complexity. 90% of | software development is maintenance. People doing maintenance | are lowly productive because the vast majority of time is | spent on trying to understand how things even work, | debugging, and making changes in a way that is safe. | | The reason code is complex are abstractions and dependencies. | Many of which are of a questionable or negative value and | never deliver their imagined benefits. Hence, instead of | "abstract by default" and seeing DRY as some religion, I | abstract when value is real, proven and outweighs the massive | downsides of doing so. | | Imagine that we'd need to write a recipe. Sane and pragmatic | people would write it as a sequential step of instructions. | If a software engineer were to write one, just to follow the | recipe you'd need to have 23 additional papers each | describing a sub step referred to from the main instructions. | Next, we would abstract each substep into "Motion" and | "Activity" generalizations. This way, you can reuse "Motion" | when you walk your dog later that day. Quite obviously, | motions can fail so we extend it with a MotionError, which is | a sub class of Error. MotionError has a Logger interface. | These 3 super abstractions are useful, instead of just | directly saying that you failed a recipe step, we can now | apply failure wherever we want, even onto life itself. Since | recipes are written in a language, we should abstract | Language, itself a composition of sentences, words, | characters and glyphs with a Dictionary interface, which | itself is an array of Name and Value objects, that we might | as well generalize as Thing. | | Anyway, those were recipe steps. Next, let's do ingredients, | also known as immutable multivariant singletons. But not | really, because as we follow the instructions, the | ingredients morph into something else, which we'll solve with | a Morp object, which holds an interface StateChange, | consisting of two State objects, which are Things. A | ThingsIterator takes care of handling multiple things but not | in a way you'd expect, we'd override map() because I really | feel my Thing is special. Thinking of recipes, they can | potentially come from any source so we'll implement Verbal | (an abstraction of Language), Database and the jungle that | comes with it, all neatly wrapped into MoreThings. | | Next we redo all of his by writing unit tests, so that our | software that by now implements most of the universe is | qualitative. It's math. | | Or...we can just write the damn recipe. | naasking wrote: | Your beef appears to be with over-abstraction and not | functional programming. FP generally makes debugging much | easier because the state of the system isn't changing in | unexpected ways underneath you, so you only need to look at | local state. | | A "functional" recipe is just as simple and straightforward | as an "imperative" recipe. | | Over-abstraction on the other hand, can be introduced with | imperative object oriented programming just as easily as | with functional programming. | avgcorrection wrote: | Hahaha, this is delightful :) | tome wrote: | It's funny because I completely agree with you principles | but completely disagree with, what I think is, your | conclusion, that functional programming should be eschewed. | On the contrary, pure FP, specifically in Haskell, is the | only way I've ever found to reliably put your principles | into practice. | zabzonk wrote: | agreed with your first post, much less so than this one, | because it is based on analogy. recipes are really not like | programming. when i try a new recipe, i normally read the | description to see what i am going to get out of it, and if | it appeals, give it a bash, without any accuracy. | | for example, elizabeth david (never a one for accurate | measurements) has a great recipe for beef with olives, | which comes down to onions, garlick, bay leafs (other herbs | to taste) olive oil, lemon peel, beef, black olives, red | wine. then just cook them. any experienced cook will have | no probs with this - the interesting thing is the | combination of beef and olives. all the quantities are | somewhat irrelevant and can be adjusted to taste. | | whereas a computer program must be pin-point accurate in | all details | | analogies == bad. imho | | sorry i went on a bit there | sfvisser wrote: | You're really fighting the wrong fight here. FP is not about | prematurely writing reusable code. | | It's about recognizing patterns in code that exist all over the | place and _reusing those existing patterns_ for the new code | you 're writing. This way you can break your new feature into | say 20% newly written domain specific code and 80% reusing | existing patterns. Patterns that help with composition, | enforcing invariants, simplifying writing test-cases, and more | clearly signaling intent. | | For example, if you provide a monad instance for your | "formatting notification objects" I will probably understand | how to use your library without even reading a single line of | implementation at all. Just by scanning a few type signatures. | | This way you and your team mates have a huge head start in | understanding your new addition to an existing code base. This | is a great win for everyone! | salawat wrote: | ...except that has nothing to do with FP and everything to do | with... actually reading code. | fleddr wrote: | I'm not fighting, only offering an opinion. We simply | disagree at a fundamental level. | | My first and main point is that 80% of software developers, | the silent majority of so-called "bread programmers", do not | grasp these concepts. So even if the concept is great, it | doesn't matter. | | Second, and this is purely based on experience, I do not | subscribe to the idea that your envisioned way of writing a | new business feature is very realistic or useful. It leads to | dependency hell, difficult to understand code, which is | difficult to debug. I don't even subscribe to the idea | (anymore) that if you're asked to implement a business | feature, that it is implied that you should write it as if it | can be torn apart and used for some imagined other feature. | Nobody asked for that, we just made that up. | | One can say that I've become a DRY infidel. I don't expect | you to agree, very few would agree. Yet I've arrived here for | a reason: 20 years of headaches looking at "best practice" | codebases. | spc476 wrote: | You can apply some FP notions to any code base: | | 1. Don't use globals. Zero global variables should be the goal, | that way, you avoid "spooky action at a distance" where some | code here changes a global that changes the behavior of code | over there. A function that avoids global variables is easier | to deal with than one that doesn't. If you feel you need to use | a global, think about why you need one before adding it. Maybe | you can avoid it. | | 2. Parameters to functions are immutable. That way, you won't | have to worry about data changing during a function call. If | the parameter can be mutated, can you signal the intent in the | function name or signature, so the programmer knows that to | expect? Generally, try to avoid changing parameters in a | function. | | 3. Separate I/O from processing. Do input, do your processing, | then output. God, I wish the major component I worked on did | that (concurrent DB requests)---it would make testing the | business logic so much easier as it can be tested independently | from the I/O. | | Those three things can be done in any language, and just by | doing those three things, you can most of the benefit of FP | without the mathematical jargon (Monads? How do they work?). | There's no need for over-engineered code, and you can be as | explicit as you like while keeping the cognitive aspects to a | minimum. | fleddr wrote: | Well done. Your comment shows actual FP best practices and | their immediate, significant advantages. And you did so | succinctly. | wtetzner wrote: | > It wouldn't weirdly copy the entire object over and over | again, it would directly manipulate the object, one piece at a | time. | | I think this is the only part I disagree with. Immutable data | types make it much easier to understand code, because | everything is local. You don't have to worry that some other | part of the code base might have a reference to this object, | and could be manipulating it out from under you. | draw_down wrote: | ozim wrote: | I agree - problem is loads of people think they should write | framework and not a simple CRUD app. | | I remember one meeting where clueless business person told | developers (including me) that we need an * _abstract*_ | solution for a problem. | | It took me quite some hours to explain to other devs that for | this business person abstract solution is not what they think | it is. Business person wanted CRUD app where they could | configure names for database entries where developers wanted to | make some fancy data types to be used with parametric | polymorphism. | jimbokun wrote: | Sounds like Go is your ideal language. | commitpizza wrote: | Interesting article with an interesting writing style, I may pick | up the book even if I suck at reading books! | | I didn't start as a skeptic and really think functional | programming looks beautiful and is very useful in preventing side | effects. However, in most practical situations I've found over | the years functional programming to be harder to understand and | debug when stuff goes wrong. | | In javascript in particular, functional programming is very nice | but at the same time usually way more ineffecient so for many | tasks I tend to go back to normal for loops just because it's | that much faster. | | I had a collegue that wrote a lot of functional code and his code | was extremely hard to follow and understand. Even if it was | beautiful figuring out where the system went was the hard part | and I had to go to the function decleration, understand it, move | on to the next until I found whatever the issue was. When I found | the issue, I couldn't easily fix it because the next function | called was expecting the data structure to behave in a specific | way so for one issue once I had to change basically every | function called before and after which was way more tedious then | if it would have been more sequential. | | I don't know. There is something to say about functional | programming but lately I have kind of tilted back into the more | standard approach. Functional programming is a lot more beautiful | in practically every implementation I've seen but the standard | approach is, in my experience, usually more useful in practical | terms. | zozbot234 wrote: | > I had a collegue that wrote a lot of functional code and his | code was extremely hard to follow and understand. | | That's not a problem with functional code, it's about badly | designed abstractions. You get the same issues in garden- | variety OOP codebases, only to a far greater extent. | valenterry wrote: | There really needs to be a more clear separation between | functional programming (a very unclear and ambiguous term) and | pure functional programming (formerly just functional | programming, short and well defined but with huge | implementations) | mrkeen wrote: | Yes, but pure means different things in different contexts. For | language designers, purity is about the language itself, not | what you're doing in it. E.g. Haskell is still a pure language | even when you're reading and writing files. I don't think it's | as useful a definition than the lay definition of purity. | 752963e64 wrote: | quickthrower2 wrote: | The dirty secret is Typescript is going to become the most | popular language due to it's dirty pragmatism and simultaneously | very elegant features. It is definitely a get shit done language. | Typescript without JS toolchains (for example compiled to WASM | would make it very good). | vasergen wrote: | For me, the second variant with map chaining | const dataForTemplate = notificationData | .map(addReadableDate) .map(sanitizeMessage) | .map(buildLinkToSender) .map(buildLinkToSource) | .map(addIcon); | | is the best one, it has way less mental overhead comparing with | the end result. | | Also a catch with no error handling try { | return Just(JSON.parse(data)); } catch () { return | Nothing(); } | | in most cases is not the best idea, at least I'd put a log about | it | | Nevertheless, waiting for the book from the author | | UPD: edited code formatting | toastal wrote: | It's not performant though generally speaking you keep boxing | and unboxing the type (imagine having a list of 100,000 | notifications and then mapping over it 5 times in a row). The | composition law would give you a just-as-readable option | const dataForTemplate = notificationData.map(pipe( | addReadableDate, sanitizeMessage, | buildLinkToSender, buildLinkToSource, | addIcon )) | | > in most cases is not the best idea, at least I'd put a log | about it | | Well, in FP you don't want to do side effects like write to | console. Instead you want to hold onto the errors (with an | `data Either e a = Left e | Right a` instead of `data Maybe a = | Just a | Nothing`) til you get to the part of your application | where you _do_ do the side effects. try { | return Right(JSON.parse(data)) } catch (err) { | return Left(err) } | thaway34453 wrote: | I do FP in JS, but I don't use Result or Option types or pipes | because they are not part of JS's lingua franca. | | But I expect everyone in JS to be comfortable with | `Array.prototype.map`, `Array.prototype.reduce`, and closures as | these are core tenants of the language. | throwaway2037 wrote: | One take away from functional programming that I have | incorporated into my Java (and C#) code: If/When possible, | _never_ (ever ever!!!) re-assign a local variable. (Rare | exception: Classic C-style for-loop iteration with mutable `i` | integer variable.) Really, really, I am not trolling / not a | joke. How / Why? How? All primative parameter value must be | final, e.g., `final iint level`, and local variables must always | be final. Why? Final vars are significantly easier to "reason | about" when reading code, as compared to non-final (mutable) | vars. If you are using containers, always, always, always use | immutable containers from Google Guava unless you have an | exceptionally good reason. Yes, the memory overhead will be | higher, but big enterprise corps do not care about adding 4/8/16 | extra GB RAM to host your Java / C# app. Yes, I know that any | `Object` in Java is potentially mutable. As a work-around, try to | make all `Object`s immutable where possible. On the surface, the | whole thing / exercise feels like a terrible Computer Science | nitemare. In practice, it becomes trivial to convert code from | single-threaded to multi-threaded because most of your data | classes are already "truly" final. | quickthrower2 wrote: | I rarely reassign local variables too. (Other then the set to | null as a fallback then set again in a branch "pattern"). | | The only excuse is when I am doing something algorithmic and | that would improve the performance. | | By extension I prefer local variables over class state and | static state and other "self managed lifetime" stuff like that. | | And I am a GC blub programmer, I don't use Rust or C. | waynesonfire wrote: | > If/When possible, never (ever ever!!!) re-assign a local | variable. | | Neat rule, but, it won't work. Firstly, as an example, Scala | which allows you to declare a read only val, you still see vars | used. There is no way you're going to get any traction | enforcing this across the developer spectrum. | | The benefit of these concepts are realized when they are the | only option, hence FP and why mixed paradigm languages are | half-assed. Java isn't even a mixed-paradgim. You're wishing. | lmm wrote: | > Scala which allows you to declare a read only val, you | still see vars used. | | You see them very rarely, and usually with a narrow scope. | | I agree that you probably can't enforce an absolute rule of | no mutable variables. But making it the exception rather than | the rule (e.g. require it to be justified in code review) | makes a huge difference. | kaba0 wrote: | Local mutability is perfectly fine, and depending on the | algorithm can be much more readable. | | Use the best tool for the job. | viscanti wrote: | > Neat rule, but, it won't work. | | You'd get the benefit of easier to reason about code | everywhere you used it, even if others within the codebase | don't. Using that argument, we would argue that it's never | worth trying to find a cleaner way to implement something | because maybe some intern some day will do something weird in | a different part of the code base. We don't have to drop down | to the lowest common denominator for code quality and we | benefit every time we simplify things, even if not everyone | does. | senand wrote: | Agreed, I see this as a code smell. It's already hard from a | semantic point-of-view, normally you have then the same name | for two different meanings. | wtetzner wrote: | > If you are using containers, always, always, always use | immutable containers from Google Guava unless you have an | exceptionally good reason. | | I actually prefer pcollections: | https://github.com/hrldcpr/pcollections | | AtomicReference + immutable data types is a really nice way to | program in Java, and is basically the way most Clojure programs | are written. | acchow wrote: | Read the example about "producers" - If I chain a whole bunch | of producing together across many different Pcollections, I'm | essentially forming a graph behind the scenes? Since no | copying is happening | | Surely this would cause some strange performance outcomes | with, say, a gradually built up immutable list. | P_I_Staker wrote: | That's interesting. On the other hand, I've seen code go | through great lengths to not do a double assign. | | I doubt it was easier to reason about at all. The code set an | error var on a number of if / else conditions. They then used a | separate flag, so that a double write wouldn't be necessary. | ie. int flag = 0; | | int err; // notice no initializer | | if(stuff) { flag = 1; err = 1; } else {} | | if(other_stuff) { flag = 1; err = 2; } else {} | | if(flag) { LogError(err); } | | I follow the "avoid double write" convention too, but in some | cases, it can become very difficult to reason whether a local | is uninitialized; this is a pretty terrible class of bug, | because you work with garbage data off of the stack. | | Setting to zero at declaration makes it much clearer that it | will never be uninitialized, but it's essentially meaningless | and arguably a form of dead code. | | Maybe someone will suggest a redesign? More functions, smaller | functions? It seems that there's a strong (though unpopular) | argument that you should use medium sized functions, and some | teams insist on doing this. In some cases, it can be easier to | find the content, verify specifications. | | Edit: On second thought I can see we didn't avoid the double | write, just did it in a different variable. So I don't | understand what the author's point is, lol. | bluefirebrand wrote: | When I have a flag that is bouncing through a bunch of | conditionals only to return at the end, I prefer to change it | to multiple return paths instead of multiple spots to modify | the flag that is being returned. | | If you are doing a lot of work inside those branches it may | be worth a refactor to simplify the branching logic. | rzzzt wrote: | Java 11+ has factory methods for unmodifiable collections: | https://docs.oracle.com/en/java/javase/11/docs/api/java.base... | hinkley wrote: | There's a short list of reasons to factor out a method. The | most popular one is "if you see the need for inline comments | for a block of code, maybe the code shouldn't be inline", but | feeling the need to redefine a variable, especially in the | middle of a function, is often a good indicator of a new scope | starting. Sending variable foo with an increment, a merge, or a | concatenation to another function is fairly readable. Having | foo and foo' inline is quite a bit harder on working memory. | bob1029 wrote: | FP is only part of the equation when working with meaningfully- | complex systems. Having a clear data + relational model is the | other big part. | | I would strongly recommend reviewing the functional-relational | programming model described in this paper - | http://curtclifton.net/papers/MoseleyMarks06a.pdf | | The overall theme goes something like: FP is for handling all | your logic, and RP is for arranging all of your data. Some sort | of relational programming model is the only way to practically | represent a domain with high dimensionality. | | For real-world applications, we asked ourselves: "Which | programming paradigm already exhibits attributes of both FP and | RP?" The most obvious answer we arrived at is SQL. Today, we use | SQL as a FRP language for configuring our product. 100% of the | domain logic is defined as SQL queries. 100% of the domain data | lives in the database. The database is scoped per unit of | work/session, so there are strong guarantees of determinism | throughout. | | Writing domain logic in SQL is paradise. Our tables are | intentionally kept small, so we never have to worry about | performance & indexing. CTEs and such are encouraged to make | queries as human-friendly as possible. Some of our most | incredibly complex procedural code areas were replaced with not | even 500 characters worth of SQL text that a domain expert can | directly understand. | UK-Al05 wrote: | Nearly every system I worked on that had significant business | logic in SQL turned out to be a maintenance nightmare. Used to | be a lot of this in the 90s or early 2000s. Where dB venders | encouraged it for obvious reasons. | | SQL isn't built with sensible decisions. Delete/update forget a | where clause? Whoops. Lots of traps like that. Also it's not | easily testable. Domain concepts don't always easily map. | alexvoda wrote: | Part of the reason is that SQL is simply a bad language (the | JavaScript of databases). | | Part of the reason is also the thinking and philosophy behind | DBMSs as interactive systems. Especially with regards to | code. | | A function doesn't just exist. You create a function and it | lives in the database until you alter it or drop it. | | This creates a different (temporal?) impedance mismatch with | standard code management tools like version control. The | result is most often a maintenance nightmare. | primeblue wrote: | eddsh1994 wrote: | Oh god, no! Please no! | | Okay maybe it works for you, but I've worked on one of these | systems for a financial engine with over 10m SQL LoC. This was | a big product that was ~15 years old and used by dozens of | companies having bespoke feature flags that changed significant | parts of the calculation engine. Everyone except a couple grey | beards who'd joined straight out of university left that place | after a few years because of how insane it was to work on and | we all became way too interested in good architecture design | from that experience. My friends from that time who I still | keep in touch with are almost entirely working on FP-based | environments now. | alexvoda wrote: | When done poorly that is the result. And it is very easy to | do poorly. | | But I think the basic premise of combining functional and | relational is valid. At the very least it avoids the object | relational impedance mismatch. | alexvoda wrote: | A few points: | | - I can understand why this might appear as hell to many. SQL | is unwieldy and doesn't play nice with many tools we use to | make life easier. | | - I agree with the fundamental premise that a combination of | functional and relational principles can be highly effective | for data heavy scenarios | | - i disagree that pure SQL is the solution to achieve this. If | using MS SQL Server, there are great opportunities to leavrage | the CLR and F# (with caveats because F# gets less love than | C#). You can write functional logic once and use it both | outside of the database and inside the database. PostgreSQL has | extensions for other languages. | wibblewobble124 wrote: | SQL is not functional. But otherwise I support the desire for | an FP and relational language. | bob1029 wrote: | > SQL is not functional | | I agree that you are technically correct. It is simply | declarative in nature. I've got a habit of conflating these | terms. Functions aren't first-class citizens in SQL. We also | have some UDFs that are impure (because the real world | unfortunately exists). | | I'd be perfectly happy to rename this a "Declarative- | Relational" programming model if that sits better with | everyone. | oxfordmale wrote: | The first code iteration in this article could have been deployed | to Production before I finished reading the rest of the article. | | Functional programming definitely has its use cases, as side | effects can introduce bugs. Unfortunately real world business | logics often does require side effects. Often you only find out | about side effects later in a project, and then you suddenly need | to start hacking your clean functional approach. | z9znz wrote: | > real world business logics often does require side effects | | Yeah, all business logic requires side effects, else it is | doing nothing :). | | But following some FP practices, you can push the side effects | very far out to ward the edges. Instead of typical OOP, where | objects get passed around and mutated anywhere and everywhere | (which makes identifying where certain things started to go | wrong), with pure, single responsibility functions you can | write very simple but thorough tests which give you vastly more | confidence about the code. And as a huge added benefit, the | lines of test code actually go down because there's so much | less setup and teardown (mocking, stubbing, etc.). | mrkeen wrote: | > side effects can introduce bugs | | > real world business logics often does require side effects | | > you suddenly need to start hacking your clean functional | approach | | These are great arguments as to why you need: | | a) A type system which tracks effects | | b) A language which makes refactoring safe and easy | lexx wrote: | Beautiful website. The transitions between the code blocks are a | very nice touch | steinuil wrote: | I'm a fan of functional programming but I'm pretty sure this post | would do a terrible job of convincing anyone to try FP out. | There's a very bad pattern of replicating very specific language | features and control flow structures just to make them more | similar to point-free Haskell, which is not going to win anybody | over. | | The author begins by replacing a language feature, the . | operator, with a pipe() function. After that they swap out | exceptions for Result, null/undefined for Maybe, Promise with | Task, and the final code ends up becoming an obfuscated mess of | wrapper functions and custom control flow for what you could | write as: fetch(urlForData) | .then(data => data.json()) .then((notifications) => | notifications.map((notification) => ({ | ...notification, readableDate: new | Date(notification.date * 1000).toGMTString(), | message: notification.message.replace(/</g, '<'), | sender: `https://example.com/users/${notification.username}`, | source: `https://example.com/${notification.sourceType}/${notific | ation.sourceId}`, icon: `https://example.com/assets | /icons/${notification.sourceType}-small.svg`, }) | ) .catch((err) => { console.log(err); return fallback | }); | | ...which is just as functional because doesn't involve any | mutation and it doesn't require several pages of wrapper | functions to set up, you can tell what it does at a glance | without having to look up any other pieces of code, it's gonna | run faster, and it uses standard control flow which you can | easily debug it using the tools you use for any other JS code. | | This post has nothing to do with functional programming, this is | a poor monad tutorial. | Kerrick wrote: | Your response describes exactly how I felt using RxJS for the | first time. | azangru wrote: | I just wanted to say, among all the negativity in the | comments, that I love RxJS, and fell in love with it after | Jafar's workshop. | | As Ben Lesh often says, there are sadly a lot of | misconceptions around Rxjs and the Observable type. The | Observable type is a primitive so useful that it keeps | getting reinvented over and over (React's useEffect is a | weird React-only observable; a redux store is an observable); | whereas Rx is a set of convenience functions to help with the | use of the Observable type. If people are happy to use | lodash, I can't understand what makes them so unhappy about | Rx, which is like lodash, but for async collections. | dmitriid wrote: | It took the author of RxJava _months_ to understand the | concept. Screenshot from the book: | https://twitter.com/dmitriid/status/811561007504093184 | | (Jafar is the author of Rx .Net, and even he couldn't explain | it to the future author of RxJava :) ) | dtech wrote: | I've been working with Rx for 10 years or so and I think it's | a terrible model. | | If you _need_ processing of streams of asynchronous dynamic ( | "hot") data it's the least-bad model I know, otherwise there | are much better ways, especially now that many languages have | async-await keywords or at least a Future/Promise type. | halpmeh wrote: | antihero wrote: | I found using rxjs with redux to be a way of decoupling | control flow. Instead of having a function with a bunch of | thunks, you did something and then other bits of code to | could effectively subscribe to the side effects of that and | you built your control flow up that way. It had pros and | cons, was quite powerful but you ended up with a lot of | indirection and not exactly knowing what was going to happen. | Cthulhu_ wrote: | I've only been using RxJs for a short while now (we're moving | to react soon), I don't really get it. I mean I get it, just | not why we're using it just to consume some REST APIs. 80-90% | of that is boilerplate code to please RxJs, and only alll the | way down through two layers of stores / states and a library | another team maintains is there a single call to `fetch()` | that does the actual work. | | I'm pushing to use react-query with the new app, I think | people will get confused when it turns out hooking up the | whole API will take hours. | briznad wrote: | I read halfway through the article before my eyes glazed over | and I could no longer tell whether the post was serious or a | joke. | nailer wrote: | Or post ES2017, without all the .then()s: const | doThing = async (url, fallback) => { try { | const response = await fetch(url) const notifications | = await response.json() return | notifications.map((notification) => ({ | ...notification, readableDate: new | Date(notification.date * SECONDS).toGMTString(), | message: notification.message.replace(/</g, '<'), | sender: `https://example.com/users/${notification.username}`, | source: `https://example.com/${notification.sourceType}/${notif | ication.sourceId}`, icon: `https://example.com/asse | ts/icons/${notification.sourceType}-small.svg`, })) | } catch (error) { console.log(error.message); | return fallback } } | adamwk wrote: | And now we've gone full circle to writing imperative code | nailer wrote: | That's a pure function, asides from the HTTP call (which it | would seem reasonable to mock). Is there a better way to | handle it? | adamwk wrote: | I'm not disparaging it; I think it's great that languages | have new constructs making certain FP patterns obsolete | still_grokking wrote: | Is this code really equivalent? | | Wouldn't it block on the `await` calls, whereas the original | code would instantly pass control back to the caller? | | (Sorry if this question is odd. My JS is a little bit | rusted). | LegionMammal978 wrote: | It's an async arrow function, which means that it returns a | `Promise` when called, much like the original code that | constructs a `Promise` explicitly. This `Promise` resolves | once the function returns. | still_grokking wrote: | Thanks for the prompt answer! | | I've overlooked the `async` on the first line... My | fault. :-| | 0x445442 wrote: | I'm JS illiterate but I think control is returned to the | main thread until the await call returns, at which time the | execution continues. | still_grokking wrote: | JS is single threaded. | | Async / await is just a code transformation that | introduces Promise wrappers. | | The code should be equivalent to the original. (I've just | overlooked the top-level `async`...). | borbulon wrote: | > ends up becoming an obfuscated mess of wrapper functions and | custom control flow | | This right here is the reason I don't like pure functional | programming. Any engineer should be able to pick up your code | and understand it pretty close to immediately. And with pure | FP, even when they understand functional programming, they end | up wasting valuable time tracking down what it is you're trying | to do. | | * I guess I need to edit to say I mean pure FP in a language | not explicitly built for pure FP. The article is about | implementing in JS, this is what I'm addressing. | steinuil wrote: | I have to agree with you as far as pure functional | programming goes. I also don't like the Haskell approach of | of overgeneralizing concepts just because you can; it's the | FP equivalent of stuffing every design pattern you can into | your OO code. I'd argue that not every pure functional | programming language _has_ to be like that but I 'm pretty | sure all the ones that exist are. | valenterry wrote: | Please don't mix the two things. There are a lot of | abstractions in Haskell (monads, monoids, ...) but those | are 100% orthogonal to pure functional programming. You can | do the latter without any of those abstractions even | existing in the language. | | And if you want to see an example for a language that is | not like that, look at Scala and ZIO (www.zio.dev). It is a | library for pure functional programming, but look at the | examples - those abstractions you mention are not there. | The library really aims at making concurrent/async | programming easier by leveraging pure functional | programming but without crazy category theory stuff. | HelloNurse wrote: | Two other things that shouldn't be mixed are a | programming language's features and what programmers are | using them for. As noted in other comments, Haskell is | good at defining all sorts of functions, but the card | castles of excessive and inappropriate algebraic | abstraction are a tradition of rather uniformly extremist | Haskell users. | jose_zap wrote: | I took a look at ZIO | (https://zio.dev/guides/quickstarts/hello-world) and just | the first example explains a monad. | | That first example shows that the for syntax desugars to | nested flatMap. Which is analogous to Haskell's do | notation, which desugars to nested bind. | | The abstractions you said are not there, seem to actually | be there in zio! | valenterry wrote: | > the first example explains a monad | | Strictly speaking that's wrong. Yes, ZIO is a monadic | type. But you really don't need to understand monads to | understand the example. In fact, the word monad does not | even appear at all. Or would you say javascript also | "explains a monad" when someone explains how to | map/flatmap over an array? I doubt it. | | > That first example shows that the for syntax desugars | to nested flatMap. Which is analogous to Haskell's do | notation, which desugars to nested bind. | | > The abstractions you said are not there, seem to | actually be there in zio! | | Again, those concepts are even in javascript. In fact, | they are in every language. The question is if it is | decided to make them explicit and reusable or not. And | ZIO chose to not require any knowledge of them to be able | to use the library - and that's what we are talking about | here no? | | Let's look at the code of flatMap that you are | complaining about: /** * Returns | an effect that models the execution of this effect, | followed by the * passing of its value to the | specified continuation function `k`, followed * by | the effect that it returns. * * {{{ | * val parsed = readFile("foo.txt").flatMap(file => | parseFile(file)) * }}} */ def | flatMap[R1 <: R, E1 >: E, B](k: A => ZIO[R1, E1, | B])(implicit trace: Trace): ZIO[R1, E1, B] = | ZIO.OnSuccess(trace, self, k) | | Where are monads, monoids, functors, ... here? They are | not to be found. Replace "flatMap" with "then" and you | pretty much have javascript's promises. | jose_zap wrote: | What I meant to say is that the abstractions are | definitely there in zio, which was meant to address your | first comment where you said that zio did not have them. | | By reading the docs I could easily recognize monads. If | you replace "for" with "do", they even have the same | syntax! I could also see clear examples of things that | were modeled as monoids and functors. I would claim that | zio took inspiration from Haskell to model the composable | parts. | | Hey, I'm not complaining about any code, and specially | not flatMap. I think it is a great design. It is the bind | operator in the monad class in Haskell, and there is also | syntax sugar to hide it and make it look like imperative | code, just like Haskell. I like that! | | It seems to me that it is fair to say that the | abstractions are useful as you recognize in your previous | comment. Maybe you just have an aversion against the | abstraction names, or the way many Haskell-related | articles go on and explain them... which is fair enough. | | I learned Haskell without having to understand those | concepts. I could just used them by experimenting with | what was possible and seeing examples from other people. | It was not particularly difficult. So, as you also | figured out with zio, understanding the abstractions is | not a requirement for using them. | steinuil wrote: | I did acknowledge that they are orthogonal when I said | that not every pure FP language has to be like that. | Thanks for pointing out ZIO, I didn't know about it. | valenterry wrote: | I know, but you also said "but I'm pretty sure all the | ones that exist" so I wanted to show you a counter- | example. I'm also not surprised by your thought, since | you are right - most of the time it gets mixed, which is | actually sad. | | We need more practical pure functional programming | libraries that focus on tackling problems with concurrent | updates, streaming, state-changes etc. without forcing | you to take a course in category theory beforehand. | valenterry wrote: | > Any engineer should be able to pick up your code and | understand it pretty close to immediately. | | So a webdev should almost immediately pick up a mix of C++ | and assembly? | | No. There's a reason we specialize. That is not a good | argument against functional programming. | borbulon wrote: | You're hitting a strawman here. I shouldn't have to | explicitly say that I meant someone with at least some | experience in the language you happen to be writing in. | valenterry wrote: | Okay, I think your edit made it more clear. I would also | not try to turn a javascript codebase into pure | functional style. On that one I agree with you. :) | Verdex wrote: | I'm not convinced that previous poster was hitting a | strawman. | | Your argument (if I understand correctly) is that given | some language (here javascript) that one ought not | program in such a way that other people who use that | language are incapable of understanding it. This example | of FP involves a bunch of junk that makes javascript | incomprehensible to javascriptors therefore it's bad. | | Now, previous poster replies with "Hey not everyone | understands c++ and assembly ..." and this does sort of | sound like a strawman, however we can extract the core | argument as "there exist things which you haven't seen | before which you don't understand, but that doesn't make | them bad" and that does not sound like a strawman. That | sounds like a legit argument. | | For example, perhaps there exists a library which | performs some domain specific task using FFT, linear | algebra, and distributed computing load balancer | algorithms. All of it in javascript. An arbitrary | javascript developer is probably not going to be able to | understand what's going on. However, that wouldn't be a | legit argument against FFT, linear algebra, or | distributed computing load balancing algorithms. | | Specializations exist. Pure FP in javascript might not be | a great idea, but it possibly confusing someone who | otherwise knows javascript doesn't feel like a real | argument against it to me. | 0x445442 wrote: | But is the article/book actually advocating the idea that | this non-idiomatic paradigm should only be used in very | specific places in a code base where it's objectively and | demonstratably better? Or is the article/book advocating | wide spread usage? I think it's the latter and so I think | the comment was a straw man. | | As an aside I think this a problem with kitchen sink | languages that allow all these different paradigms. Maybe | not so much with JavaScript because its roots are more | functional but with Java where they've continually bolted | on functional capabilities. With a language like | Smalltalk, there is no idiomatic Smalltalk, just | Smalltalk. I imagine something like Clojure is the same | way. With these kitchen sink languages you can end up | with code bases that vary wildly in style, especially if | there's been a lot of turnover in the project. | Verdex wrote: | With respect to your first point: | | Wide spread usage is a relative term. Like, if the a | program just happens to be 99% FFT code, then you'll have | "wide spread usage" of incomprehensible FFT code in the | code base. This is a question of scope of the project. So | I don't think that an argument becomes strawman just | because most projects will probably be bigger than their | specialized component. | | With respect to your second point: | | Hard agree. I think languages should have much more | constrained focus that largely disallows an impedance | mismatch between what you're trying to do and how you go | about doing it. | | [Worth noting is that I largely agree with the points | against this type of FP in javascript. However, I'm | opposed to the line of reasoning that's being used | against it. Something not being readily understood is not | a good argument for rejection. Because anything worth | doing that hasn't already been done is likely to be | incomprehensible until you've spent some time with it.] | jjav wrote: | > > Any engineer should be able to pick up your code and | understand it pretty close to immediately. | | > So a webdev should almost immediately pick up a mix of | C++ and assembly? | | There's a reason assembly has been relegated to very | specific & specialized use cases. I'd almost want to say | the same about C++. | | The best production code is very simple, very | understandable. | jerf wrote: | There's a difference between "pure FP in an imperative- | oriented language" and "pure FP in a pure-FP-oriented | language". In Haskell, this isn't an obfuscated mess of | wrapper functions and custom control flow, it's the most | straightforward and sensible way to do things. Functions have | very thin syntax and don't get in the way, laziness means you | lose all the function wrappers that imperative languages add | just for that, etc. | | Personally I find trying to write Haskell in an imperative | language to make exactly as much sense as trying to write | imperative code in Haskell. Same error, same reasons, same | outcome. | agentultra wrote: | Hot take here: Haskell is also a better imperative | language. | still_grokking wrote: | For context: | | https://stackoverflow.com/questions/6622524/why-is- | haskell-s... | throwaway17_17 wrote: | Bob Harper, from CMU, has a really fun section of his | 2021 OPLSS lectures where he talks about Haskell being | the best version of Modernized Algol, so your hot take | has, at least, some measure of reasoned support for | precedent. However, the syntax for imperative programming | in Haskell is not quite as simple as it could be and so | makes it a tough fit for a 'better' imperative language. | borbulon wrote: | I agree completely, but since the article is about writing | JS in a pure FP manner, I was addressing that and not | anything else. | jerf wrote: | That's fine. I just couldn't tell. I agree with you. | PaulHoule wrote: | There's also a difference between a Scottsman and a "True | Scottsman". | | There is a common story of a programmer who has journeyed | like an itinerant martial artist looking for functional | programming enlightenment but never finds it... Our | industry is way too susceptible to snake oil stories like | "If I wrote all my tests first my programs would never have | any bugs" or "If I wrote my programs in Erlang they | wouldn't have any bugs..." | | There are certain tasks for which functional programming | idioms are highly effective, other ones where you will | twist your brain into knots and end up writing completely | unmaintainable code. | jerf wrote: | This is absolutely not a "true scotsman" argument. I am | talking about very distinct classifications of languages, | with _wildly_ divergent feature sets, runtimes, | libraries, and communities. The differences are real and | objective. | | This is just a special case of the more general | principle, "Don't write X in Y". Don't write BASIC in C. | Don't write C in Python. Don't write FP in imperative. | Don't write imperative in FP. Don't think you've solved | whatever problems you have with Y when you're forced to | use it by writing X in Y. Writing X in Y inevitably leads | to the _worst_ of both worlds, not the best. | PaulHoule wrote: | There is a triangle of promise, hype and results. When | the promise is there and the hype is there but the | results aren't there that's different from promise | without hype. Haskell has a lot of promise and a lot of | hype and could use some "tough love" in terms of | understanding why it hasn't been adopted in industry. | (Hint: most of the time when somebody is writing a | program that whole point is that they want to create a | side effect. A real revolution in software might come out | of that insight.) | | "X in Y" is a common programming motif in advanced | systems. Large systems in C, for instance, tend to | contain some kind of runtime and DSL-ization if not an | embedded interpreter. I think it's an absolute blast to | write DSLs in Java and unlike other languages that get | DSL hype (Scala) you have IDEs that work and cooperate | with this use case. | still_grokking wrote: | FP is the best thing since sliced bread, don't get me wrong! | | But the seemingly arising "Haskell religion" is at least as | mislead as the OOP-religion that held mainstream in | stranglehold for a long time. | | Haskell's syntax isn't anything to imitate. It's hostile to IDE | features, at least. Alone that should make it a no-go. | | The next thing is that Haskell's features may make sense in the | Haskell context, but they don't make any sense at all in almost | any other language. | | When you don't have strong types, no lazy evaluation by | default, and it's not mandatory to use IO-wrappers, it makes | not sense to mimic solutions that arose out necessities that | are again results of constrains of Haskell's feature design. | You need to do some things in Haskell the Haskell way because | Haskell is the way it is. In a different language the chosen | solutions are at best bonkers (even they may make sense for | Haskell). | | As always: It's a terrible idea to start doing something | because "it's cool" and the "new hot shit", without actually | understanding _why exactly_ things are (or should be) done the | way they are. The "Haskell religion" is by now imho already | mostly only cargo cult... The more worrisome part is that it | seems it attracts more and more acolytes. This will end up | badly. It will likely kill the good ideas behind FP and only | leave a hull of religious customs that the cult followers will | insist on; exactly like it happened to OOP in the past. | | That's my personal opinion, speaking as a big FP Scala fan. | tikhonj wrote: | JavaScript et al do have IO wrappers, they just call them | promises and only force _some_ effects through them. | still_grokking wrote: | The key word here was "mandatory usage". | | BTW: Haskell uses its IO wrapper also only for a very | narrow set of effects. | | One of my favorite examples: `head []` will result in a | crash at runtime. IO won't safe you. (Besides that the | whole "effect wrapping" story in Haskell is anyway shallow | at best. There is nothing like "none observable effects" at | all as every computation needs to happen in space-time, | leaving observable traces therein; you can't define away | "side channels" and call the result "pure"; that's mocking | reality). | cies wrote: | > One of my favorite examples: `head []` | | This is realllly unidiomatic in real world Haskell. Even | removed from Elm and PureScript. | | Idris maybe has better effect handling for your taste. | Also see: Koka, Frank (mainly a paper) | still_grokking wrote: | > This is realllly unidiomatic in real world Haskell. | | Whether idiomatic or not does not matter. It proves my | point: | | IO won't save you, and even very mundane effects are not | part of the game... | | Idris is the "better Haskell" sure, but the effect | tracking is still part of the uncanny valley (still IO | monad based). | | Koka is a toy, and Frank mostly "only a paper" (even | there is some code out there). | | The "Frank concept" is to some degree implemented in the | Unison language, though: | | https://www.unison-lang.org/learn/fundamentals/abilities/ | | Having a notion of co-effects (or however you please to | call them) is imho actually much more important than | talking about effects (as effects are in fact neither | values nor types--something that all the IO kludges get | wrong). | | I think the first practicable approach in the mainstream | about this topic will be what gets researched and | developed for Scala. The main take away is that you need | to look at things form the co-effects side first and | foremost! | | In case anybody is interested in what happens in Scala | land in this regard: | | https://www.slideshare.net/slideshow/embed_code/key/aLE9M | 37d... | | https://docs.scala- | lang.org/scala3/reference/experimental/cc... | | But also the development in OCaml seems interesting: | | https://github.com/ocaml-multicore/eio#design-note- | capabilit... | | Look mom, "effects", but without the monad headache! | gottlobflegel wrote: | > This is realllly unidiomatic in real world Haskell. | | Yet it is the actual behaviour in the stdlib Prelude. | 1-more wrote: | Yep. I think it is needed to satisfy some category theory | thing but I never got a good answer on that. I like to | use nri-prelude since I went from Elm to Haskell and it | has the same idioms, to wit `head : List a -> Maybe a`. | misja111 wrote: | > But the seemingly arising "Haskell religion" is at least as | mislead as the OOP-religion that held mainstream in | stranglehold for a long time. | | I wanted to say the same but you were faster than me. The | article reminded me a lot of the design patterns fad in the | OOP world: that compulsion to make everything abstract and | reusable even if there's no use for it yet. But hey, at some | point it might be needed and then it would be great! | | Of course there are some cases where you really want to be | ahead of what might come , e.g. public library api's, but | those are rare. | celeritascelery wrote: | I have heard is said that both OOP and functional design | are best applied only about 80% of the time. More than that | and you are overfitting the method to things where it | applies poorly. | pencilguin wrote: | Every big enough system has a few parts that would fit | OOP, and a few parts that would fit FP. If your language | supports those, then great, use those for them. | | The overwhelming majority of problems do not fit OO, and | using OOP on them makes a mess. Similarly, FP. | efnx wrote: | I think a lot of this (to quote the Dude) "is just like, your | opinion, man". | | Haskell is great but it's not for everybody. It really gets | you thinking about software engineering in a different way. | | I wrote Haskell full time for five years, with some of the | best in the industry and I can tell you that there is no real | religion. I will admit the language attracts ideologists, | theorists and folks who like to pioneer (which I like), but | everybody is just trying to make things work - these are | patterns that solve problems and guidelines that avoid | problems. | | Now, it's really hard to take the good parts of Haskell and | bring them to a language like JavaScript and have it feel | "natural" - especially to someone who isn't a native Haskell | writer! And especially a concept as reliant on higher kinded | types as effects! | chongli wrote: | _Haskell 's syntax isn't anything to imitate. It's hostile to | IDE features, at least._ | | Can you elaborate on this? I haven't used Haskell in years | but I recall enjoying some of its really cool IDE-friendly | features, such as typed holes and the ability to press a key | and have whatever is under my editor's cursor show me its | type and another key to insert that inferred type into the | document. | | I have heard that more recent versions support nice record | syntax using the '.' operator (but without whitespace, to | differentiate it from composition). That's also very IDE | friendly since type inference in the editor should be able to | resolve the members of a data type that's been defined with | record syntax. | still_grokking wrote: | The main problem is discovery. | | When I hold some object I can easily discover the things I | can do with that object--just by typing "." after the | object reference. | | In something like Haskell I need to know _upfront_ what I | may do with some "object". The IDE can't help me discover | the methods I need. All it can do is to show me _all_ | available functions in scope. Most of this functions aren | 't what I'm looking for. But the IDE can't know that as | it's missing the "context" in which I want to call my | functions / methods. | | And regarding records in Haskell: First of all you will get | beaten when you use them (that's part of the religious | movement and has nothing to do with comprehensible | reasons). The other part is: Records are no substitute for | objects. Not even close. Those are just quite primitive | structs. (To learn how unpractical is it to try to build | objects from structs without proper language support just | ask someone who was forced to use C after coming from a | more complete OO language). | TremendousJudge wrote: | to quote grugbrain.dev: | | >grug very like type systems make programming easier. for | grug, type systems most value when grug hit dot on | keyboard and list of things grug can do pop up magic. | this 90% of value of type system or more to grug | | >big brain type system shaman often say type correctness | main point type system, but grug note some big brain type | system shaman not often ship code. grug suppose code | never shipped is correct, in some sense, but not really | what grug mean when say correct | | >grug say tool magic pop up of what can do and complete | of code major most benefit of type system, correctness | also good but not so nearly so much | CharlieDigital wrote: | Haha, I love this. | | I wrote up something similar [0] but not quite as | creative. _Discoverability_ and _Locus of Control_ are | two of the best reasons to use OOP that rarely comes up | in the academic discussion of OOP as a practice. | | Honestly, OOP is many cases is simply more practical. | | Even imperative code has its merits; case in point: it's | often much easier to debug. | | [0] https://medium.com/@chrlschn/weve-been-teaching- | object-orien... | still_grokking wrote: | > https://grugbrain.dev/ | | How could I miss that? | | Gold! Thanks! | skhm wrote: | >but grug must to grug be true, and "no" is magic grug | word. Hard say at first, especially if you nice grug and | don't like disappoint people (many such grugs!) but | easier over time even though shiney rock pile not as high | as might otherwise be | | >is ok: how many shiney rock grug really need anyway? | | ... I actually needed to see this today | zopa wrote: | > In something like Haskell I need to know upfront what I | may do with some "object". The IDE can't help me discover | the methods I need. All it can do is to show me all | available functions in scope. | | Sorry, but this just isn't true. Hoogle | <https://hoogle.haskell.org/> searches function by type, | fuzzily: ask for functions whose first parameter is the | type of the object-like thing, and you'll get just what | you're looking for. And it's perfectly possible to run | hoogle locally and integrate it with your editor. | | Now, the tooling for a language like Java have had | several centuries more of aggregate development work done | on them compared to Haskell's tools, and if that polish | is a difference-maker for you, that's fine! But it's not | a fundamental limitation, and claiming it is is just fud. | lolinder wrote: | The main point is that the noun-first syntax of OO | languages means that by just _starting_ to write the code | that you know you need, you 've already given the IDE | enough information to give you a list of options for | which function to use. That kind of tight integration | directly into the editor is hard with Haskell because you | have to write out the function name first. | | I could imagine an editor having some sort of "holes" | capability, where I can hit a key combo to insert a hole | where a function should go, provide the function | arguments, then double back and fill in the hole with | search results from Hoogle. Done right, such a system | would be marginally harder to use than Java-style IDE | completions, but not enough to be a problem. The main | difficulty is that the implementation and UX complexity | of such a system is _far_ greater than with a noun-first | syntax. | tome wrote: | > That kind of tight integration directly into the editor | is hard with Haskell because you have to write out the | function name first. | | Sort of. But you can just write _ x | ^ | | and get the same sort of benefit as you get from | x. ^ | zopa wrote: | The approach you're describing sounds a lot like proof- | search in idris and agda (coq too, I thing), and it's | nicer than you think: you're very often working with | definition-stubs created by automated case-splitting, | which adds holes for you. Jumping around between holes | becomes how you program, not an irritating interruption. | | But even aside from that, I don't think verb-first needs | to be a showstopper: given that we've got type | signatures, there's a lot of local context to work with. | You're probably calling a function on one or more of the | parameters, or else you're typing the leftmost-piece of a | composition chain that gives you your result type. So | throw Param1Type -> a, b -> ResultType etc at hoogle and | populate a completion list. Completion doesn't have to be | perfect to be useful. The hard part would be performance: | if completion isn't fast, what's the point? | still_grokking wrote: | > And it's perfectly possible to run hoogle locally and | integrate it with your editor. | | Interesting. | | Could you point to some demo of such IDE integration? | Never seen it. | | I'm still struggling to imagine how something like | "IntelliSense" would look like in such a system. Do you | have e.g. any demo video you could point to? Curious to | learn more! | zopa wrote: | If I made it sound like there's something like | IntelliSense today, apologies! We've got | <https://github.com/haskell/haskell- | mode/blob/master/haskell-...>, but it's type-a-command- | and-do-a-search: it's not linked in with completion | directly in the setups I've seen. | | (In practice, I'm usually starting from a slightly | different place: I know I want a Frob and I've got a This | and a That, so I do :hoogle This -> That -> Frob and get | some options. The thought-process is working backwards | from the goal more than forwards from one key object in | focus. A different way of working, but I'm not convinced | it's less effective.) | | My point though was that it's an engineering issue, not a | fundamental language limitation. ie not a reason all | future languages should shun haskell features. The | building blocks to do better at completion than haskell | curently does are there. | lolinder wrote: | > The other part is: Records are no substitute for | objects. Not even close. Those are just quite primitive | structs. (To learn how unpractical is it to try to build | objects from structs without proper language support just | ask someone who was forced to use C after coming from a | more complete OO language). | | I agree with you about C and Haskell's records, but I'm | curious if you'd say the same about Rust's struct + impl | system? Are there problems that can only be solved with | real, Smalltalk-inspired objects, or can lightweight | structs do the job if supported by enough syntactic | sugar? | still_grokking wrote: | The answer lies in the answer to why Rust added `dyn | traits`... | | Indeed Rust striked some kind of sweet spot because it | got the _defaults_ right. | | Syntactic it mimics mostly the OOP approach, but without | the conceptional burdens behind it. | | Still Rust could not do without dynamic dispatch (which | is the--wrong--default in OOP). | | In the (admittedly) seldom cases where you need dynamic | dispatch & _runtime_ polymorphism you just need them. | | You could build this stuff by hand (and people did in the | past in languages without this features built-in) but | having dedicated language level support for that makes it | much more bearable. | | Structs + the impl system is static dispatch. This just | can't replace dynamic dispatch. If it could nobody would | have ever invented v-tables... | pera wrote: | Your first point is also true for procedural programming | languages, but at least in Haskell you know that if | something is, say, a functor then you know what sort of | things you can do with that value. I think it should be | possible for an IDE to suggest functions if you used the | reverse application operator: [1, 2, 3] & | ^-- suggestion here | | Unfortunately this wouldn't be very idiomatic in Haskell, | maybe in Idris with |> this would make more sense? | still_grokking wrote: | > if something is, say, a functor then you know what sort | of things you can do with that value | | Sure. Because you memorized all the functor methods and | remembered that there is a functor instance defined for | the data type at hand... ;-) | | > Unfortunately this wouldn't be very idiomatic in | Haskell, maybe in Idris with |> this would make more | sense? | | Or you could just admit that the "OOP style syntax" is | superior. (I'm not saying that OOP is superior, though) | skavi wrote: | With Haskell, you learn to discover in a different way. | Just think about the shape of the function you'd want, | then type that into Hoogle. I learned Rust before | Haskell, but prefer Hoogle to Rust's searching via dot | chaining. | still_grokking wrote: | > Just think about the shape of the function you'd want, | then type that into Hoogle. | | Do you _really_ think this is a adequate replacement for | proper IDE support? | likeclockwork wrote: | All you work with is method calls? No free functions? No | library functions? | | I perform operations on stuff, if I don't know the name | of the operation I intend to perform I clearly have some | reading to do. Integrated look up for one specific class | of operation which isn't even omnipresent in Class- | oriented languages isn't a killer feature for me. Nor is | structuring every module even if it's completely | stateless as a class, just to benefit from IDE driven | development a reasonable price to pay. | deltasevennine wrote: | This opinion is also biased. We have no theoretical method | for determining which design philosophy is better than the | other. | | We can't know whether the OOP religion is better, we also | can't know if the Haskell religion is better, and we can't | know whether NEITHER is better. (this is key, even the | neutral point of view where both are "good" can't be proven). | | We do have theories to determine algorithmic efficiency. | Computational complexity allows us to quantify which | algorithm is faster and better. But whether that algorithm | was better implemented using FP concepts or OOP concepts, we | don't know... we can't know. | | A lot of people like you just pick a random religion. It may | seem more reasonable and measured to pick the neutral ground. | But this in itself is A Religion. | | It's the "it's all apples and oranges approach" or the "FP | and OOP are just different tools in a toolbox" approach.... | but without any mathematical theory to quantify "better" | there's no way we can really ever know. Rotten apples and | rotten oranges ALSO exist in a world full of apples and | oranges. | | You can't see it but even on an intuitive level this | "opinion" is really really biased. It seems reasonable when | you have two options to choose from "OOP" and "FP", but what | if you have more options? We have Declarative programming, | Lisp style programming, assembly language programming, logic | programming, reg-exp... Are we really to apply this | philosophy to ALL possible styles of programming? Is every | single thing in the universe truly apples and oranges or just | a tool in a toolbox? | | With this many options it's unlikely. Something must be bad, | something must be good and many things are better then other | things. | | I am of the opinion that normal Procedural and imperative | programming with functions is Superior to OOP for the | majority of applications. I am not saying FP is better than | imperative programming, I am saying OOP is a overall a bad | tool even compared with normal programming. But I can't prove | my opinion to be right, and you can't prove it to be wrong. | | Without proof, all we can do is move in circles and argue | endlessly. But, psychologically, people tend to fall for your | argument because it's less extreme, it seemingly takes the | "reasonable" mediator approach. But like I said even this | approach is one form of an extreme and it is not reasonable | at all. | | I mean your evidence is just a bunch of qualitative factoids. | An opponent to your opinion will come at you with another | list of qualitative factoids. You mix all the factoids | together and you have a bigger list of factoids with no | definitive conclusion. | com2kid wrote: | > Computational complexity allows us to quantify which | algorithm is faster and better. But whether that algorithm | was better implemented using FP concepts or OOP concepts, | we don't know... we can't know. | | The CPUs code runs on are imperative, with a lot of | complexities and details hidden from programmers by magic | the CPU does involving things like reordering and automatic | parallelization. | | However, none of the current languages are great at writing | code that maps to how the CPU works. One can comment that | functional programming does a better job of breaking up | data dependencies, but imperative code can also do that | just fine. | | The problem with mapping paradigms to performance is that | none of the paradigm purists _care_ about performance, end | of the day they care about theoretical purity. | | CPUs don't care about paradigms, they care about keeping | execution units busy and cache lined filled up. | rileyphone wrote: | > without any mathematical theory to quantify "better" | there's no way we can really ever know. Rotten apples and | rotten oranges ALSO exist in a world full of apples and | oranges. | | So you believe that the only way things can be compared is | on quantitative measurements? Not with how they impress | their users within whatever context they're in? | | > I mean your evidence is just a bunch of qualitative | factoids. An opponent to your opinion will come at you with | another list of qualitative factoids. You mix all the | factoids together and you have a bigger list of factoids | with no definitive conclusion. | | This is the process in which we gain knowledge in an | uncertain world. I guess you could take the nihilistic | stance and ignore it, but what's the use of arguing with | nihilists? | Supermancho wrote: | > We have no theoretical method for determining which | design philosophy is better than the other. | | We do have a theoretical method. It's the scientific | method. Other than that, I'm largely of the same thinking. | Also, confusing language implementation with overall design | is a major source of confusion (eg Java vs OOP vs Erlang vs | FP vs Haskell, etc) | | How to measure "better" and how the data is interpreted, | are the major stopping points to improving software | language usability. There have been some attempts (re: | Quorum). Classic OOP (inheritance, et al) is simpler to use | than mixins for many projects. So now we have to worry | about project size as another axis. Then we have to address | the issue of median developer effort. What about memory? | How do you weigh FP allocate-stack-as-a-for-loop vs reduced | mutability? It's more complex than FP good OOP bad. | deltasevennine wrote: | >We do have a theoretical method. It's the scientific | method. | | Science is limited. That being said this isn't part of | science at all. Programming is more the domain of logic | and maths which is entirely different from science which | is more data driven. | | >How to measure "better" and how the data is interpreted, | are the major stopping points to improving software | language usability. There have been some attempts (re: | Quorum). Classic OOP (inheritance, et al) is simpler to | use than mixins for many projects. So now we have to | worry about project size as another axis. Then we have to | address the issue of median developer effort. What about | memory? How do you weigh FP allocate-stack-as-a-for-loop | vs reduced mutability? It's more complex than FP good OOP | bad. | | I mean this is your opinion. I don't see any logic or | science here. Just another factoid. | Supermancho wrote: | > That being said this isn't part of science at all. | | Human behavior (and usability at-large) is subject to | scientific investigation and conclusions. Again, the | issue is data categorization and availability to conduct | studies. | | > I mean this is your opinion. | | I don't know what you're referring to, but it seems | spurious, based on available data. | | [edit after following some other thread...you also say] > | But of course most members of either group have Not | actually went to space to verify the conclusions for | themselves. | | "I don't know because I didn't experience it", trolling. | How quaint. Good luck with your navel gazing. | still_grokking wrote: | Nobody argued for any "better". | | The point is: When things become a religion with cargo | culting acolytes even the "best" approaches stop making | sense. | | That's completely independent of the concrete religion at | hand. | | I did not argue to "pick sides"! | | In the end all the approaches are just tools. You need to | wield them wisely for optimal results. | deltasevennine wrote: | This is the problem. You didn't even read my argument. Go | read it again, carefully, instead of skimming through it. | | My point is: | | Maybe one of these religions is right. Maybe something is | the best. Maybe a side must be picked. | | You didn't argue for better. You argued that everything | is the same, that all things are good and nothing is bad | and that every single thing in the programming universe | is a tool in a toolbox. | | I disagree. Violently. | | The point is neither the culting acolytes OR people like | you can prove it either way. | | But calling people who don't share your opinion as | "culting acolytes" is manipulative. The words have | negative connotations and it's wrong. Extreme opinions in | science and logic are often proven to be true, they are | often validated. To assume that anyone without a neutral | opinion is a cultist is very biased in itself. | | Here's a good analogy: I believe the world is round. I'm | an extremist. You on the other hand embrace all theories | as tools in a toolbox. The world could be round or it | could be flat, your the reasonable neutral arbiter taking | neither the side of the flat-earther or round-earther. | | The illusion now is more clear. All 3 sides are a form of | bias, but clearly our science says only one of these | sides is true, and this side is NOT the "neutral arbiter" | side | still_grokking wrote: | I did not argue for "everything is the same". | | Quite the contrary. | | I've said: _Everything depends on context._ | | What makes sense for Haskell does not necessary make | sense for other languages. | | Also there is no "side" that needs to be picked. What's a | good idea in one context could be a terrible idea in some | other context. | | But people are copying blindly Haskell lately. | | The issue is that this happens _blindly_ -- again without | questioning anything about the underlying premises. | | Doing so is called "cargo culting". And that's something | done by acolytes. (The words are loaded for a reason, | btw.) | | I'm coming from a language (Scala) where it took almost | 15 years to recognize that Haskell isn't anything that | should be imitated. Now that most people there start to | get it people elsewhere start to fall for the exact same | fallacy. But this time this could become so big that this | will end up like the "OOP dark ages" which we're just | about to finally leave. People are seemingly starting to | replace one _religion_ with the other. This won 't make | anything better... It's just equally stupid. It makes no | difference whether you replace one "hammer" with another | but still pretend that everything is a nail. | deltasevennine wrote: | You did argue for everything is the same. Basically by | "same" I mean everything is "equally good" depending on | context. The whole hammers are for hammering and | screwdrivers are for screwing thing... I explicitly said | your argument was that everything was a tool in a toolbox | and you exactly replicated what I said. | | My point is: something can be truly bad and something can | be truly good EVEN when considering all possible | contexts. | | You can't prove definitively whether this is the case for | FP or OOP or any programming style for that matter. You | can't know whether someones "side" is a cargo cult or not | when there's no theoretical way for measuring this. | | The cultish following may even be correct in the same way | I cargo cult my belief that the world is ROUND and not | flat. | hutzlibu wrote: | "You did argue for everything is the same." | | I do not see where he did that. He argued simply that | context matters. (And yes a "bad" tool can be the right | tool, if it is the only tool avaiable.) | | "My point is: something can be truly bad and something | can be truly good EVEN when considering all possible | contexts." | | And diving deeper into philosophy here, can you name one | example? | deltasevennine wrote: | >I do not see where he did that. He argued simply that | context matters. (And yes a "bad" tool can be the right | tool, if it is the only tool avaiable.) | | Well I see it. If you don't see it, I urge you to reread | what he said. | | A bad tool can be the right tool but some tools are so | bad that it is never the right tool. | | >And diving deeper into philosophy here, can you name one | example? | | Running is better then walking for short distances when | optimizing for shorter time. In this case walking is | definitively "bad." No argument by anyone. | | Please don't take this into a pedantic segway with your | counter here. | 0xdeadbeefbabe wrote: | Not deltasevennine, but giving the CPU fewer things to do | sounds good to me (in any context), even if it is | currently unpopular. Some cults are popular and some | aren't. | hutzlibu wrote: | "Not deltasevennine, but giving the CPU fewer things to | do sounds good to me (in any context)" | | Ah, but what if you are freezing and that CPU is your | only heat source ... | still_grokking wrote: | > My point is: something can be truly bad and something | can be truly good EVEN when considering all possible | contexts. | | No, that's impossible. "Truly good" or "truly bad" are | moral categories. Something closely related to religion, | BTW... | | > You can't know whether someones "side" is a cargo cult | [...] | | Of course I can. | | If it _objectively_ makes no sense (in some context), and | is only blindly copied from somewhere else _without | understanding why there things were done the way they | were done_ , this is called "cargo cult". That's the | definition of this term. | | How can I tell whether there is no understanding behind | something? If the cultists would understand what they are | actually copying they wouldn't copy it at all. ;-) | | Replacing methods with free standing functions is for | example on of such things: In Haskell there are no | methods. So free standing functions are all you have. But | imitating this style in a language with methods makes no | sense at all! It complicates things _for no reason_. This | is obviously something where someone does not understand | why Haskell is like it is. They just copy on the syntax | level something that they think is "functional | programming". But surface syntax should not be missed for | the actual concepts! Even it's easy to copy the syntax | instead of actually adapting the ideas behind it (only | where it makes sense of course!). | deltasevennine wrote: | >No, that's impossible. "Truly good" or "truly bad" are | moral categories. Something closely related to religion, | BTW... | | Wrong. Good and bad is used in a fuzzy way here, I'm | OBVIOUSLY not talking about morality OR religion. What I | am talking about are things that can be potentially | quantified to a formal theory. For example we know the | shortest distance between two points is a line. We have | formalized algorithmic speed with computational | complexity theory. O(N) is definitively more "good" then | O(N^2). | | Right now we don't have optimization theory or formal | definitions on logic organization. We can't quantify it | so we resort to opinionated stuff. And the whole thing | goes in circles. But that is not to say this is | impossible to formalize. We just haven't yet so all | arguments go nowhere. But the shortest distance between | two points? Nobody argues about that (I hope some | pedantic person doesn't bring up non-euclidean geometry | because come on). | | All we can say right now is because there is no theory, | nothing definitive can be said. | | >Of course I can. >If it objectively makes no sense (in | some context), and is only blindly copied from somewhere | else without understanding why there things were done the | way they were done, this is called "cargo cult". That's | the definition of this term. | | You can't. The definition of bias is that the person who | is biased is unaware of it. You can talk with every | single religious person in the world. They all think they | arrived at their beliefs logically. Almost everyone | thinks the way they interpret the world is logical and | consistent and it makes sense. They assume everyone else | is wrong. | | To be truly unbiased is to recognize the possibility of | your own fallibility. To assume that your point of view | is objective is bias in itself. You ask those people who | "blindly" copy things if they did it blindly, they will | tell you "No." They think they're conclusions are logical | they don't think they're blind. The same way you don't | think your blind, the same way I don't think I'm blind. | All blind people point at other blind people and say | everyone else is blind except for them. | | The truly unbiased person recognizes the possibility of | their own blindness. But almost nobody thinks this way. | | Nobody truly knows who is blind and who is not. So they | argue endlessly and present factoids to each other like | this one here you just threw at me: | | "Replacing methods with free standing functions is for | example on of such things: In Haskell there are no | methods. So free standing functions are all you have. But | imitating this style in a language with methods makes no | sense at all! It complicates things for no reason. This | is obviously something where someone does not understand | why Haskell is like it is. They just copy on the syntax | level something that they think is "functional | programming". But surface syntax should not be missed for | the actual concepts! Even it's easy to copy the syntax | instead of actually adapting the ideas behind it (only | where it makes sense of course!)." | | I mean how do you want me to respond to this factoid? | I'll throw out another factoid: | | Forcing people to use methods complicates things for no | reason. Why not just have state and logic separated? Why | force everything into some horrible combination? If I | want to use my method in another place I have to bring | all the state along with it. I can't move my logic | anywhere because it's tied to the contextual state. The | style of the program itself is a weakness and that's why | people imitate another style. | | And boom. What are you gonna do? Obviously throw another | factoid at me. We can pelt each other with factoids and | the needle doesn't move forward at all. | still_grokking wrote: | > Forcing people to use methods complicates things for no | reason. | | No, it doesn't. | | All functions are in fact objects and most are methods in | JavaScript, and there is nothing else. | | Methods (== properties assigned function object values) | are the natural way to express things in JavaScript. | | Trying to pretend that this is not the case, and trying | really hard to emulate (free) functions (which, to stress | this point once more, do not exist in JavaScript) makes | on the other hand's side everything more complicated than | strictly needed. | | > Why not just have state and logic separated? | | That's a good idea. | | This is also completely orthogonal to the question on how | JavaScript is supposed to be used. | | JavaScript is a hybrid langue. Part Small Talk, part | Lisp. | | It's common in JavaScript since inception to separate | data (in the form of objects that are serializable to and | from JSON) from functionality (in the form of function | objects). | | JavaScript was never used like Java / C++ / C#, where you | glue together data and functionality into classes, and | still isn't used like that (nevertheless they've got some | syntax sugar called "class" at some point). | | > Why force everything into some horrible combination? | | Nobody does that. At least not in JavaScript. | | Still that does permit to use methods. | | Functions themself are objects. Using objects is the | natural way for everything in JavaScript as there is | nothing else than objects. Everything in JavaScript is an | object. And any functionality the language provides is | through methods. | | Working against the basic principles of a language is a | terrible idea! (In every language, btw). It complicates | everything for no reason and has horrible code | abominations as a consequence. | | > If I want to use my method in another place I have to | bring all the state along with it. | | No, you don't. You need only to bring the data that you | want to operate on. | | The nice thing is: You get the functionality right at the | same place as the data. You don't need to carry around | anything besides the data that you work on. | | The alternative is needing to bring the modules that | carry the functionality that you want to apply to the | data you need also to have around... As an example: | `items.map(encode)` is nicer to write and read than | `List.map items encode`. | | You don't need to carry around the `List` module when the | method can already be found on the prototype of the data | object. Also it's more clear what's the subject and | what's the object of the operation. | | > I can't move my logic anywhere because it's tied to the | contextual state. | | That's just not true in JavaScript. | | Nothing is easier then passing functions objects around, | or change the values of properties that reference such | function objects. | | JavaScript is one of the most flexible languages out | there in this regard! | | You can even rewrite built-in types while you process | them. (Not that I'm advocating for doing so, but it's | possible). | | > The style of the program itself is a weakness [...] | | You did not present any facts that would prove that | claim. | | > [...] that's why people imitate another style. | | No, that's not the reason. | | You don't need to imitate Haskell when you want to write | functional programs in a Lisp derived language... ;-) | | People are obviously holding some cargo cult ceremonies | when trying to write Haskell in JavaScript. | | Your other opinions are based on wrong assumptions. I'm | not going into that in detail, but some small remarks: | | > For example we know the shortest distance between two | points is a line. | | In Manhattan1? ;-) | | > O(N) is definitively more "good" then O(N^2). | | Maybe it's more "good"... | | But it's for sure not always faster, or even more | efficient, in reality. | | Depending on the question and your resources (e.g. | hardware) a brute force solution may be favorable against | a solution with a much lower complexity _on paper_. | | Welcome to the physical world. Where practice differs | from theory. | | > But the shortest distance between two points? Nobody | argues about that (I hope some pedantic person doesn't | bring up non-euclidean geometry because come on). | | You don't need to look into non-euclidean geometry. | | Actually, even there the shortest distance between two | points is a "straight line". Only that the straight line | may have some curvature (because of the curved space). | | But you didn't even consider that "distance" is actually | something2 about that one can actually argue... | | > You can't. The definition of bias is that the person | who is biased is unaware of it. | | No, that's not the definition3. | | > Nobody truly knows who is blind and who is not. | | Nobody truly knows anything. | | What's the point? | | What was actually the point of your comment, btw? | | --- | | 1 https://en.wikipedia.org/wiki/Taxicab_geometry 2 | https://en.wikipedia.org/wiki/Distance 3 | https://en.wikipedia.org/wiki/Bias | [deleted] | _gabe_ wrote: | > I believe the world is round | | This isn't an opinion or a belief, it's a verifiable | fact. We know the world is round because: if you travel | east you'll eventually arrive at your starting | destination; if you stand on a ship's crow's nest you can | see further than the crew on the deck because of the | curvature of the earth; if you fly a plane and make two | 90 degree turns and travel an equal distance, you will | end up at your starting point due to the curvature of the | earth; if you go to space in a space station, you can | visually verify that the earth is round. | | Cargo culting acolytes will believe the earth is round | with no explanation as to why. Just because you believe | the right thing doesn't mean you're not cargo culting. If | you can't explain why you believe what you believe, | you're simply believing to follow a particular crowd, | regardless of the validity of the belief. | deltasevennine wrote: | Sure. | | I simply use this "world" example because everyone here | is in the same cult as me: "The world is round cult." | When I use it, they get it. If I were speaking to a | different cult, I would use a different example. | | You will note, both flat earthers and non-flat earthers | have very detailed and complex reasoning for why they | "believe" what they believe. But of course most members | of either group have Not actually went to space to verify | the conclusions for themselves. | awgm wrote: | yeah but sometimes its useful to use a flat earth model | when for instance the ground youre going to build | something like a shed on is relatively flat. in the big | picture sense i agree but in different contexts an | alternative abstract model can suffice and actually be | more efficient if the aim is to build the shed in this | case | AnimalMuppet wrote: | Nope. You're the one who isn't listening or | understanding. still_grokking's point is that _even if_ | one approach is better than the other, a slavish cargo- | cult adherence to that approach will still produce bad | results. | still_grokking wrote: | Thanks! Couldn't say this better. | | I'm not arguing about the approaches as such, I've stated | that cargo culting around any of them is a bad thing by | itself. | | Blindly imitating something for which you didn't fully | grasp all pros and cons will at best not help you and in | most cases make things worse. | deltasevennine wrote: | Nope. You aren't listening to me. I am getting exactly | what he's saying. What I am saying is that the cargo | culters could be right. You don't know. Nobody knows. | | Additionally he DID say that the approaches were all | tools in a toolbox. | ladyattis wrote: | I'm biased but I find the pattern for FP in LISP-like | languages easier to understand. I think that and other | languages with FP facilities are better to emulate than | Haskell which seems more focused on mathematicians. | toastal wrote: | Totally agreed. Coworkers will hate you if you start mixing in | these styles into an existing code base. If you want to do FP, | go switch languages (and likely jobs/teams) to something where | the ergonomic are clear, concise, and idomatic--then the | benefits become more obvious. It's a bit like teaching object- | orientation through OCaml... just because you can, doesn't mean | the community recommends it generally. | agentultra wrote: | Javascript is a multi-paradigm language as are most others | people are mentioning in these threads. It shows in the | specification for the language too: higher-order functions, | anonymous functions, Array.map/filter/reduce, etc. Like it or | not the language has facilities to enable programming in a | functional style. | | There are advantages to this approach like enabling different | styles when appropriate. | | There are disadvantages as well: you rely on the discipline | of the programmers or tools to catch mistakes. | | But there's nothing inherently _wrong_ about thinking of | programs in terms of FP ideas. There are other ways to think | about programming than in terms of program counters, control | flow statements, and procedures. It 's not even idiomatic | Javascript to write code that way! | | JS is a more functional language than most people seem to | think. | | The tragic thing about TFA is that it's not an article but a | chapter in a long series of posts and this is just one step | along the way to showing how an FP style _can_ manage the | complexities of a non-trivially large JS code base. | toastal wrote: | I won't disagree that JavaScript isn't multi-paradigm and | you _can_ write in a functional style, but you have to look | at where the ergonomics are: | | * dot, `.`, syntax on objects vs. composition/pipe | operators | | * no autocurry | | * no ADTs or basic pattern matching | | * has null + undefined but no Maybe/Option (there is null | coalescing and null chaining, but it's easy to accidentally | skip) | | * const can still mutate the on the object, freeze incurs a | massive performance cost | | * no tail-call optimization outside Safari despite being in | the ECMA2015 spec | | * `class` keyword existed and private methods and values | now, FP is pretty anti-encapsulation | | * no immutable data structures in the stdlib | | * no way to prevent side effects (/* __PURE__ */ in code | generation is a hack) | | Can you get benefits from avoiding state/mutation and | choosing composition over inheritance? Sure, but it's | nothing like being in a language where these aren't | something you _can_ do or the default, but the _only_ way | to write code and having the tool to express it. | agentultra wrote: | It's a mess for sure and you'll never get a reasonable FP | language from it. You won't get a Smalltalk out of it | either. Instead we get a mish-mash of ideas hashed | together by a committee of people with differing | backgrounds, experiences, and ideas. | | ... but some of those FP ideas are pretty neat and when | used well can make code that is easier to maintain (for | people who are familiar with the concepts). | rileyphone wrote: | It's actually fairly easy to get most of a Smalltalk out | of Javascript. Prototypes and first-class functions form | a really powerful basis for a higher language. Though, to | your point, it wouldn't really still be Javascript. | rory wrote: | Do you know of an example snippet / article demonstrating | how to do this? | rileyphone wrote: | Smalltalk in Self: nearly complete Smalltalk | implementation on similar prototypical language. | http://www.merlintec.com/download/mario.pdf | | Mootools: ancient class system for Javascript. | https://mootools.net/core | | Also, here's a very old Crockford article on implementing | classes in Javascript. | https://crockford.com/javascript/inheritance.html | wiseowise wrote: | You're implying that those things are necessary to write | reasonable functional programs. They're not. | agentultra wrote: | True. Reasonable to me is a pretty high bar. You can | write in a functional style in JS and feel it is | reasonable. You might feel differently after using | SML/OCaml/Haskell/etc. | tmountain wrote: | I love FP, but I wouldn't let someone introduce these | abstractions into our JavaScript code base, as they ramp up | the complexity in understanding code that can be represented | in a simpler fashion with the same results. | toastal wrote: | A loop isn't simpler than recursion or high-order functions | since it introduces state and is noisy, but most examples | of JavaScript/TypeScript + FP involve heavy usage of | utility libraries that aren't community standards (like a | Prelude). Not to say these libraries aren't valuable or of | good quality, but it's a lot to learn atop the language | itself for a new hire and can lead to some library lock-in | as said functions will end up all over the code. The folks | that preach the libraries the most are the ones that | usually want their JS/TS to look like Haskell which ends up | making it impossible for outsiders to follow (so now I need | to know JavaScript + TypeScript + Haskell + the weird | esoteric version these people write?). And those folks | should be allowed and encourage to just write some ML- | flavor-to-JavaScript code instead which can often be easier | to for a new hire to follow since all examples will follow | community standards (wouldn't be surprised if the coders | were happier writing it too). | AtNightWeCode wrote: | That example is what's called fluent code in OO in combination | with the builder pattern. To experience peak builder pattern | hell one should look at the Android API. | theptip wrote: | Thanks for this. I got the same growing sense of discomfort | reading the OP, that decomposing everything into operations | makes the code way harder to grok vs. just doing the entire | transform in one step. | | I think the point being made was "each transform could be | reusable across your codebase", but I think for this example | you really pay a high comprehensibility cost. And duplicating a | few lines of code is often actually the right call, instead of | coupling two unrelated bits of code that happen to be saying | the same thing right now (but which will plausibly diverge in | the future). | | As a new Rustacean, I do really like Result and Option, but | writing idiomatically in your language is really important too. | [deleted] | chris37879 wrote: | Exactly this. I realized that a lot of how I use objects fits | in nicely with FP patterns, but when I started looking into FP | "best practices" the number of times the best practice is | "Replicate this exact behavior from a 'more functional' | language, even though your language has idioms for that" is | astounding. I decided I'll just keep programming without side | effects, if that's what FP is. | RadixDLT wrote: | write more robust, maintainable code | Throwaway23459 wrote: | There's no agreed way to organise a functional codebase. You end | up with thousands of functions, can you imagine the mess a big | team is capable creating? With some discipline I suppose it could | work, but you would need some organising principle and some | serious discipline. Object oriented is organised by virtue of the | object paradigm. | piaste wrote: | > Object oriented is organised by virtue of the object | paradigm. | | Or in other words, OO code ties business logic to presentation | logic. By sticking to the dominant 'one class per file' | principle, the same code behaves differently based only on | whether it appears in one file or two, so it's not trivial to | move code around to make it more organized. | | When I write functional code, I first write all the functions | and types I need in a single file, without worrying about | presentation or naming until it compiles. Then, before | committing, I reorganize them into folders, files, and modules | so it's easier to read and navigate, and I can do it any way is | more appropriate (sometimes layer-first is more readable, | sometimes domain-first). | | I can also split pure functions into chains of smaller, pure, | private functions if they're too long to follow (90% of the | time some functions end up being way longer than I expected), | which is _way_ simpler than splitting a large class into | smaller ones. | tempodox wrote: | The languages in the ML family have modules. They are the | agreed-upon way to organize code and control visibility in | those languages. | dluan wrote: | Nice writing style! | dluan wrote: | Also the markup font is a little wonky and `Ok` ends up looking | like `0`. | mike31fr wrote: | After reading this article, I changed my mind about functional | programming. I no longer think I need to learn it asap. I now | think it may actually make my code more difficult to understand | and maintain, and will keep avoiding such patterns. | runevault wrote: | Learn FP with a language designed to be FP instead of one that | has the tools for it but isn't primarily meant to be used that | way. See: Ocaml, f#, Haskell if you want to go all the way off | the deep end, etc. | BoppreH wrote: | I love softcore FP (immutability, first-class functions, etc), | but I always get the impression that hardcore FP, like in this | article, is about reimplementing programming language concepts on | top of the actual programming language used. | | The pipe feels like a block of restricted statements, `peekErr` | and `scan` recreate conditionals, the Maybe monad behaves like a | try-catch here, etc. | | Of course there are differences, like `map` working on both lists | and tasks, as opposed to a naive iteration. That's cool. But this | new programming language comes with limitations, like the | difficulty in peeking ahead or behind during the list iteration, | reusing data between passes, and not to mention the lost tooling, | like stack traces and breakpoints. | | I've written many (toy) programming languages, the process is | exactly like this article. And it feels awesome. But I question | the wisdom of presenting all this abstract scaffolding as a | viable Javascript solution, as opposed to a Javascripter's | introduction to Haskell or Clojure, where this scaffolding _is_ | the language. | daotoad wrote: | I agree 100%. | | I've worked on some big TypeScript code bases that make heavy | use of sort of FP the article promotes; dealing with stack | traces and debugging the code is incredibly painful. Jumping | around between 47 layers of curried wrapper functions and | function composers that manage all state mostly in closures is | a real drag. | | Until the tooling is better, I can't recommend these idioms for | real work. | | TBF, there is a kind of beauty to the approach and as you | really dig into it and understand it, it can feel like a | revelation. There's something addictive about it. But any | feeling of joy is obliterated by the pain of tracing an error | in a debugger that isn't equipped to handle the idiom. | throw10920 wrote: | As a complement to what you said: a far better paradigm to | everything-must-be-purely-functional is "write as much of your | program as is practical in the functional style, and then for | the (hopefully small) remnant, just give it the side-effects | and mutation". | | This leads to fewer errors than the imperative/object-oriented | paradigms, and greater development velocity (and quicker | developer onboarding, and better tools...) than the | 100%-purely-functional strategy. | | Hopefully, over time, we'll get functional programming | techniques that can _easily_ model more and more of real-world | problems (while incurring minimal programmer learning curve and | cognitive overhead), making that remainder get smaller without | extra programmer pain - but we may never eliminate it | completely, and that 's ok. 100 lines of effectful code is | _far_ easier to examine and debug then _the entire application_ | , and our job as programmers is generally not to write purely | functional code, but to build a software artifact. | | The above applies to type systems, too, which is why gradual | typing is so amazing. Usually 99/.9% of a codebase can be | easily statically-typed, while the remaining small fraction | contains logic that is complex enough that you have to contort | your type system to address it, and in the process break | junior's developers' heads - often better to box it, add | runtime checks, and carefully manually examine the code then | resort to templates or macros parameterized on lifetime | variables or something equally eldritch. | | (and, like the above, hopefully as time goes on we'll get more | advanced _and_ easier-to-use type systems to shrink that | remainder) | bongobingo1 wrote: | The only thing stopping Functional Programming from taking off | are functional programmers. | | It took me years (and Elixir) to get past the "here are some | words, some mean what you think, some mean something completely | different to what you know, some mean almost the same thing. | Also here's 30 overloaded operators that you have to juggle in | your head and we wont introduce things like variables for | another 4 pages." without running back to something imperative | to actually _build something_. | | Functional programming's great when you have immutability to | drop an entire class of bugs _and the mental energy that | accompanies that_ , can pass-around and compose-with small | functions easily which makes writing, testing and figuring much | simpler and can make your brain click that "its all data" | without trying to over complicate it by attaching methods to | it. | | Honestly I think that's 90% of what the royal You needs to | understand to get FP and actually try it. Selling people on | "its all stateless man" or other ivory tower tripe is such | garbage because (as the tutorials always point out) a program | with no state is useless, and then the poor shmuck has to learn | about monads just to add a key to a map and print something at | which point they're asking "how was this better again?" | BoppreH wrote: | I think that's a bit harsh. It's frustrating to translate an | imperative task to an FP language, but it's also frustrating | to translate a Bash command into Python, or Prolog semantics | into Go, or any program full of mutations and global state | into (safe) Rust. | | I think a lot of the friction you mentioned comes from | learning imperative programming first. Our vocabulary, tools, | and even requirements come with an implicit imperative | assumption. | | PS: I didn't downvote you, though your tone is harsher than I | prefer for HN. | bongobingo1 wrote: | > I think a lot of the friction you mentioned comes from | learning imperative programming first. Our vocabulary, | tools, and even requirements come with an implicit | imperative assumption. | | Yes that's exactly my point and why no one cares about how | great a monad is when trying to learn FP. | wiseowise wrote: | How is that a problem of functional programming? When you | learn a language you don't start with "Well in English we | say..." you learn language with its idioms and quirks. | Izkata wrote: | > When you learn a language you don't start with "Well in | English we say..." you learn language with its idioms and | quirks. | | Idioms and quirks come after rote memorization of basic | vocabulary and sentence structure, which are then used to | learn the idioms and quirks. I think Bongo is saying FP- | ers are skipping the basic vocabulary part and jumping | straight to the idioms and quirks. | | It was interesting in 2015/2016 to see co-workers learn | some of that basic vocabulary of functional programming | without realizing they were touching on it, using | map/fold/etc when learning React. | bongobingo1 wrote: | It's not. It's a problem with people trying to sell FP to | non FP programmers. A non FP programmer doesn't care | about monads. They care about making what they already do | simpler and clearer. | wiseowise wrote: | Fair enough. I guess it's the same as when you're trying | to teach imperative programming starting from classes and | OOP. | shrimpx wrote: | I would argue that command sequences that mutate global | state are far more intuitive to humans (food recipes, | furniture assembly manuals, the steps to take to fix a flat | tire on your bike, etc.) than functions. So it's not just | what we learn first in a pedagogic sense. We're already | wired for imperative programming. "Thread the state of the | world through the food recipe instructions" is a ridiculous | concept to a normal person. | olddustytrail wrote: | > "Thread the state of the world through the food recipe | instructions" | | Oh, that's how it works? That actually makes sense. | Thanks! | kaba0 wrote: | I think it is very dependent on the problem at hand. | | Some traditional CS algorithm? Mutable it is. But a | compiler with different passes is a great fit for the FP | paradigm. | | But even if we go to pure math, you will have | constructive proofs beside traditional ones. | senand wrote: | I agree in principle. However, if you work imperatively, | you'll use _much_ more (global) state than if you worked | with a pragmatic functional language like Clojure. Which | in turn leads to normal people not understanding what's | going on, since humans are built to keep, say, 10 things | (atoms) in mind, not 100. | revskill wrote: | s = 1; s = 2; | | is same as | | unit(1).chain(add(1)) | | That's the beauty of immutable data. | skrtskrt wrote: | I strongly disagree, it is definitely the teachers and | advocates of FP which are lacking. | | There are plenty of programs and libraries written in | "imperative" languages like Python with lots of functional | style applied. That should be the starting point, not | sermonizing about whether a monad can even be defined with | English words. | alexvoda wrote: | Of course you can use English words. To quote: | | "A monad is just a monoid in the category of | endofunctors, what's the problem?" | | /s | TheOtherHobbes wrote: | Haskell is particularly, egregiously bad for this. | | None of the concepts are particularly complex. They're | unfamiliar, sort of, but they're not insanely obscure. | | But then there's the labelling. If an ordinary for-loop | was called a yarborough and a function call a demicurry | it _really_ wouldn 't help with the learning. | | I realise the concepts are supposed to map[1] to category | theory, but category theory stole words like monad from | elsewhere anyway. | | [1] Ha. | rkangel wrote: | I agree that Elixir is the ideal gateway into FP. It's also | quite a good argument that "FP" at its core is something more | fundamental than what is talked about in this article. Elixir | doesn't really use many of the category theory derived | approaches at all, it has a convenient way of doing method | chaining and some higher order function stuff and that's | about it. And the results are excellent code. | | The two main FP things that you need to learn to do Elixir | well (IMO) are thinking about data structures first, and | purity. Choose the right representation of the data at each | stage and _then_ what functions do you need to move between | each. Make those functions side effect free (but not | obsessively). Then you put your stateful code in GenServers | and you have useful real code and most of it was incredibly | easy to test. | skydhash wrote: | Pretty much this. I've been learning Common Lisp and the | way I try to design my programs is to have a somewhat clear | understand on how your data should transform from input to | output. Then you write the functions. | | Let's say your data is a log file and you want to load | everything in memory. You write the code that returns the | data, which you either store in a variable or compose with | other functions. At the end, you compose all of these | functions in your programs. Everything is an expression, | meaning that you can extract things easily into functions. | This is how you make things readable. `load-log-from-file` | and `auth-failure-count` is better than keeping everything | together in a single function. | nailer wrote: | > Functional programming's great when you have immutability | to drop an entire class of bugs and the mental energy that | accompanies that, can pass-around and compose-with small | functions easily which makes writing, testing and figuring | much simpler and can make your brain click that "its all | data" without trying to over complicate it by attaching | methods to it. | | A hundred percent. FP for me, is "let's stop gluing state and | functions together for no reason" and get all the benefits | above. I understand pure functions, avoiding state, closures, | and using first class functions as the basic building blocks | of creating higher level structures. | | When it becomes "a functor is a monoid of the schlerm | frumulet" - ala the typical FP advocacy blog / latest React | state management manifesto masquerading as library | documentation, I zone out. The odd thing is, I don't feel | I've lost anything by doing so. | disantlor wrote: | "softcore FP" is a great way to put it. | | JS with Ramda (and an FRP library, if needed) is the sweet spot | for me. I use a lot of pipes() but usually don't break them | down into smaller functions until there is a reason to; but FP | makes it trivial to do so. | zigzag312 wrote: | Improved type system to me is the most valuable. Compared to OO | inheritance fest, designing with types popular in functional | languages can be much more natural and easier to maintain. | | There are other valuable things, but IMHO the key is that, like | OO programming, they are valuable sometimes, not always. Being | explicit what is mutable and what can produces side effects is | really good, but forcing _everything_ to be immutable and without | side effects, is not. | | So, while I like some things about functional programming, I'm | not a fan of pure functional programming. I'd rather have a | language that supports mixing multiple paradigms, even though | that means giving up some things that are avaliable only in pure | functional languages. | mrkeen wrote: | > I'm not a fan of pure functional programming | | > Being explicit what is mutable and what can produces side | effects is really good | | _!!! That 's what Haskell is !!!_ | | What language are you using which is explicit about side | effects? | stevenalowe wrote: | interesting examples, but not compelling, i.e. procedural code to | do the same thing would also work (and might be easier to | understand for some). | | the thing i loved about FP was the elimination of exception | handling | | the thing i disliked about FP was how complex classes were | implemented as bizarre templates of structured list subtypes - | too much focus on the data types and structures of the | implementation, obscuring the domain-model (aka business) types | and structures required for human comprehension. | | disclosure: scala, a few years ago | | there is a happy medium...theoretically | twawaaay wrote: | I am a fan of mixing functional and object oriented (and also | some other paradigms). | | Functional is great in cutting amount of repeatable code. It is | great for writing tools to process things regardless of their | exact type and for writing infrastructure code. | | Object orientation is great when you want to model the domain of | the problem. Things that actually make sense when you are talking | to non-developers at your company. Clients, contracts, | appointments, employees, teams, invoices, things like that. But | when you start making objects that do not represent anything | meaningful you probably went a bit too far. | | I think knowing various paradigms, knowing when to use which and | how to marry them together is critical for maintainable | implementations. | AtNightWeCode wrote: | The science says the math approach to programming is wrong. That | is why the langs with very little need of understanding of math | are the popular ones. It's the langs where people can be more | productive and more easily be able to do things like code | reviews. The topic is just silly. | teg4n_ wrote: | Never go full FP in JavaScript. I had a tech lead try to reinvent | Haskell in JavaScript and everyone else in the team had such a | difficult time dealing with their code it was ignored as often as | possible. When you start seeing shit like a "blackbird" function | to compose some other functions and you have absolutely no clue | wtf the point of it even after reading the source and genuinely | trying to understand you start to feel stupid and a bit resentful | lmao. | Waterluvian wrote: | I'm a fan of pure programming because of its predictability and | testability. And I'm a fan of chaining array functions to make a | data processing pipeline obvious. I like how these can be opt-in | and don't require a totally different | paradigm/language/architecture. | | I'm guessing this may be seen as a part of FP but not the core? | [deleted] | adamwk wrote: | I wonder if the return of value semantics in languages lessens | the appeal of FP. Localized mutation lets you manage side-effects | but you don't need to bring in an esoteric discipline of math to | harness it. | mrkeen wrote: | How do you know if you're doing localised mutation? | | Rust and Haskell are the only languages I know which help with | this. | adamwk wrote: | I was thinking of Rust, yes, but also Swift which has less | restrictions on ownership but still requires exclusive access | to modifying variables ___________________________________________________________________ (page generated 2022-11-16 23:00 UTC)