Live data from Hacker News

Tour of our 250k line Clojure codebase

tech.redplanetlabs.com

81–90 of 237 posts

Re: Tour of our 250k line Clojure codebase

#81
post #64

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.

Most static type systems that can do what clojure.spec can do tend to include runtime assertion and type checks and do not erase type data from runtime (what some static type zealots call "uni-type" approach). For example Ada's type system, which has equivalent of Common Lisp's SATISFIES construct, which implements a runtime type assert that can use all the power of the language.

Sorry, I don't understand

>Most static type systems that can do what clojure.spec can do tend to include runtime assertion and type checks and do not erase type data from runtime (what some static type zealots call "uni-type" approach).

When you don't erase the type data... you're gonna have more than one type. How is this a "uni-type" approach?

Re: Tour of our 250k line Clojure codebase

#82

> Detecting an error when creating a record is much better than when using it later on, as during creation you have the context needed to debug the problem. This is a great insight no matter what language or framework you're operating in. Laziness has its virtues, but invariants, validity checks, run-time type checks, etc. should all be performed as early (and often) as possible - it's much easier to debug issues whe…

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…

> If the object is created without exceptions I can be sure it is valid.

Without some kind of custom validation system in place (JSON schema, property attributes, etc), that doesn't tell you much. With most serialization libraries I've used the default settings would let you deserialize {} into any class without an exception and all the properties would just have the default value for their type. Using stricter setting gets you a little more but definitely no guarantee of a logically valid state. If I want to go even one step past what little validation static typing provides, and I usually do, I'd rather just take the type noise completely out of my data and move all validation to a single place.

Re: Tour of our 250k line Clojure codebase

#83

Earlier quoted context omitted.

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

Ok yes, C++ might not be the greatest language for this.

My experience here is mostly from Kotlin which is a great language for this. Nullability, extension methods, (reified) generics, data classes, delegates, etc can all help reduce boilerplate.

Re: Tour of our 250k line Clojure codebase

#84

> 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 seamlessl…

Imagine starting a job as a closure dev and being told you have to learn a new language with substantially different semantics

Re: Tour of our 250k line Clojure codebase

#85
post #84

> 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 seamlessl…

Imagine starting a job as a closure dev and being told you have to learn a new language with substantially different semantics

Everyone is doing it already, to a large extent. Where "an API" ends and "a programming language" starts is a matter of opinion. It's a fuzzy boundary.

Re: Tour of our 250k line Clojure codebase

#86

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…

As I stated in the beginning I do not work with Clojure. Pure functions is something I use in any programming language. I have a hands on experience from a lot of different functional languages though including F#. From what I understand it is very common to use context based style of coding in Clojure. That is at least what I heard and Google does not seem to disagree...

Re: Tour of our 250k line Clojure codebase

#87

> 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 seamlessl…

I have dabbled in Clojure and Julia, and both of them support macro to make it easier to develop DSL looking syntax. I have always wondered how difficult it it to debug macro heavy code - where's other language cite 'no macro' as a feature such as Zig.

Re: Tour of our 250k line Clojure codebase

#88
post #66
post #11

Earlier quoted context omitted.

It's still in stealth mode. They raised $5M in 2019: https://news.ycombinator.com/item?id=19565267

how exactly is it stealth if they have a blog and announced funding?

I meant that they didn't reveal the product yet.

Re: Tour of our 250k line Clojure codebase

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

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…

A language like Kotlin can do some of these things using delegates, interfaces and extension methods.

For example a MutableList can be dropped down to a List which is not mutable. And a generic conversion from Collection to NonEmptyCollection should be trivial to write as an extension method.

Re: Tour of our 250k line Clojure codebase

#90

> 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 seamlessl…

I have dabbled in Clojure and Julia, and both of them support macro to make it easier to develop DSL looking syntax. I have always wondered how difficult it it to debug macro heavy code - where's other language cite 'no macro' as a feature such as Zig.

I kind of like Rust's version: macros are limited and clunky (and really clunky if you want to do anything super fancy), which means they're available, but I only end up using them when they're really necessary (like I'd be copying and pasting huge amounts of code and there's no other way to avoid doing that)
Post reply on HN