7 ms·
> Modules? Meh. The OSGi thing has always struck me as hideously overengineered And that's _exactly_ why you need modularity built into the ecosystem. OSGi and
by gavinking 13y ago
> Modules? Meh. The OSGi thing has always struck me as hideously overengineered
And that's _exactly_ why you need modularity built into the ecosystem. OSGi and Maven _are_ hideously overengineered, and they're overengineered because there's no common model built into the language, toolset, and platform.
> I've never understood what problem it's supposed to solve.
Management of versioned dependencies between libraries and components developed by different teams. This is absolutely central to what software engineering is all about.
> Nullable types? The trouble with doing this at the language level is that you can't then treat them as plain old objects.
I don't think you've understood what this is. In Ceylon, null is the singleton instance of the perfectly ordinary class Null. A nullable type is just a union type of form Null|X, for any type X.
> 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!
After that, check out signatures of operations like Set.union(), Set.intersection(), coalesce(), compose(), flatten(), curry(), etc, to see a bunch of other things you just can't write in Scala.
> functions and methods? Got them.
Does scala have toplevel functions? I thought they always had to be nested inside some object?
But my real big problem with Scala here is it just doesn't have proper function and tuple types that you can abstract over. Instead you have F, F1, F2, F3 ... F22 and Tuple2, Tuple3, ... Tuple22. That to me is just rubbish.
- barkmadley 13y ago> > 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(): > {Float+} oneOrMore = .... ; > {Float*} zeroOrMore = .... ; > Float maxOfOneOrMore = max(oneOrMore); //never null > Float? maxOfZeroOrMore = max(zeroOrMore); //might be null While this is interesting, is it enforced by the compiler? i.e. is the following a runtime, or compile time error?: Float maxOfOneOrMore = max(zeroOrMore);
- gavinking 13y ago> 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? A compile-time error, of course.
- barkmadley 13y agoI 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/module-doc/max.ceylon.html 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/module-doc/Iterable.type.html 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/module-doc/Nothing.type.html http://modules.ceylon-lang.org/repo/1/ceylon/language/1.0.0/...
- lmm 13y ago> Management of versioned dependencies between libraries and components developed by different teams. This is absolutely central to what software engineering is all about. Let me put it this way: I develop in Java (and Scala) using maven, without ever touching OSGi. What is the problem I'm supposed to be having that OSGi would be solving? Because I've never seen it. > I don't think you've understood what this is. In Ceylon, null is the singleton instance of the perfectly ordinary class Null. A nullable type is just a union type of form Null|X, for any type X. You're right, the short syntax put me off (it's something I worry about in scala too). > Sure. You should see the crazily cool stuff we can do with stuff like the signature of max(). You'll be impressed, I promise! > After that, check out signatures of operations like Set.union(), Set.intersection(), coalesce(), compose(), flatten(), curry(), etc, to see a bunch of other things you just can't write in Scala. I tried to do that, but it looks like you don't have an equivalent to javadoc/scaladoc with the standard library docs publicly available? Either that or google can't find them. (And I'd still like to hear whether you can do something as general as scalaz's sequence. It's a function that takes F[G[A]] to G[F[A]] for any F and G with suitable typeclasses (Traverse and Applicative, IIRC), so the same function works for Set[Future] or List[Validation] or Vector[MyMonad]) - I don't think you can do that without higher kinded types. And it appears as an extension method on the F[G[A]]). > Does scala have toplevel functions? I thought they always had to be nested inside some object? Point, but I find it hard to care. You can stick them in a package object if you really need to emulate one at toplevel. > But my real big problem with Scala here is it just doesn't have proper function and tuple types that you can abstract over. Instead you have F, F1, F2, F3 ... F22 and Tuple2, Tuple3, ... Tuple22. That to me is just rubbish. It's a bit rubbish yeah, I agree. Shapeless 2 makes it painless to treat tuples as HLists (using implicit macros - a concept which admittedly would have utterly horrified me six months ago) which means you can work around it in practice.
- gavinking 13y ago> using maven Brrrrr. > What is the problem I'm supposed to be having that OSGi would be solving? Because I've never seen it. The problem OSGi solves is having two versions of the same module as part of the same assembly. This is quite possible if you're using third-party libraries that in turn depend on another third-party library. But I think you're missing something here. Ceylon's module system supplants both OSGi _and_ Maven. It's not just an alternative to OSGi, it's a whole architecture for modularity. > I tried to do that, but it looks like you don't have an equivalent to javadoc/scaladoc with the standard library docs publicly available? https://modules.ceylon-lang.org/modules/ceylon.language/1.0.0/doc https://modules.ceylon-lang.org/modules/ceylon.language/1.0.... > I don't think you can do that without higher kinded types. Correct, we don't have type constructor polymorphism in the official language, and even if we did, we wouldn't use it for this particular problem. Our equivalent APIs are based on (lazy, non-copying) stream-processing. Now, I do have a working implementation of type constructor polymorphism in a branch of the Ceylon typechecker, but I probably won't merge it because: 1. we don't have a really convincing use for it, and 2. I see it as essentially useless without type constructor argument inference, and Ross and I doubt that a robust and decidable inference algorithm exists for a language with subtyping. > Point, but I find it hard to care. Speaking for myself, I don't like unnecessary distracting complexity, even trivial unnecessary distracting complexity. > Shapeless 2 makes it painless to treat tuples as HLists (using implicit macros - a concept which admittedly would have utterly horrified me six months ago) which means you can work around it in practice Brrrrr. And then you Scala folks tell the rest of us that Scala is not complex and that we're full of FUD or worse ;-) FTR, the way Ceylon models this is the way this is the same way it is modeled in mathematics. That is to say, in mathematics, a function is a set of ordered pairs, where the first element of each ordered pair is a tuple. The notion of a tuple is itself defined by recursion. Therefore, in Ceylon, a function type is Callable<AType, ATupleType>, and Tuple is is a class with a recursive definition. Clean, intuitive, abstractable.
- frowaway001 13y ago> Modules? Meh. The OSGi thing has always struck me as hideously overengineered One of the next versions of Java will ship with a module system. This will likely be good enough and not worth the hassle of having to deal with two competing module systems. > Sure. You should see the crazily cool stuff we can do with stuff like the signature of max(). You'll be impressed, I promise! I have looked it, and maybe I'm not seeing it, but it looks like Ceylon is repeating all the things which have been wrong with Comparable in the first place. Given that Ceylon had a clean sheet to start with, I'm surprised that no better solution has been found. The signature is intimidating and basically combines the non-extensibility of Comparable with the declaration-site boilerplate of Comparable—with none of its benefits. - If your type was written by someone who didn't care to implement Comparable, bad luck! - If the type has a Comparable implementation which differs from what you need, bad luck, too! - You need the type to be comparable in multiple ways? Again, bad luck! If any of this use cases came up, it would mean adding a new method which takes an Comparator to all APIs working with Comparable. Leading to more boilerplate and all the pain usually associated with defining Comparators. Scala has things to complain about, but I think it's hard to claim that they haven't nailed this case down perfectly. Consider: case class Person(firstName: String, lastName: String, age: Int) extends Ordered[Person] { def compare(that: Person): Int = this.lastName compare that.lastName } val persons = Person("Ann", "Miller", 32) :: Person("Bob", "Smith", 17) :: Person("Charlie", "Miller", 71) :: Nil val personsSortedByFirstName = persons.sorted // Person doesn't implement Ordered (Comparable) // or we want to use a different Ordering? val orderByAge = Ordering.by[Person, Int](_.age) val personsSortedByAge = persons.sorted(orderByAge) None of the drawbacks, all of the benefits! Additionally, the signature of the sorted method would be much simpler and more readable in Scala, too: def sorted[T : Ordering] = ... Compare that to: shared Element[] sort<Element>({Element*} elements) given Element satisfies Comparable<Element> => ... But I guess messing with Comparable as an upper bound is the best thing one can do without typeclasses. :-/ > Instead you have F, F1, F2, F3 ... F22 and Tuple2, Tuple3, ... Tuple22. That to me is just rubbish. Looking at what you did with Tuples (basically a linked list), I'm not sure that there is a large difference. Scala's approach seems to be more in line of YAGNI (if you need that 523-Tuple, you should think a bit about your data model) and performance (no pointer chasing, all values are just a method call away). Anyway, if one wanted tuples-as-linked-lists, one could just use Shapeless' HList library, which has a few benefits over Ceylon's tuples, too, as far as I see: val hlist = 1 :: "str" :: 42.31 :: 'sym :: HNil val str: String = hlist(1) // Not possible in Ceylon, afaik