Clojure at a Real Estate Portal
pitheringabout.com
pitheringabout.com
Not sure why he just didn't say it, that information is available elsewhere anyway.
I created a kind of a template with how to organize namespaces around that and how to address and automatically parse the URL/form parameters depending on the URL and send a "missing param" or "malformed param" back in the appropiate cases. It also deals with optional params
The app basically only abstracts away the HTTP Server stuff and calls the appropriate call in an API namespace (where someone else could hook in if he wants to use his own server).
Also, I now save all DDL in a separate resources/SQL folder and parse it if I need to rewrite the DB from clojure (e.g., at initialization after deploy). In the folder there is a schema.sql in which all other DDL is called via \ir.
Checking whether the current version is the most up to date I still do manually (so if I add an index to some table in the DB, I add checking for this index via postgres native tables in the migrated? function) but this will be automated as well some time in the future.
The reason why I use org.clojure/java.jdbc is because I can use all native postgres features (arrays, jsonb, tsvector, ...) without using the very weird java.sql constructors. This is also one of the major points against any current library.
I think the right question should be FP vs imperative languages? As far as I know even functional languages can have OO features, but functional programming is more about getting away from an imperative style of programming.
Maybe someone who knows more can chime in though.
But in my personal experience, using a functional language just helps me write correct code. Using an imperative language makes it easy to be sloppy and include bugs
For me understanding the greatness of FP was build on the understanding that there are no "benefits of OO".
Once I realized that the "abstractions of classes and inheritance" and "encapsulations of state in objects" (and other OO-paradigm pillars) where harmful, I started truly researching the alternatives.
I could go back to C --imperative, state-all-over-the-place, self-managed memory-- but it seemed like an impossible step in web development.
Quickly I found promise in languages like Clojure and Haskell. High-level enough for web development, yet faster them my trusted Ruby code, and seemingly more maintainable.
The main benefit of OO I know experience is it's popularity. Going from Java to Ruby is a lot easier then from Ruby to Clojure or Haskell. The good news: many OO languages are incorporating FP'isms, and allowing a functional approach to solve problems, this helps FP like myself newcomers quite a bit.
TL;DR: I came to the realisation that the benefits of OO where mainly theoretical and did not hold in practice. Main 'real' benefit of OO is its popularity.
When people talk about how their favorite FP language is not OO, it raises my hair a bit and wonder what kind of code this person produces. A Clojure programmer will throw out some multi-level map reduce that abstracts over arbitrary sequences and then jam an ordered tree map into it, and claim it's not OO. And I'm like, what do you think polymorphism and encapsulation are?
I guess my wording is not entirely accurate. I meant OO-class-based-inheritance and OO-class-based-encasulation-of-state.
> I'm like, what do you think polymorphism and encapsulation are?
Well, when writing Haskell I'm not dealing with object instantiation, OO-classes and OO-subclassing. I have no problem with encapsulation per se; it is an important tool of creating abstractions needed to reach any kind of productivity. It is more that the OO-style of encapsulation is no longer something that I believe works in the long run. In popular Haskell terms "it does not compose" or more lay-men terms "its likely facilitates creating a lot of maintenance burden".
This leads to both a narrow idea of what OO is (mostly limited to the way those languages differed from C), and the idea that OO is incompatible with FP (because early Java and C++ had poor FP features, even though some earlier languages in which the OO paradigm had been supported had much stronger functional features.)
I think what the person you're replying to is saying is you actually are. You don't literally write "Foo x = new Foo()" but that's just a syntactic feature of some language. Haskell has types and classes and sub classes. You just don't write them the same way.
In the theoretical sense OO and FP share some concept like encapsulation and polymorphism. But a class in C++/Java/Ruby is whole other thing then type-classes in Haskell.
First, Clojure is very much inspired by OOP (with actual OO features). It has not one, but two subtype polymorphic dispatch mechanisms ("virtual dispatch" in OOP parlance): multimethods and protocols. These aren't just any features. Much of Clojure's functionality and design is built around protocols, which is just your plain-old OOP polymorphism. So some OOP concepts are not only present in Clojure, it is central and essential. OCaml, of course, is also FP and OOP, as is Common Lisp.
Second, what you call FP'isms have been part of OOP for about 40 years now, and not only Smalltalk. Closures were a planned feature of Java 1.0, but were dropped because of deadline considerations[1]. In fact, most features you associate with FP were very well known when OOP came about.
So OOP and FP have never been in opposition to one another. Just sets of features that you can choose from. The thing is, that in spite of having some sharp corners, OOP has proved very apt as a paradigm for building large maintainable system. In fact, we know it's done a much better job than the previous prevailing paradigm of structured programming, that we can't be sure whether OOP's "downsides" are anything particular to OOP or simply a result of any imperfect paradigm that has achieved such wide usage by programmers of a very wide range of skill and experience.
As to non-OOP FP, it is quite possible that much of its allure is nothing more than its relative novelty to developers today, and that it hasn't yet suffered much abuse in the hands of inexperienced programmers. But we don't yet know whether it would fare as well as OOP in the development and maintenance of large, long-lasting software.
It's best, I think, not to be too dogmatic about these things. OOP has some great ideas, as does FP. Both have their shortcomings.
My admiration for Clojure comes not from its abandonment of OOP (because, as I've explained, it didn't completely abandon OOP), but its very wise selection of features from both OOP and FP, while keeping the language very simple and incorporating some very interesting new ideas (all shared data is immutable/transactional). It does remain to be seen, however, how Clojure fares in large projects over time.
[1]: http://www.blinkenlights.com/classiccmp/javaorigin.html
Rich Hickey did say that the big problem with OOP is only that OO languages mix many concepts together (identity+state+polymorphism+encapsulation+...etc) and that Clojure has all of these things, but separated out into their individual pieces, so that the programmer can choose which of them make sense to the problem at hand. Basically, that the ideas behind OOP are good, but that you should be able to choose just the bits that make sense for the situation you are in and not pay for the ones that don't (eg in terms of complexity or understanding).
That's true, even though OOP as a paradigm was articulated before FP was (both paradigms were prefigured by less-structured practices in programming, before either was articulated as a programming paradigm, so the techniques of both existed before either was cataloged as a paradigm.)
> So OOP and FP have never been in opposition to one another. Just sets of features that you can choose from.
OOP and FP aren't sets of features, they are approaches to programming; there are features of languages which facilitate FP and/or OOP, to be sure, but the paradigms aren't sets of features.
> As to non-OOP FP, it is quite possible that much of its allure is nothing more than its relative novelty to developers today,
I doubt it; non-OOP FP has been around almost as long as OOP has been, has frequently played a fairly central role (whether or not it was explicitly named as a paradigm) in computer science in education, and, in any case, and it became popular when domains where it particular implementations of it had long been proven (particularly, concurrent/distributed systems) started becoming part of the problem space of mainstream development.
The idea that its popularity is driven primarily by novelty doesn't seem like the best explanation.
Yes, but not a big role in OOP's home court: large systems with long lifespans.
> The idea that its popularity is driven primarily by novelty doesn't seem like the best explanation.
I didn't say that. I said that the low number of reported problems is likely a result of it not having seen much actual combat and coming under heavy enemy fire just yet (namely, large projects, maintenance over many years, some by inexperienced programmers). It's still fresh out of basic training (in comparison to OOP). OOP also seemed almost perfect for quite a few years.
I use Clojure professionally, and CLOS is easily the thing I miss most.
Most of those FPisms (LINQ) are already present in Smalltalk.
The real problem comes from completely ignoring data structures. By mixing in functions with data, proper data structures are virtually nonexistent in the OO world and manipulating data properly becomes an exercise in frustration and working around the limitations of the OO implementation (data that should be together isn't; data that shouldn't be is; easy access to the data, etc.). In this regard, I'd say procedural programming is on roughly equal footing with functional programming in as much as they both tend to focus heavily on data structures first. As they say, show me your data structures and I don't need to see the code; show me your code and I won't know shit.
Your point that OO's main advantage is popularity is spot on. It certainly is possible to build large, maintainable systems in an OO fashion, but it is very difficult and requires a lot of experience. That's why there is so much terrible OO code out there. It's virtually impossible to write decent code without a lot of experience. This mammoth learning curve is not as steep in languages that focus on data structures first, whether they be procedural or functional.
What do you mean, given that Smalltalk, JavaScript, SELF, Ruby, Python, Dart, Dylan, Common Lisp, Julia, StrongTalk ....
Are all dynamic languages with OOP features.
> encapsulation
Encapsulation is primarily a state control concept. It reduces the surface area of a piece of data to the object it's contained in. The reduction in the amount of code that can touch the data makes it easier to reason about. Clojure works around this by having most of its data be immutable and most functions pure. Debugging is walking up the stack trace to find the change you didn't expect.
> composition
Pure functions compose trivially. Encapsulating data in a class actually limits your composition by design. Using maps lets you re-use the many functions in the standard library pretty much everywhere.
> interfaces
Clojure has an extended version called protocols. It provides an api guarantee but it can be retroactively implemented on Java classes you don't control. Look up the expression problem in clojure for considerably more depth. You can also use multimethods if you want to dispatch on something besides class.
> Patterns
I subscribe to the argument that patterns are workarounds for limitations in a language. Most languages have them but nobody else formalizes them like the Java people. There are ways to accomplish the same objectives, generally with less ceremony.
> SOLID
Single Responsibilty - Concept carries over to functions in a namespace, a good idea in general
Open/Closed - I've always understood this to mean encapsulation, see above.
Liskov substitution - Dynamic languages are duck typed. If you want more assurance you can add something like Schema, which does structual subtyping instead of nominal subtyping.
Interface segregation - Preference in the community is for protocols to be VERY small, usually 1-5 functions.
Dependency inversion - Tends to not come up. Usually when you're designing an API you have a good idea for when a consumer is going to want different behavior. In those cases, you have the API take a function instead.
Are the comparisons are hard to find online, or are they not addressing your exact issue? What benefits of OO do you think you'd be losing?
This is what scares me the most in Clojure and all other dynamic languages - that early on in the project, I'd make an unfortunate decision which I wouldn't be able to go back on once the project goes over 2-3kloc... without introducing hundreds of potential runtime errors, that is.
In practice refactorings such as changing Address->MaybeAddress didn't hurt us so much. With good test coverage also, it's not a huge issue.
Clojure code-bases tend to be more much concise and well organised, so refactoring based on expected data and output doesn't bite as much as people often fear.
I've noticed a huge emphasis on testing the sytem, specifically through means of generative/property-based testing.
Or take this discussion on testing: "Finally, testing is greatly facilitated by design. Ideal testing takes some design constraints, some specification and turns it into tests as opposed to sort of embodying design inside tests. That's inside out. But again, that's something we have to work more at. Stems like Quick Check are interesting because you're basically starting with propositions about your system, which reflect the design and saying you write the tests, computer." (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...)
Do you really think that just because you have automated tests, that your software is defect free? Tests (outside of generative/property-based testing, which is still under-utilised) aren't very good at finding bugs outside of regressions. Having said that, most Clojure code worth talking about does have automated tests.
In my ideal world, I would use expected-case Unit Tests to test the contract/requirements/interface. Regression tests to prevent fixed bugs from reappearing and generative tests to try and find bugs. I would write the expected-case tests before the code, use REPL testing while developing and finally write property tests after (once my code has evolved so that I understand the properties) and regression tests only as I fix bugs after the fact.
[1] http://www.infoq.com/presentations/Simple-Made-Easy (15:30)
And Rich Hickey isn't against testing. He was having a jab at test driven design.