| I assume this is the implementation [1](took me a while to find the type to see how the compiler might interpret it). I read the definition as (ignoring the implementation): return a Value or Absent when given an Iterable<Value,Absent> where Value is Comparable and Absent is Null
Correct me if I am wrong,
but there doesn't appear to be anything linking the nullability of the return value based on the possible emptiness of the input value. How does the compiler check this constraint (which sounds a lot like a dependent type relation)?Upon further reading of the Iterable type[2], I have more questions, does "Nothing" satisfy "Null"? | If not, then it looks to me like passing in a non-empty iterable (Iterable<X,Nothing>) is a type error. | If so then I would imagine that it becomes harder to enforce the constraint,
likely because the return type can still be Null regardless of input restrictions.
Does this use extra type inference on the expressions inside the function to do the enforcement,
for instance using the "exists" operator to perform type set subtraction between {Value|Null} and {Null} on the true branch.
This implies that if I re-write the max function to have a single exit point (`ret = Null; if (exists values.first) { ...; ret = ...; } return ret;`) then I don't get the same guarantees? Also the fact that Null is referenced by the "exists" operator also implies that it is part of the language specification, and not actually definable in any meaningful way as a user type. Digging a bit deeper, I was very disappointed to read this snippet [3]: Nothing is considered to belong to the module ceylon.language. However, it cannot be defined within the language.
If I read correctly, that means that I cannot implement the same behaviour/enforcement with completely user defined types.[1]: http://modules.ceylon-lang.org/repo/1/ceylon/language/1.0.0/... [2]: http://modules.ceylon-lang.org/repo/1/ceylon/language/1.0.0/... [3]: http://modules.ceylon-lang.org/repo/1/ceylon/language/1.0.0/... |
Sure there is. The second type parameter of Iterable encodes that, see the definition of Iterable.
> does "Nothing" satisfy "Null"?
Of course. Nothing is the bottom type, it satisfies every type.
> If so then I would imagine that it becomes harder to enforce the constraint, likely because the return type can still be Null regardless of input restrictions.
I don't know what you mean by this. An Iterable<X,Nothing> cannot return Null from its first attribute. Check the declaration of Iterable.first.
> Does this use extra type inference on the expressions inside the function to do the enforcement, for instance using the "exists" operator to perform type set subtraction between {Value|Null} and {Null} on the true branch.
Approximately correct. But "exists" is not type subtraction, it's intersection with the class Object. Cool, huh?
> This implies that if I re-write the max function to have a single exit point (`ret = Null; if (exists values.first) { ...; ret = ...; } return ret;`) then I don't get the same guarantees?
I don't follow. You can't rewrite the function to do anything unsound because the compiler won't let you. Our type system is sound.
> Also the fact that Null is referenced by the "exists" operator also implies that it is part of the language specification, and not actually definable in any meaningful way as a user type.
That's not correct. Go and check the definition of Null. It's just a regular class, as advertised. There is no special behavior defined in the language spec. Sure, the exists operator is defined by the language spec to mean "is Object", but that's just trivial syntax sugar.
I guess the problem here is that you're used to languages full of weird special cases and ad hoc behavior. Ceylon is not like that. Ceylon's type system is very simple and these fancy constructs are layered over it as pure syntax sugar. It's a very different approach to other languages. But in the end it's much more satisfying. You'll just need a little mental adjustment to get used to it.
> If I read correctly, that means that I cannot implement the same behaviour/enforcement with completely user defined types.
Wha?! Nothing is the bottom type! The empty set. You want to define your own bottom types? That's not possible (nor desirable) in any language I know of. There's just no reason why you would want to write a class with no instances.