5 ms·
See my comments about Clojure.spec here: https://news.ycombinator.com/item?id=16414942 https://news.ycombinator.com/item?id=16414942 This is all application s
by nogridbag 9y ago
See my comments about Clojure.spec here:
https://news.ycombinator.com/item?id=16414942 https://news.ycombinator.com/item?id=16414942
This is all application specific, but for the types of apps I've worked on (large enterprisey OO apps) you often need various bits and pieces of domain data across different methods. So given some function, you either pass in DomainClass1, DomainClass2, DomainClass 3 (using a couple of properties of each) or you define a new class SomeSubsetOfPropertiesClass solely to call that single method. In the former case, the types do not serve as documentation for the reader as it's not clear what shape of the data is required for the function. In the latter case you're duplicating code (the properties and their types) and the class really has no meaning except as a struct to call that method.
Now that I've been working with Clojure for a little bit I find I'm able to write much more concise, testable functions and calling them is dead simple since I can work with the raw data, transforming it into the shape I need.
- skohan 9y agoTo be fair, the OO example you give just sounds like bad design. It should be entirely possible to avoid having to pass giant bundles of state between different layers of your application, and even if you can't you should be able to define interface conformances on your DomainClass objects to make it clear which of their members are actually relevant in a given case.
- nogridbag 9y agoAbsolutely it's bad OO design. Because it's very difficult to do OO right at scale. For a larger application like many I've worked on (thousands of domain classes), it's much less risky to have an anemic domain model than attempt to model a very complex domain properly. I would imagine in the Java world most applications that use ORMs tend to lean towards anemic domain models that simply mirror DB tables.
- frankpf 9y agoWhat you want to do is possible with a structural type system. In structural type systems, type compatibility is based on the structure of the type instead of the name (as opposed to nominal type systems). An example in TypeScript: interface Named { name: string } class Person { name: string age: number // constructor here } function f(obj: Named) { // do something with obj.name } const joe = new Person('Joe', 25) // Compiles, even though Person has an extra `age` field // Person is structurally compatible with Named f(joe) If you pass f() a class/object that doesn't have a `name` property of type string, the compiler would catch your error.
- vikiomega9 9y agoThis is not easy to do in say Java which is significantly different from clojure (or how typescript enforces typing). In your example it appears that you're using the name field rather than the type Named to shape data that the function will use (this is in essence ducktyping)
- andrewflnr 9y agoRight, but that's nothing to do with types in general, mostly just Java and friends with unimaginative type systems.
- frankpf 9y agoI am using `Named` as an argument. In structural type systems, what matters is the type structure. It's not duck typing because it's checked at compile-time. If I change the type of "Named" to have the fields `firstName` and `lastName` of type string, accessing any other property inside `f` (like `obj.nonExisting`) or passing objects that don't have those fields.
- vikiomega9 9y agothanks! just to clarify, how do you manage the composition of these types? Do you create a structural type per API and potentially have the "same" types repeated multiple times?
- nogridbag 9y agoInteresting, except you're redefining the type of "name" in each each class by specifying "string". Also, Clojure.spec allows you to be much more precise about properties. For example, it must be a certain length, non-nil, or even match a regex (e.g. first letter starts with uppercase).
- frankpf 9y agoWhat do you mean by redefining? I'm not redefining the type of "name". If you want to get compile-time checking that `Person` implements `Named`, you can use `class Person implements Named`. Even better than that, you don't need to use classes, you can use "normal" JS data structures. Extending the last example: const john = { name: 'John', age: 25 } // No class involved f(john) // compiles const alice = { age: 25 firstName: 'Alice', lastName: 'Jones' } f(alice) // compile-time error Clojure.spec is cool, but I don't see how that is incompatible with static typing. You can still have libraries that check more complex properties at runtime. EDIT: > Also, Clojure.spec allows you to be much more precise about properties. For example, it must be [...] non-nil [...] TypeScript also handles nulls in the type system: function f(x: string | null) { if (x != null) { // tsc knows that inside this if, x can't be null return x.length } else { console.log(x.length) // this doesn't compile, x is of type null here } }