5 ms·
Sort of. You can effectively do so in a language like Java, which allows you to inspect the types of objects at runtime. For example if you have a class `Foo`
by theseoafs 12y ago
Sort of. You can effectively do so in a language like Java, which allows you to inspect the types of objects at runtime. For example if you have a class `Foo` with two subclasses `SubA` and `SubB` you could do something like this:
public static void test(Foo f) {
if (f isinstanceof SubA) {
SubA a = (SubA)f;
/* ... */
} else if (f isinstanceof SubB) {
SubB b = (SubB)f;
}
}
This works okay but you have to implement all the machinery yourself, and for a statically typed language Java is notoriously type-unsafe so if you make a mistake with your casts you're basically not going to find out until you get an exception at runtime. And of course there might be other existing subclasses of `Foo` which means that you get no compile-time guarantee that you didn't miss a possible case.
Standard ML approaches subtyping in a very different way that renders this kind of approach impossible. It gives you less freedom in that it eliminates downcasting, but that ultimately results in a safer language. Of course Standard ML has proper sum types with all of their compile-time guarantees so there's little reason for it anyway.
- deleted 12y ago[deleted]
- dllthomas 12y agoThe way I was envisioning it, there needn't be any down-casting at the language level. Particularly if you want to close the sum type (which you need anyway if you want exhaustiveness checks): public static void test(MaybeFoo mf) { switch(mf.type) { case MaybeFoo.NOTHING: /* ... */ break; case MaybeFoo.JUST: Foo f = mf.getFoo(); /* ... */ break; } } Still has exhaustiveness checking and no downcasts. Still can throw if you use the wrong get* under one of those branches, so I'm certainly not saying it's ideal, but I think it legitimately expresses a sum type and can be implemented in most any language with small adjustments.
- bjeanes 12y agoI'd never thought about this before in the context of Java. IIRC, Java can not have type-parameterized enums (i.e. generic enums), so a Maybe<T> isn't possible. I hope I'm wrong — it's been a while since I did much Java.
- dllthomas 12y agoWell, you certainly can by expanding the parameterization manually like I did above. I think you're otherwise correct, though - I don't think Java will presently do it for you.
- tel 12y agoThis works fine, it just suffers from boolean blindness.
- dllthomas 12y agoCould you elaborate? I think I understand "boolean blindness" but I'm not sure how it applies here. MaybeFoo.type is deliberately an enum. (I could totally be missing/misunderstanding something, though...)
- tel 12y agoChecking whether mf.type is NOTHING or JUST is your boolean. It's linked by providence to whether or not getFoo() is a runtime exception. With a true ADT getFoo would not be accessible unless mf.type == JUST, but in this example it's up to the programmer to enforce that providence. So that's the blindness. The pre/post conditions around how mf.type relates to its surroundings cannot be expressed first-class in the code. You are required to do risky things like call potentially partial code.
- dllthomas 12y agoSo it's specifically the separation of the check and the action? I totally agree this is undesirable; I understood "boolean blindness" to be closer to "bare primitive types lack meaning", specialized to Bool.
- tel 12y agoI always think of it as your data types being too unrestricted so as to allow for invalid states. The eliminator for Maybe (a -> r) -> r -> Maybe a -> r means you only access the `a` if it's there and the access/matching are inextricably linked. So, I think "bare primitive types lack meaning" is basically correct given that you interpret "meaning" as "meaningful uses/actions".
- dllthomas 12y ago
- theseoafs 12y agoThis is sort of a different way of saying the same thing: this is effectively no different from the standard tagged-union approach, with the exact same danger of calling the wrong `get` function and triggering a runtime error that you have in C et al. Don't get me wrong, it's certainly a valid representation of a sum type, but when you hear fans of Haskell and ML talking about how useful sum types are, it's generally because the compiler provides some support to ensure that they're safe and usable.
- dllthomas 12y agoI think we mostly agree. I would note that when I'm dealing with tagged unions in C, as soon as the operation gets at all complicated (more than maybe 3 lines?) I try to pass it off to another function operating on the internal value which restores safety outside the boilerplate. I agree that support for a true tagged union is ideal, though. (Actually, this might make a good attribute in GCC...)