Live data from Hacker News

Tour of our 250k line Clojure codebase

tech.redplanetlabs.com

71–80 of 237 posts

Re: Tour of our 250k line Clojure codebase

#71
post #56

Earlier quoted context omitted.

IMO the sentence is incorrect. Static type systems would "enforce the stronger constraint" at run time... same as the dynamic type system. Perhaps the dynamic type system can have a fancy linter that does something crazy like running your code... but I'm not aware of any such linter.

> Perhaps the dynamic type system can have a fancy linter that does something crazy like running your code... but I'm not aware of any such linter. Names in Clojure codebases and libraries are pretty reliably annotated with trailing exclamation marks, looking like: `save!`. To that end, running Clojure code blindly to test it and its types is a fairly practical practice, coming up in cases like Ghostwheel [1], which…

>which can be much more sophisticated than what is commonly used in static systems, even with refinement types

Can you elaborate on this? I have some experience with Clojure, and have been relatively unimpressed with spec. Everything it does I can do with (refinement) types (I think). Reading over the Spec documentation it constantly talks about predicates... which is exactly what a refinement type is.

Spec/Clojure has the problem where it validates... but doesn't parse. See: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

Re: Tour of our 250k line Clojure codebase

#72
post #65

Earlier quoted context omitted.

Yes, and this is where statically typed languages shine (in my opinion). I like programming in a style that makes heavy use of the type system to enforce this. For example when writing an api endpoint to create a task I would typically deserialise the json into a CreateTaskRequest. If the object is created without exceptions I can be sure it is valid. CreateTaskRequest implements the ToTask interface. The service lay…

> Lots of classes and interfaces but they are all small and with a single purpose. That's the side effect. You ultimately end up with more code and not less even though it saves you from type checks. There are trade offs for both.

One thing I wish for is some kind of "type tags". Being able to express concepts like List[Widget], List[Widget, Nonempty], List[Widget, Nonempty, Sorted], Vector[User, Sorted], etc. - or even, more generic, [, NonEmpty] (where and are parameters, like in C++ templates) - without implementing an explicit new type for each. Logic verification through typing would then involve not just changing "main" types, but also adding and dropping "tags" from the "set of tags" attached to the "main" type. This should cut down on the amount of boilerplate.

Hell, in the extreme, perhaps types in general could be generalized as a set of tags?

(See also, https://news.ycombinator.com/item?id=27168893)

Re: Tour of our 250k line Clojure codebase

#73
post #65

Earlier quoted context omitted.

Yes, and this is where statically typed languages shine (in my opinion). I like programming in a style that makes heavy use of the type system to enforce this. For example when writing an api endpoint to create a task I would typically deserialise the json into a CreateTaskRequest. If the object is created without exceptions I can be sure it is valid. CreateTaskRequest implements the ToTask interface. The service lay…

> Lots of classes and interfaces but they are all small and with a single purpose. That's the side effect. You ultimately end up with more code and not less even though it saves you from type checks. There are trade offs for both.

> There are trade offs for both

Exactly.

Personally I like that bit of extra code because it gives every class one reason to exist. There are no conflicts so the type system can be used fully without ambiguity.

I’ve always disliked the way early rails promoted fat models that combined serialisation, deserialisation, validation, persistence, querying and business logic in the same class.

Re: Tour of our 250k line Clojure codebase

#74

Earlier quoted context omitted.

Yes, and this is where statically typed languages shine (in my opinion). I like programming in a style that makes heavy use of the type system to enforce this. For example when writing an api endpoint to create a task I would typically deserialise the json into a CreateTaskRequest. If the object is created without exceptions I can be sure it is valid. CreateTaskRequest implements the ToTask interface. The service lay…

You're writing more code than you have to though. More code = more bugs.

Just for clarification: Do you equate type ascriptions to "more code"?

Re: Tour of our 250k line Clojure codebase

#75
post #65

Earlier quoted context omitted.

> Lots of classes and interfaces but they are all small and with a single purpose. That's the side effect. You ultimately end up with more code and not less even though it saves you from type checks. There are trade offs for both.

> There are trade offs for both Exactly. Personally I like that bit of extra code because it gives every class one reason to exist. There are no conflicts so the type system can be used fully without ambiguity. I’ve always disliked the way early rails promoted fat models that combined serialisation, deserialisation, validation, persistence, querying and business logic in the same class.

I personally bounce back and forth about this. My experience is probably colored by the fact that I'm doing this in C++. Boilerplate gets annoying there (and attempts to cut it down tend to produce lots of incomprehensible function templates). I like the idea of using types to encode assertions at a fine granularity. I dislike the amount of tiny little functions this creates. I also dislike that the resulting code is only navigable with an IDE - otherwise you spend 50% of your time chasing definitions of these little types.

Re: Tour of our 250k line Clojure codebase

#77
> One of the coolest parts of our codebase is the new general purpose language at its foundation. Though the semantics of the language are substantially different than Clojure, it’s defined entirely within Clojure using macros to express the differing behavior. It compiles directly to bytecode using the ASM library. The rest of our system is built using both this language and vanilla Clojure, interoperating seamlessly.

Actually this sounds quite horrible.

Re: Tour of our 250k line Clojure codebase

#78

Earlier quoted context omitted.

As with all functional programming languages, if you limit the use to some specific areas you can get a lot done with a few easily understood lines of code.

Quite the opposite. Functional style is especially useful in larger codebases. However, I think a functional strongly typed language is often easier to get right than a weakly typed one. I mostly write F# and Clojure and based on my experience I would go for F# any day over Clojure but at the same time I would also go for Clojure over Java as well. I do not know where your views are coming from, sounds like 2nd hand…

[deleted]

Re: Tour of our 250k line Clojure codebase

#79

Earlier quoted context omitted.

Yes, and this is where statically typed languages shine (in my opinion). I like programming in a style that makes heavy use of the type system to enforce this. For example when writing an api endpoint to create a task I would typically deserialise the json into a CreateTaskRequest. If the object is created without exceptions I can be sure it is valid. CreateTaskRequest implements the ToTask interface. The service lay…

You're writing more code than you have to though. More code = more bugs.

I don’t agree there. Most of these extra classes are just type declarations with no methods at all.

While the total LoC written might be higher, the amount of written logic in which you can introduce bugs is less.

Re: Tour of our 250k line Clojure codebase

#80
post #60

we have a clojure codebase that's about 100k lines. Honestly I'm kinda fed up with it. Certain 3rd party libs we've used have been abandoned. We wrote our own libs for a major framework and it is failing behind. Too many "I'm very clever" functions that are hard to understand and also have subtle bugs.

Would you mind sharing the abandoned libs/framework?

When you say you have too many "I'm clever" functions, do you mean within code your team wrote, or in the ecosystem at large?

Post reply on HN