12 ms·
Ask HN: Do you mostly program in a non-FP or non-OO style? Why?
Functional and object-oriented styles of programming dominate programming languages today. Are you using a language that has neither of these features? If so, what is the language and why do you use it?
Or are you using an FP or OOP language but rarely use the FP or OOP features? If so, why?
- seanmcdirmid 9y agoI primarily use nouns for the macro architecture but use lots of verbs in the details. Neither style especially dominates, and most languages have enough support for both even if the tilt one way or the other. Most people pigeon whole other styles into one or the other, sometimes ambiguously (e.g. actors get counted as both OOP and FP, relational also has that problem).
- llogiq 9y agoData scientist/engineer here. I write Java by day, Rust by night and in both I embrace a data-driven design, where I first carefully lay out the data so that it's easy to use within the code. In hot loops, I avoid dynamic dispatch for its runtime cost. I don't embrace the FP style either because while I generally limit mutation (`final` almost everywhere), I use mutation where it leads to code that is easy to understand, which would be exceedingly "clever" in FP style.
- 5ilv3r 9y agoI try to only use capabilities that are common to all languages. Some basic math, basic conditionals, and maybe poking a file or memory address somewhere. I don't give a enough of a crap to get excited for new but short-lived features anymore.
- leksak 9y agoC
- frou_dh 9y agoStarting with C has made me consider it a travesty whenever seeing a language where "main" is forced into being a method on a class. OOP as a technique is fine, but any language disallowing freestanding functions is perverted, I tells ya.
- leksak 9y agoI agree. I prefer pragmatic languages as opposed to dogmatic ones which is probably why I'm friends with Kotlin
- k__ 9y agoI mostly code in JavaScript. Try to use FP most of the time, but sometimes a little mutation seems easier to grasp.
- messe 9y agoPhysics student here. I write a lot of python scripts/notebooks, which tend to be very imperative in style. They're usually just one-off programs, and there's little in them to be abstracted out. I rarely drop into C, as numba+scipy is usually fast enough. Other than that, I use Mathematica quite a bit for playing around with ideas. I've tried sympy, and I hoped to switch to it but it's just not as fluid and integrated. If I'm coding for fun I tend to come back to FP and OO, I'll use either Haskell, Python or CL. I've been meaning to make something using Rust for a while. I'm planning on building a fermentation chamber (and perhaps a kegerator depending on my budget) for homebrewing at some point over the next year; I'm thinking that I might use Rust for the temperature controller.
- glup 9y agoSimilar case here as a cognitive science PhD student... analyses (Jupyter notebooks) are basically imperative but if I need to write a library to support the analysis (which I almost always need to do) then OO.
- pacala 9y agoScala[!] NLP/symbolic math.
- hyperpallium 9y agoI mostly use a procedural style (i.e. C like) in java, for working out new ideas in solo projects. It's simpler, more flexible, less verbose and easier to follow than a full-on OO style. A great example is the early calculatoe e.g. in ed 2 vs 3 of the compiler "Dragon" book: older uses C, later uses OO java... and it's so much worse. However, OO is great for wrapping up modules of functionality for which you've understood and settled on an informed architecture (or you just need to sweep repetitive boilerplate away). I think inheritance is just about completely useless (but not quite completely), and (java) interfaces are great - if you have more than one implementation. Big, multi-person projects are a different story. I don't use much fp-style, except where recursion is natural (e.g. parser combinators); or for plug-in functions (hardly fp though).
- morbidhawk 9y agoI was writing a small but semi algorithm-heavy library in C# that didn't rely much on other libraries. I later decided to port it to Java and realized that the more C#-like I had written the original code the harder it was to port. Later when trying to port it to JavaScript I ended up re-writing the C#/Java code to rely less on the standard libraries since each standard library differed so much. At that point my code started to look very procedural in the OO languages possibly similar to what you've been doing in Java. I came to the realization I didn't even need a lot of the language features for what I was doing and decided to switch it to C to not have to maintain various versions of the library and it was a steep learning curve but I've come to really enjoy the simplicity but powerful capabilities of C.
- le-mark 9y agoThanks for sharing this, it's a great, succinct example of the journey many experience. You could flesh this out and post it on medium or something to get the ideas out there for discussion.
- falcolas 9y agoPrimarily a Python and Go programmer. OO: Only rarely, and only where it really makes sense. Most programs I work on are not complex enough to justify the overhead (boilerplate, cognitive). FP: I use this more, but again, only when I can really justify the added cognitive overhead. You'll notice the two references to cognitive overhead: I have adapted the "write for a 6th grade level" idiom into programming. I want as many people to be able to pick up the code and modify it as possibly can. I work with everything from interns to decades plus veterans, and my code must be grok-able by both. Pure FP tends to confuse interns, and veterans have a tendency to hate the layers and layers of indirection and abstraction OO brings into a program. Normal and boring old "an imperative main method with functions" tends to be the least offensive and most understandable to all parties. Compromise r fun.
- solipsism 9y agoHow do you test your code? When a module of code, C, depends on abstraction X, how do you test C in isolation from X? The common way to do this in OO (and yes this very much includes Go, whose standard library is incredibly OO) is to have C depend on an interface instead of a concrete X. Then you have full freedom to test C very robustly and at different abstraction levels. So how do you cope with this in your code? "Imperative main method with functions" doesn't sound like it leaves room for proper testing, unless the code doesn't do much.
- rpedela 9y agoThe functions are the interface. Just because the interface isn't formalized by a class or "interface" definition doesn't mean there isn't an interface.
- solipsism 9y agoIf you have an implicit interface then you are implicitly doing OOP. You may be intelligently eschewing the worst parts of so-called OO languages (too much statefulness, and inheritance), but by using the best concepts of OOP you're demonstrating how great OOP can be when really done right. I will point you to this interview where Joe Armstrong describes Erlang as perhaps the only Object Oriented language (countering his earlier, less considered view that OO sucks): https://www.infoq.com/interviews/johnson-armstrong-oop https://www.infoq.com/interviews/johnson-armstrong-oop
- itwy 9y agoPHP. Good old procedural PHP. Cleaner than most OOP and FP projects in the wild and I can code large things quickly and easily. The code is very maintainable as well. I even avoid PHP's OOP solutions with the exception of PDO which I wrap in procedural functions.
- cratermoon 9y agoMy current gig has me writing Go, which doesn't do much more than wave a hand at OO to start with, and the codebase I'm working with is particularly un-OO.
- KirinDave 9y agoEven in OO languages, I'm almost exclusively in an FP style because that style focuses on minimizing redundant code (e.g., don't write loops write loop transformer functions), minimizing redundant top level functions (segmentations, hand-tuned stateful getter/setters, etc), and maximizing the opportunity for correctness. The last part is pretty important because properly testing software that talks to an external service is essentially impossible. Either you test in an integration setting where repeatable results are difficult to maintain, or you mock out your services and then you're testing your opinions against other opinions. Here's an example of how I push this style to typescript, heavily redacted to remove company-specific marks, names and strutures: (this code is simple by intention; we may have a jr dev on the team soon and an excessive number of generator abstractions could help with this process but would cause readability issues). function splitOrders(orders: e.OrderResult[]): {[key:string]: e.OrderResult[]} { return orders.reduce( (accum, borders) => { // I sure would love a better idiom for non-destructive map updates but // this isn't my primary language. Suggestions welcome! let next = {...accum}; next[borders.datatype] = (next[borders.datatype] || []).concat([borders]) return next }, {}) } export async function undoifyUserProcess(env: any, tolkien: client.tolkien, token: client.APIToken, spotData: IThingData): Promise<any> { const legendaryCustomer = await e.getCustomerById(env, spotData.customer_id); const orderResults = await e.findThingContractDetails(env, +spotData.spot_id); const workSchedule = splitOrders(orderResults); let btasks = scheduler.undoifyCustomerFromOrders( env, token, { key: legendaryCustomer.userKey }, workSchedule['newstyle'].map((wi => wi.bigId))); let ltasks = (workSchedule['legacy'] || []).map(async (workItem) => { // .. process elided but... const nodeKey, groupKey = ["things", "were", "removed"]; const customerEmail = legendaryCustomer.some_field + "removed work" // Factoring this out would have introduced a function with a bunch of parameters // or state that is only synthesized here, so we left it embedded & closed over. try { return await omg.undoifyUserWithTagGroup(env, tolkien, customerEmail, nodeKey, groupKey); } catch(e) { console.log(`... malformed database call your mom for help ...`); throw e; } }) try { const work: Promise<any>[] = [btasks, ...ltasks]; const result = await Promise.all(work); return await e.recordUndoifyingThing(env, +spotData.spot_id); } catch(e) { console.log(`...`); } } If a language can't express a decently abstract functional style, I do my best to avoid it. Still, I've had to write a bit of Golang at my current job despite its utter lack of descriptive capabilities. It's poignantly frustrating to deal with, but a good reminder of how bad life used to be for programmers.
- throwaway7645 9y agoI primarily use Python scripts in my day job to parse data, automate job runs, analyze data...etc. Rarely is anything complicated enough to abstract without making the code longer and more complex. Anybody can follow imperative code. OO is more difficult.
- tscs37 9y agoI mainly write in Go. My adopted approach is to be pragmatic about how I approach my problems. I kinda tend to start with a OOP style for the data models and a small shake of FP for the functions but change this according to my needs. There is no one true style for programming, there is only tools for a task and using the best or good enough tool is the only important thing. (Though sometimes it's fun to use the wrong tool and see how it goes)
- lgas 9y agoFP. Because it makes most things easier while resulting in software that works more consistently.
- morbidhawk 9y agoMy preference recently for side projects has been imperative/procedural style of programming. I prefer it to OO and FP hands down. To me code is so much easier to read when you don't have clever abstractions everywhere, most code reads line by line and isn't trying to hide anything. I think there is a tradeoff between concise terse code you find in OO/FP that reuses other code vs the long step-by-step procedural code. For me personally I'd prefer longer code that is not reusing a bunch of other functions. It's almost a weird paradox: OO/FP focuses on reusing code yet imperative/procedural code (ie: C) does not reuse code but is itself the most reusable code. You can't have your cake and eat it too I guess.
- jstimpfle 9y agoReusable code may be not the best code. There is probably a lot of redundancy (cross-cutting concerns). Or alternatively, increased interface complexity, to the point where it's easier to just write a custom-tailored version of the code. I still like to program in C and make my own thin abstractions / "runtime", tailored to the task at hand, and using only minimal dependencies. That way it's much easier to have only essential dependencies, not accidental ones. It's such a relief seeing a couple thousand lines of cleanly modularized C code compile almost instantly. For example, by putting a char * into a struct definition a dependency on a more clever string type can be avoided. All that it takes is delegating memory management decisions to another place. Which is actually very beneficial, since most code shouldn't be concerned with mutation or especially allocation.
- platz 9y agoredundancy/duplication is not a boon to maintainability admitting a revolving door of co-authors.
- Animats 9y agoI've written almost entirely object-oriented code for two decades. My programs have almost no global variables. I've tried functional programming in Rust, but it's not going well.[1] I don't like the "x.and_then(|foo|).if_even(|bar|).except_on_alternate_tuesdays(|baz|)" style. Rolling your own control structures is not good for readability. [1] https://github.com/John-Nagle/rust-rssclient/blob/master/src/wordwrap.rs https://github.com/John-Nagle/rust-rssclient/blob/master/src...
- d1ffuz0r 9y agoPython with tricks from Erlang, PHP frameworks, Lisp. Do FP for most of things, using OOP to structure the code when reuse is necessary: e.g. connectors to external APIs
- analog31 9y agoProcedural programming is what I learned first (Pascal), and I trust myself to write good code in a procedural style. I do some microcontroller programming, and it's usually straight C. The hardware registers are all global, and their contents change based on external stimuli, so that kind of rules out the idea of stateless programming. The earlier versions of Visual Basic had kind of a compromise, where it came with a lot of pre-made objects, and you could create objects if you got the special kit, but the casual programmer was only expected to use objects. I kind of adhere to that idea when I write in OO languages such as Python. I use OO sparingly when programming Python, often to encapsulate hardware functionality, but then use those objects within programs that still look procedural. I avoid using inheritance, mainly because I don't trust myself to do it in a maintainable way.
- rasjani 9y agoLast project I did was mainly procedural and that's my default go-to type of structure but OO has its place too. For example in that last one, i had few objects that implemented same functionality but with different data structure. So if I wanted to later on add new data structure, just adding a new class with predefined and partly dynamic features, I'd only need to implement a new class. For data iteration, i prefer functional style (even in python, even after years list comprehension just feels so wrong).
- nnfy 9y agoPersonally, I learned to code in an oop style. I cut my teeth on CPP and then a few years of c#, but these days I work in python. My problem with non OOP in a language like python is that I have trouble scaling large apps and interacting with large libraries, because without type information enforced by the compiler, there is too much left to my own working memory, compared to a strongly types language built around OOP. What's more, without rigid encapsulation I feel im exposed to an excess of internal code any time I need to look up a parameter or kwarg. Never mind the trouble of navigating five files feel through aliased method names... As examples, consider matplotlib or tensorflow. Extremely easy to use once you know where everything is and what params to pass where, but I can't help but feel that it would be easier with stronger OOP. I feel like I have not yet learned to think in a pythonic manner, but after a few years I'm not sure if I every well. As an aside, I just dont understand how python doesn't cause problems for devs when it scales...
- zaptheimpaler 9y agoI use Scala - it can be used or abused to do advanced OOP and advanced FP. I tend to see programming as writing. The style of writing depends very much on the context and the audience. I lean towards "basic" FP (immutability/pure functions/composition) but none of the heavy category theory concepts (anything with types that are too complex). Its mostly procedural with a hint of FP. Most things should be functions, most higher level things (classes/packages) should be primarily ways to bundle related functions. Prefer to maintain a clear separation between data & operations on data or structures imposed on top of it. I dislike the OOP approach of bundling data + transformations together. Also dislike inheritance because that is baking one particular structure into the definition of data.
- imh 9y agoSQL is neither FP nor OO! It's not a general purpose language though, so it may not be a great answer. It's still amazingly useful and incredibly prevalent.
- nnq 9y ago1. "Shallow" OOP - minimal use of inheritance, composition ok but without making a Russian doll with 7+ layers with it. 2. "Grand scale Functional characteristics" - exposed API should try and have "referential transparency" and "composability" (you can write systems with "functional properties" in languages like PHP just fine btw.)... find not much benefit from "small scale / low level" FP. 3. Wrap stateful algorithms & other code rich in mutable variables in either (a) referentiaally transparent functions or (b) shallow objects that make it obvious how and when state changes. 4. Avoid the "islands of functional purity in a sea of objects" pattern like the plague - it results in large scale intellectual masturbation at the small scale, and incomprehensible systems at a large scale, your monadic fantasy is useless when stuck inside the method of a 8-levels-inherited monster object... functional is important on a whole-system level, a 10 line method is easy to understand even if it mutates local variables all over the place In theory FP would be great at all scales when coupled with a good type system... but I've never got to work on projects in languages like Haskell or Scala and I'm not sure I could handle the cognitive overhead of it. Oh, and don't use Exceptions, ever!
- nnq 9y agoOn programming languages: It also doesn't matter whether the language has such a good support for functional programming when you don't care about doing in on the small, inside inner-inner-function level, so `map`, `foldr`, functor or whatever... make no much difference. As long as you have the basics, like "first class functions" and "lexical closures", you can do "large-scale functional" programming in languages like Go just fine. It actually feels more refreshing in a minimalist language like this and with a minimalist and explicit type system :)
- ajarmst 9y agoOOP wasn't really yet a thing and GUIs (which I think of as the killer app for OO) were still experimental when I was first learning programming. I started out with Fortran and Pascal on a timeshared mainframe. Probably because of this background, I still find OO languages ungainly, especially the huge libraries that are usually associated. I'm also a bit of a skeptic about the purported value of the OO abstraction, which always seems to cherry-pick from a few problem classes that lend themselves to representation as interacting objects. While I sometimes teach programming in C# and C++, my main responsibility is our Algorithms and Data Structures courses, for which we've kept using plain old C. From a teaching perspective, it's a great language for teaching fundamental algorithms and data structures. The students are also able to leverage that work into later courses on microcontrollers and embedded systems. For my own work, I tend to mostly be writing code for somewhat idiosyncratic one-off data processing (I'm currently working on a genetic algorithm for class, student and professor scheduling) or embedded systems. The former I generally work in Common Lisp (SBCL)(although I do keep promising myself to learn enough Clojure to see if I prefer it). This is probably because of the way I think, as I tend to be comfortable building a system from bottom up, moving the data representation from the general facts I start with toward a representation that meets my needs. I actually came to CL pretty late, about a dozen years ago, but it really seems to suit me, possibly because I'm still primarily a command-line person. Being an emacs user certainly was a strong influence, too, although I don't hack elisp very much. I should add that I do often use CLOS, especially when working with more structured data, so I do use some OO, although it's mostly just to provide a more convenient interface to some complicated data type. For embedded development, I still work almost exclusively in assembler or C, with less assembler every year. I tend to be working on pretty low-powered special-purpose devices, so code reuse and robust interfaces don't add much value, but close control of exactly what the hardware is doing does. This was normal in that domain until comparitively recently, but the power of even cheap devices and the availability of libraries mean that there are a lot more options. I expect that I'll continue to work mostly in C out of familiarity and inertia, but I will admit to having recently bought some Lua books with the intent of trying it with ESP8266.
- lj3 9y ago> which I think of as the killer app for OO Was this written in a book somewhere? I keep hearing it from people, but nobody's able to really explain why. OO is terrible for GUIs IMHO.
- fpoling 9y agoWhen I have an option, I program in a procedural style. The code is easier to maintain and follow this way. For side projects and for small Python code at work recently I have been applying data-oriented programming patterns. So far they worked surprisingly well. Perhaps this is because one have an overview of the whole program state that is not hidden behind multiple layers of OOP abstractions and there is no mix between data and code, as happens with functional style.
- jstewartmobile 9y agoI strive for FP, but since most external libraries are OOP, I've made my peace with it. That's probably not the right question though. If we're just focusing on implementation, using the right data structures for the problem would have to be job #1. Screw that up, and everything else (APIs, UIs, enhancements, training, you name it) is going to be an uphill climb.
- gt565k 9y agoI primarily use a declarative language called LogiQL, which is a superset of Datalog. It's basically logic programming. Using predicates and rules. We have a proprietary database called LogicBlox, that allows us to push all business logic down to the database layer, and almost entirely avoid having a service layer, or at least a very lean one. This allows us to address efficiency problems purely by optimizing database joins between predicates (tables). Our data is highly normalized (6NF). http://www.logicblox.com/learn/ http://www.logicblox.com/learn/