Hacker News new | past | comments | ask | show | jobs | submit login
Clojure at a Real Estate Portal (pitheringabout.com)
156 points by dodders on Dec 14, 2015 | hide | past | favorite | 45 comments



If anyone is interested, he's talking about onthemarket.com.

https://www.onthemarket.com/

Not sure why he just didn't say it, that information is available elsewhere anyway.

https://juxt.pro/

http://blog.juxt.pro/posts/otm.html


I use an eerily similar stack with clojure, ring/compojure, http-kit, java.jdbc + c3p0, timbre and I have to say it's fantastic.

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.


you should replace c3p0 by hikaricp https://github.com/brettwooldridge/HikariCP


Semi off-topic, but might helps others as well: What should an experienced OO dev read to better understand how dynamic FP languages, like Clojure, alleviate the aforementioned cons of OO in the architecture of large-scale agile projects? In other words: I know from experience what's the downside of OO, but I don't know how dynamic FP languages would help without sacrificing the benefits of OO?


I might just be confused, but from what read/understand I don't think FP vs OO is a proper comparison.

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


> the benefits of OO?

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.


Alan Kay claimed that the big ideas of OO were polymprhism and encapsulation, which not only are first-class features of Clojure but one of its main selling points, via protocols. Haskell type classes support behavior inheritance and subclassing, with monad subclasses being the preferred way to encapsulate state.

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?


Alan Kay believed the big idea in OO is message passing: http://c2.com/cgi/wiki?AlanKayOnMessaging


Thanks for your reply.

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".


I think one of the reasons people talk past each other on this issues is for lots of people, "OO" isn't the concepts that had been around under that name for at least a couple decades by the 1990s, but rather the particular mechanisms by which those ideas were realized in the popular OO languages of that time, most notably Java and C++. While for other people, Java and C++ are just examples of a particular approach to adding some OO features to an otherwise C-like language, and aren't definitive (or even necessarily very good, particularly in the early forms) examples of OO support in a programming language.

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.)


> Well, when writing Haskell I'm not dealing with object instantiation, OO-classes and OO-subclassing.

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.


I upvoted you, but I strongly disagree with that I actually am using those in Haskell (knowing that you did not say that but merely point out what was said).

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.


Let me expound a bit on pjmlp's comment.

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


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).

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).


> In fact, most features you associate with FP were very well known when OOP came about.

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.


> has frequently played a fairly central role (whether or not it was explicitly named as a paradigm) in computer science in education

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 encourage you to read Practical Common Lisp for an introduction to CLOS - the Common Lisp Object System. It's available for free online. (http://www.gigamonkeys.com/book/) If you are convinced that there may be something to object-oriented programming in Common Lisp that might be interesting and novel, I suggest two further advanced readings: Object Oriented Programming in Common Lisp and The Art of the Metaobject Protocol.

I use Clojure professionally, and CLOS is easily the thing I miss most.


Do you have some examples of when you prefer to use an OO style vs traditional FP? This is something I've been contemplating as I learn FP


So you never make use of multimethods or type classes?

Most of those FPisms (LINQ) are already present in Smalltalk.


I too think the OO paradigm, at least as implemented in popular OO languages (mainly through classes and inheritance), has few benefits, though not necessarily none. Certainly the idea of modeling real world objects has failed miserably while still being taught to inexperienced developers who then make everything a descendant of a class like Thing (or some nonsense like that). On the other hand, interfaces (protocols in Clojure) are the one good idea that's implemented in a lot of OO languages, though really that's just part of a type system and not an OO concept.

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.


Oddly enough, I find that good OOP codebases often converge toward an FP-ish approach (little or no inheritance, focus on objects-as-records and "static" services, emphasis on immutability and "result" types).


> but I don't know how dynamic FP languages would help without sacrificing the benefits of OO?

What do you mean, given that Smalltalk, JavaScript, SELF, Ruby, Python, Dart, Dylan, Common Lisp, Julia, StrongTalk ....

Are all dynamic languages with OOP features.


What are the benefits of OO? I normally think of encapsulation, but this isn't that big of a deal in Clojure (or many other functional languages) as you normally can't mutate objects anyway.


Hm, good question :). By the "benefits of OO" I meant a collection of strategies that evolved over time to tackle the difficulty of building complex systems: encapsulation, composition, interfaces, patterns, SOLID principles, etc... I think what I'm after is an OO-FP map of these concepts, or more probably: alternative strategies.


I'll answer these specifically for Clojure since they vary by language.

> 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.


I found this article[0] a pretty enlightening tour of how SOLID principles don't require traditional OO. You might find it useful in transitioning - it explains how each of these concepts maps onto idiomatic Clojure.

[0]http://www.lispcast.com/solid-principles-in-clojure


I see encapsulation as being more or less orthogonal to mutability. The main benefit is helping to preserve a consistent interface that doesn't expose much implementation structure. Functional languages are at least as capable of encapsulation as oo. It's just accomplished through namespaces instead of objects.


> but I don't know how dynamic FP languages would help without sacrificing the benefits of OO?

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?


How did you handle refactorings? Like, changing userAddress's "type" from Address to Maybe Address?

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.


We made heavy use of Primastic's schema when we felt we needed guarantees about the shape of the data, this help in areas where you want the extra checking and documentation.

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.


One way is to use regression unit testing, though Clojure community is not very into testing.


I've noticed the complete opposite. There's less of an emphasis on unit tests, but that's because the nature of FP enables you to need less of them. Instead of needing to check (at least as often) if objects behave correctly, you can write the few tests necessary to give you some regression abilities later on.

I've noticed a huge emphasis on testing the sytem, specifically through means of generative/property-based testing.


That last part is complete rubbish.


Can you elaborate? Most of Clojure libraries I use have extensive, sometimes seemingly too many, tests that accompany them, as if they are covering literally every possible scenario the user could find themselves in.


Did you respond to the wrong comment? He says it's rubbish that the Clojure community is NOT into testing.


I misunderstood sorry!


That's just what I've heard - sorry if it's incorrect.


Sadly, one must investigate original sources, otherwise we get soundbite-knowledge. Here's a blogpost written by a core Clojure contributor that starts: "Occasionally I hear someone say that the Clojure community is against testing." (http://tech.puredanger.com/2013/08/31/clojure-and-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...)


The Clojure community is very big on testing, but not all of it is automated (eg testing on the REPL). Automated testing is popular too though, to the point where Clojure has a number of testing libraries (clojure.test, test.check, test.generative, expectations, speclj, midje).


If the testing isn't automated, I'm not sure it should really count.


Sure it does. It depends on the purpose of your tests and the cost/benefit.

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.


The "Clojurians don't like testing" meme probably has more to do with Rich Hickey's famous "guard rail programming" [1] comment than anything else. Of course, even at the time, the joke within the community was, "Yes, Rich Hickey doesn't need to write tests....you do!"

[1] http://www.infoq.com/presentations/Simple-Made-Easy (15:30)


> The "Clojurians don't like testing" meme probably has more to do with Rich Hickey's famous "guard rail programming" [1] comment than anything else.

And Rich Hickey isn't against testing. He was having a jab at test driven design.


Thanks for the write-up! Juxt seems like a cool bunch - I'm not London-based (Berlin) but would not hesitate one minute given the chance to join them.


Hey, send us a mail to say hi at info@juxt.pro :-)




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: