Ceylon Web Runner: Reddit -- ceylon_might_just_be_the_only_language_that_got
class A(shared B? b = null){
string = if (exists b) then "(A of ``b``)" else "A";
shared A? foo(B b) => if (exists bb=this.b, bb == b)
then A(B("B of ``string``"))
else null;
}
class B(String s = "B"){ string = s; }
class C(){ string = "C"; }
class D(shared B b, shared C c){
string = "D of ``b`` and ``c``";
}
D? bar(A a, C c) => if (exists b=a.b) then D(b, c) else null;
"""(In reply to: https://www.reddit.com/r/programming/comments/4aig00/ceylon_might_just_be_the_only_language_that_got/d1mz1it)"""
shared void third() {
third1();
third2();
}
void third1() {
print("First, I want to point out that Ceylon was not designed to supersede Scala or any other functional style language.
At it's root is the concept of type safe object oriented programming with generic polymorphism and union and
intersection types.
So it has come from totally different origins and has different outlook on what kind of problems it is trying to
solve than Scala for example.
Secondly - language support for union and intersection types does not mean that you can not use monadic design
patterns in your code and libraries. It's just that those patterns are not first class citizens in Ceylon, so
there is no syntactic support in the language like in Haskell or Scala.
Now the this is out of the way, let's see, how would I achieve the equivalent patterns in Ceylon.
");
print("For your first example, this is simple in Ceylon:");
print("
A? maybeA;
B? maybeB;
C? maybeC;
value maybeD = if (exists a=maybeA, exists b=maybeB, exists c=maybeC, exists aa = a.foo(b)) then bar(aa, c) else null;
");
A? maybeA = A(B());
B? maybeB = B();
C? maybeC = C();
value maybeD = if (exists a=maybeA, exists b=maybeB, exists c=maybeC, exists aa = a.foo(b)) then bar(aa, c) else null;
print("> maybeD is ``maybeD else "null" ``
");
print("This works because of flow typing asserts that within then block neither of the maybeXs are null and can therefore be safely dereferenced.
Nice and I would say just as clean as your example from Scala.
It would be even nicer with shorter names from getgo:
value d = if (exists a, exists b, exists c, exists aa = a.foo(b)) then bar(aa, c) else null;
");
A? a = A(B());
B? b = B();
C? c = C();
value maybeD2 = if (exists a, exists b, exists c, exists aa = a.foo(b)) then bar(aa, c) else null;
print("> maybeD is `` maybeD2 else "null" ``
");
}
Integer? f1(Integer integer) => if (integer >= 0) then integer * 1 else null;
Integer? f2(Integer integer) => if (integer >= 0) then integer * 2 else null;
Integer? f3(Integer integer) => if (integer >= 0) then integer * 3 else null;
Integer? f4(Integer integer) => if (integer >= 0) then integer * 4 else null;
Integer? f5(Integer integer) => if (integer >= 0) then integer * 5 else null;
void third2() {
print("As for your second example, there are some contradictions in your two code blocks.
First one applies num to functions `f1` through `f5` and only returns the result of `f5` or `null`.
Your second (Scala?) sample applies number to `f1` and the result of this to `f2` and so on until
calling `f5` with a result from `f4`. Finally it returns result of `f5` or `-1`.
So I am going to try both in Ceylon:
First one is really quite easy with function deconstruction (I could have ignored intermediate results,
but I did not, just for sake of functional parity):
Integer? number = parseInteger(\"3\");
value [n1, n2, n3, n4, n5] = if (exists number)
then [f1(number), f2(number), f3(number), f4(number), f5(number)]
else [null, null, null, null, null];
");
Integer? number = parseInteger("3");
value [n1, n2, n3, n4, n5] = if (exists number)
then [f1(number), f2(number), f3(number), f4(number), f5(number)]
else [null, null, null, null, null];
print("> n5 else -1 == `` n5 else -1 ``
");
print("The other one seemed initially a bit more convoluted, but I managed to find a pattern that I think turned out pretty nice.
value n = if (exists parseInteger(\"foo\"),
exists n1 = f1(number),
exists n2 = f2(n1),
exists n3 = f3(n2),
exists n4 = f4(n3),
exists n5 = f5(n4))
then n5
else -1;
");
value n = if (exists n0 = parseInteger("foo"),
exists n1 = f1(n0),
exists n2 = f2(n1),
exists n3 = f3(n2),
exists n4 = f4(n3),
exists n5 = f5(n4))
then n5
else -1;
print("> ``n``
");
}
"""(In reply to: https://www.reddit.com/r/programming/comments/4aig00/ceylon_might_just_be_the_only_language_that_got/d1lrjxu)"""
shared void second() {
print("if (exists maybeInt) then maybeInt + 5 else null == `` maybePlusFive(parseInteger("foo")) else "null" ``");
print("maybeInt?.plus(5) == `` maybeIntElseZeroPlusFive(parseInteger("foo")) ``");
print("(maybeInt else 0) + 5 == `` plusFiveElse5(parseInteger("foo")) ``");
print("maybeInt?.plus(5) else 5 == `` plusFiveElse5(parseInteger("foo")) ``");
print("");
print("""if (exists maybeInt) {
print("Int plus 5 is `` maybeInt + 5 ``" );
}""");
maybePrintPlus5(parseInteger("0"));
}
"""You use different tools for different concepts.
I find that in Ceylon I never really miss monadic patterns, because I have other tools to deal with this type of code:
"""
shared Integer? maybePlusFive(Integer? maybeInt = 5)
=> if (exists maybeInt) then maybeInt + 5 else null;
shared Integer? maybePlusFiveShorter(Integer? maybeInt = 5)
=> maybeInt?.plus(5);
"""but really, I often want to get rid of nulls as early as possible, so more likely, my code would look more like this:"""
shared Integer maybeIntElseZeroPlusFive(Integer? maybeInt = 5)
=> (maybeInt else 0) + 5;
"""or even like that:"""
shared Integer plusFiveElse5(Integer? maybeInt = 5)
=> maybeInt?.plus(5) else 5;
"Or, when I need to perform something based on non-nullness of a value, I would do this:"
shared void maybePrintPlus5(Integer? maybeInt = 5) {
if (exists maybeInt) {
print("> Int plus 5 is `` maybeInt + 5 ``" );
}
}
module web_ide_script "1.0.0" {
// Add module imports here
}
shared void run() {
// first();
// second();
third();
}
"""(In reply to https://www.reddit.com/r/programming/comments/4aig00/ceylon_might_just_be_the_only_language_that_got/d124w17)
In Ceylon you can do functional style stuff quite easily:
"""
shared void first() {
{String?*} strings = {"a", "0", "b", "1", "c", null, "15"};
{Integer?*} maybeIntegers = {for (string in strings) if (exists string) parseInteger(string) };
{Integer*} integersPlus5 = maybeIntegers.coalesced.map( Integer.plus(5) );
printAll(integersPlus5, ", ");
}