3 ms·
> The obvious answer is that while you can't solve everything with types, you can still succinctly represent what would otherwise be a ton of tests with a singl
by catnaroek 10y ago
> The obvious answer is that while you can't solve everything with types, you can still succinctly represent what would otherwise be a ton of tests with a single type name.
Except a name is just a declaration of intentions. A Python class name is just as good a declaration of intentions as a Java class name. But why should I trust an unverified declaration?
> Well, one man's trivial is another man's non-trivial, but I'm happy having a machine automate all my trivial tests.
Finite discriminate unions are pretty trivial by any reasonable standards, and Java can only handle them by very cumbersome means (visitors), which people who value their time reasonably avoid.
> You could make that charge against most of the BCPL languages. C++ gets around it with templates, but even then has all kinds of limitations on how the output type is a function of the input type.
Somehow Rust and Swift manage it. Even C++ and D fare better than Java, since finite discriminated unions can be implemented using templates, although pattern matching using variant_visitors is a truly maddening experience.
> Java has Optional<T> which is exactly the way to express that case.
But `null` still exists, and it's used in several parts of the Java standard library.
> Exceptions are a reasonable way of expressing alternative results, as is Optional<T>, as is returning a common supertype/interface, as is null (much as I despise it), as is having an object that serves as a discriminated union.
(0) No, exceptions aren't reasonable. They don't always show up in types, so you don't know what kinds of exceptions a particular procedure might throw.
(1) Java's `Optional<T>` is hardly more satisfying: how do I simultaneously pattern-match over two or more optionals?
(2) In any case, there exist many more interesting discriminated unions than can be expressed using only optionals and recursive types.
- cbsmith 10y ago> A Python class name is just as good a declaration of intentions as a Java class name. Yes, it is just as good a declaration of intentions. > But why should I trust an unverified declaration? Wait a second though... def foo(bar): if !isinstance(bar, Baz): throw WrongTypeException THAT is somewhat equivalent to having: public void foo(Baz bar); The Java one gets verified without test cases (there's literally steps in the process with "Verifier" in the name), but the Python one does not. Most Python code doesn't have all of its parameters laden with type checks, whereas Java code tends to have type names attached to its parameters. More importantly though, that Python type check doesn't actually get verified until runtime, and only on a case by case basis. The static type checks in Java will be done first at compile time, and then again during bind/load time. You still have to write code to verify that you never get a non-Baz "baz" for the Python function, and that's trickier than it looks. > Finite discriminate unions are pretty trivial by any reasonable standards, and Java can only handle them by very cumbersome means (visitors), which people who value their time reasonably avoid. You value your time, so because of cases where you'd like to return more than one type without a common parent type, you would throw out type checking entirely?... > But `null` still exists, and it's used in several parts of the Java standard library. So, Rust at least still has null as well (they say they don't, but in practice they have std::ptr::null and they also have None... now tell me that doesn't create more problems than it solves ;-), as there's this problem that reality is already filled with nulls. In practice, what you are getting at is exactly what I pointed out earlier: Java is locked in to certain behaviours and interfaces that predate the current somewhat more sophisticated state of the type system. > No, exceptions aren't reasonable. They don't show up in types, so you don't know what kinds of exceptions a particular procedure might throw. They do for checked exceptions: final java.sql.ResultSet foo = query.execute(); foo.getInt(1); That's going to get you an int, a SQLException, a RuntimeException, or an Error depending on circumstance. While the Error & RuntimeException aren't checked by the compiler, the SQLException is, and you've got a fairly natural way of writing logic for it too. That's a very reasonable way of declaring that your function will return either an int or a SQLException. Without any test cases, the compiler will tell me if I'm not handling that SQLException case (much to my chagrin). > Java's `Optional<T>` is hardly more satisfying: how do I simultaneously pattern-match over two or more optionals? Generally, I haven't needed to. Usually the optional comes in a different stream or in a different stage of processing a stream. However, for cases where you really have multiple optionals, you typically either pass them as parameters to lambda or create a named type that contains your Optional<T> types. Honestly, given the complexity of those cases, it isn't that great a burden compared to the logic you're going to bind to it... > In any case, there exist many more interesting discriminated unions than can be expressed using only optionals and recursive types. Might be more interesting yes... but for your typical coding context, not that important.
- catnaroek 10y ago> Most Python code doesn't have all of its parameters laden with type checks, whereas Java code tends to have type names attached to its parameters. Why should I care about the name of the type, though? > More importantly though, that Python type check doesn't actually get verified until runtime, and only on a case by case basis. The static type checks in Java will be done first at compile time, and then again during bind/load time. My point is that Java's static checks don't buy you much, because types aren't very precise about what objects are supposed to be in the first place, as my quote from Oracle's own documentation above shows. I'm all for more static checking, but only when it actually buys you something. Java is effectively a dynamically typed language with an integrated linter. > You still have to write code to verify that you never get a non-Baz "baz" for the Python function, and that's trickier than it looks. Python programmers tend to care more about the methods a concrete object has, rather than the name of its class. Which is IMO pretty sensible - as well as doable with a static type system, see OCaml. > You value your time, so because of cases where you'd like to return more than one type without a common parent type, you would throw out type checking entirely?... No, I use sum types. The problem is that Java doesn't have them! > So, Rust at least still has null as well (they say they don't, but in practice they have std::ptr::null You can't do anything with raw pointers in safe code. Unsafe code is, well, unsafe. > and they also have None... `None` is the right way to handle optional values. It doesn't have type `T`, but rather `Option<T>`. > now tell me that doesn't create more problems than it solves ;-) I tell you. > as there's this problem that reality is already filled with nulls. No, reality doesn't have nulls. Where's null in the laws of physics?
- cbsmith 10y ago> No, I use sum types. So, you are suggesting then that if a static type system doesn't have sum types, it provides no value and you are better off without it, and I'm pointing out that as annoying as that might be, there is still plenty of value without them. > The problem is that Java doesn't have them! Java actually has SumType's for the simple cases of representing two types (Pair<K,V>) or for representing one type or null (Optional<T>). For the rest of the cases, you'd either have to recursively use Optional<> and/or Pair<> or come up with your own type name, but considering you have to name everything else anyway, and you have to handle the various cases involved, it's not that bad to create a static inner class for some new combination of values. It's not elegant, but it really doesn't add much work. If you feel it does, use an attribute processor to make it easier to generate one on your behalf. > It doesn't have type `T`, but rather `Option<T>`. Yes, we're all very proud of the correctness of having a distinct type for null/nil/none/whatever. It's nice, but from a practical standpoint you can achieve the same thing in Java. You just can't fix the existing class library. > No, reality doesn't have nulls. Where's null in the laws of physics? Well, in physics, "no magnetic field" != "no gravitational field", so...