You probably could, but there would be lots of extra boiler plate that distract from the central idea.
The OCaml language features that are used that would need to be emulated somehow in Java:
* Sum types (type a = A of int | B of float). Think tagged unions [1].
* Module functors (module NAME(PARAM : MODULE_TYPE) = struct ... end). Think package level functions (you get some of this with dependency injection frameworks, but it's probably no-where near as safe/sane and flexible.
* GADTs (type a = A : int -> int | int term). I'm not sure how you would represent these in java, I'm not fully up to speed on how they work yet.
I'm surprised you mention camlp4 as an advantage over F#. It is being removed from the official distribution due to the problems that it causes[1], to be replaced with extension points[2].
We use camlp4 a bit at Red Lizard Software and we are eagerly looking to move to extension points as soon as they are released.
> > Can I write a method that's polymorphicly nullable?
> Sure. You should see the crazily cool stuff we can do with stuff like the signature of max(). You'll be impressed, I promise!
From the docs:
> A nonempty iterable is an iterable object which always produces at least one value. A nonempty iterabe type is written {String+}. Distingushing nonempty streams of values lets us correctly express the type of functions like max():
> While this is interesting, is it enforced by the compiler?
Of course. That's the whole point. I really think you've missed the nature of Ceylon. Don't imagine, that just because it has a syntax that looks friendly and harmless and familiar, that it doesn't have a really killer type system under the hood.
> i.e. is the following a runtime, or compile time error?
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.
> 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.
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.
> 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.
This made me sad again[1], but then this made me happier[2] (internalFirst implied it is defined in the compiler, finding all of the pieces that make up the definition was a bit of a pain since it is dispersed across so many files), but I think I didn't fully understand union types (and that Nothing was bottom) when I wrote the grand parents logical conclusions.
:t Iterable<X,Nothing>.first ==>
X|Nothing ==>
X (because union with empty is an identity)
therefore inside max, the variable first will have type X when given an Iterable<X,Nothing>.first. I also see how it cannot be re-written as I proposed because there is no guarantee that Absent will be of type Null.
> 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 did check the definition of Null, I was merely pointing out that although it is a user definable type, the language specification does mention its existence here[3] and here[4] which is where I looked to try to find out what the "exists" keyword meant. Specifically:
in the case of an exists condition, a type whose intersection with Null is not exactly Nothing and whose intersection with Object is not exactly Nothing, or
Later on it says "exists x is equivalent to is Object x, and..." but then why reference Null earlier?
> I guess the problem here is that you're used to languages full of weird special cases and ad hoc behavior.
While I thank you for your time answering my mis-understandings of union types, please do not shoe horn me.
My main programming activities are using OCaml and Haskell, both of which have very few ad-hoc language features, if any.
> I was merely pointing out that although it is a user definable type, the language specification does mention its existence
Sure, the language spec _mentions_ Null, in order to define the syntax sugar X? and exists x. But that doesn't mean that Null has any special place _in the type system_.
> My main programming activities are using OCaml and Haskell, both of which have very few ad-hoc language features, if any.
OK, great, sorry for making assumptions. In my defense, I just didn't quite understand why you were having such a hard time believing that Ceylon could do things like this without adhoc special cases.
If I may, the goal seems a bit incorrectly specified for hypermedia. For instance, if github decided to change the number of results they return per page, then your script would not break, but it would have a different meaning.
I would suggest a better goal to be "get the 11th public gist". Although that still isn't perfect since it doesn't mention how the collection would be sorted (I presume by date).
You can implement crons level of timing specificity with a few "every" jobs and some variables.
e.g. (not real syntax)
every minute: set minute = (minute + 1) % 60
when minute = 4 && day = 5 ...
I think the better part of this system is the cascading success semantics that it would provide, where by a next stage doesn't start if the previous stage didn't finish successfully. This has a lot of overlap with Jenkins and other CI servers, but without the java/http overhead and with a (potentially) lighter weight text based syntax for specifying the dependencies between jobs.
I can even envision this sort of system to be used to distribute jobs to slave machines given a small amount of ssh foo.
[1]: https://medium.com/the-thinkmill/making-the-case-for-progres...