Could there exist rough acceptance (if not outright enthusiasm) that a
potentially worthwhile goal (if there are no trade-offs or costs) is for
Scala to gain the language marketshare so that we are more often offered
employment coding in Scala instead of requirements for Java or C# or other
mainstream language which we like less than Scala? And to gain
economy-of-scale for pushing for changes to the JVM or rolling our own VM
in the future.
On Wed, Sep 11, 2013 at 5:32 PM, Shelby Moore <she...@coolpage.com> wrote:Could there exist rough acceptance (if not outright enthusiasm) that a
potentially worthwhile goal (if there are no trade-offs or costs) is for
Scala to gain the language marketshare so that we are more often offered
employment coding in Scala instead of requirements for Java or C# or other
mainstream language which we like less than Scala? And to gain
economy-of-scale for pushing for changes to the JVM or rolling our own VM
in the future.Well it seems to be an admirable goal, but I'm surprised you seem to be focused so strongly on the syntax itself.In my experience and from reading stack overflow and other things, I think more programmers are hindered by either:a) implementation issues (e.g. the proper order to initialize fields in class, what it means to map over a set,
performance surprises due to boxing, etc, etc, etc) or,
b) concept issues, e.g. monads, etc or,
c) tooling support to some extent
So I don't think the syntax itself is reducing Scala adoption --
and on the contrary I think it is one of the main *attractions* to many developers, who are enthusiastic about the much cleaner-than-Java look and reduction in boiler plate.
Granted, there are always corner cases like type-lambdas and such that are pretty ugly, but you may go years as a professional scala developer without writing one of those.
So I don't think the syntax itself is reducing Scala adoption -- and on the contrary I think it is one of the main *attractions* to many developers, who are enthusiastic about the much cleaner-than-Java look and reduction in boiler plate. Granted, there are always corner cases like type-lambdas and such that are pretty ugly, but you may go years as a professional scala developer without writing one of those.
Here are few (not a comprehensive list or) highlights of features that add
capabilities to Scala, just to see if I can get any feedback or interest.
1. Named tuples in Copute are concise
2. First-class disjunctions. Copute allows you to write them, but since
Scala doesn't type-check them, they get subsumed to Any for now. It is
possible I might try to find a way to emulate them in current Scala, but
appears to be a can-of-worms with corner cases.
3. Traits are called INTERFACE (and there is a separate MIXIN syntax),
because they do not contain non-static implementation. This restraint and
optimization is allowed because Copute is supporting pure functional
(immutable, referentially transparent) programming only.
4. A concise syntax to write higher-kinded types for one special case.
Just use the keyword "Sub" any where in the interface, mixin, or class,
and this is converted to Scala e.g. as follows for Applicative:
trait Applicative[+Sub[A] <: Applicative[Sub,A], +A] { ...
Some initial thoughts, having chewed on this for a few hours:
On Wed, Sep 11, 2013 at 8:47 PM, Shelby Moore <she...@coolpage.com> wrote:
1. Named tuples in Copute are conciseInteresting, although I'm not seeing the motivation offhand. What's the use case? This isn't something I'd felt the lack of.
2. First-class disjunctions. Copute allows you to write them, but since
Scala doesn't type-check them, they get subsumed to Any for now. It is
possible I might try to find a way to emulate them in current Scala, but
appears to be a can-of-worms with corner cases.This one makes me nervous, and leads to a lot of questions.How does Copute deal with type-checking of the disjunctions? How do I assign to them; more importantly, are *uses* of the disjunctions fully type-checked? I'd consider them entirely off-limits if uses aren't fully checked -- indeed, I'd probably have to insist that all uses have a compiler warning if they aren't.
This is part of why I'm wondering about how this works with Scala, BTW. If Copute is intended to be used just on its own, and does enough internal type-checking, I might be willing to countenance these; if folks are going to be using Copute side-by-side with Scala, I'd probably say that this is a show-stopper, that would probably prevent me from using the language for any serious work.
Even if working with Scala isn't the plan, it's still the JVM, so people *will* try to use it with other languages, and the type signatures are going to leak. So I'd encourage you not to turn these into Any (which is a gigantic red flag for me), but instead into some sort of wrapper that can enforce at least *some* good behaviour.
For example, a disjoint type could become a wrapper around a hidden Any, and at least enforced at runtime that (a) only the expected types can be assigned to it, and (b) it could only be used with a PartialFunction, with some assertions to check that the function isDefinedAt all of the types contained in the wrapper.
(Which, now that I think on it, could probably be implemented in Scala today with appropriate use of macros. I wonder if anybody's already built that?)3. Traits are called INTERFACE (and there is a separate MIXIN syntax),
because they do not contain non-static implementation. This restraint and
optimization is allowed because Copute is supporting pure functional
(immutable, referentially transparent) programming only.I've been staring at this for hours now, and I'm not getting it. Why is this a good thing? I mean, the power of traits is one of the things I love about Scala, specifically because years of working with Java led me to *despise* pure interfaces. They sound good, sure, but in practice I've had too many times that I've built an interface that seemed like it was pure, only to gradually find that there were several methods I wanted that really, deeply, belonged on the interface. It always led to annoying duplicate code in the implementations.So I guess the question is -- why would I ever want an INTERFACE instead of a MIXIN? I *hate* interfaces with a burning passion, mostly due to Java experience.
Which reminds me: the all-caps thing just plain bugs me. This isn't anything rational -- it's just many years of Internetting, teaching me that all-caps means YELLING. So on a purely aesthetic level, I look at this code and it feels like it's shouting at me, which gives me an instinctive negative gut reaction.
Minor detail, but since your stated objective is easy adoption it may be worth thinking about -- I literally have a very slight "Ow: take it away" reaction to your examples.
4. A concise syntax to write higher-kinded types for one special case.
Just use the keyword "Sub" any where in the interface, mixin, or class,
and this is converted to Scala e.g. as follows for Applicative:
trait Applicative[+Sub[A] <: Applicative[Sub,A], +A] { ...So I have to point out, since we've talked a lot about things that people find head-scratchingly confusing, that this *totally* falls into that category for me. I haven't yet managed to mentally untangle your example. I suspect that it would come with time, but my initial reaction is "mysterious gobbledygook".None of which is to say that it's bad, but to make the point that this sort of head-scratching is *very* much in the eye of the beholder, and that deep power usually winds up causing some...
In 1975 I started using “structured programming” techniques in assembly language, and became a true believer.
In 1983 a new era dawned for me as I started doing some C programming on Unix and MS-DOS. For the next five years, I would be programming mixed C/assembly systems running on a variety of platforms including microcoded bit-slice graphics processors, PCs, 68K systems, and mainframes. For the five years after that, I programmed almost exclusively in C on Unix, MS-DOS, and Windows.
Another new era began in 1994 when I started doing object-oriented programming in C++ on Windows. I fell in love with OO, but C++ I wasn’t so sure about. Five years later I came across the Eiffel language, and my feelings for C++ quickly spiraled toward “contempt.”
The following year, 2000, I made the switch to Java and I’ve been working in Java ever since.
The one that follows is the one that preceded... ML lisp and Smalltalk all predate 1975.
Everything to *really* be an object (as in Smalltalk). None of this rubbish with primitives or pseudo-types like"void".
Declarative
A strong type system
Meta programming
The VM and garbage collection can stay, though I'd like to see stack allocation available too.
--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
On 09/11/2013 11:32 PM, Shelby Moore wrote:
> [...]
I don't think the choice of the language is important for mainstream
programmers, where I count enterprise programmers as mainstream
programmers. Such developers tend to use frameworks the whole day
because they don't spend the whole day writing code, thus they are not
skilled enough _as programmers_ to code large systems completely by
themselves.
Instead, they spend there time convincing customers to buy there
products, supporting them with there problems, and creating (in my
opinion) boring frontends for there system while wasting there time
trying to understand the overly complex frameworks the have to use.
There is no choose of a language, there is just the employer or the
customer that tells which ecosystem, which framework or which library to
use and how the product should look like. Languages are chosen because
they have a ecosystem that fulfills needs.
Scala is chosen by developers thinking that they can do their job with
it in the best possible way (and probably more as for other languages
because of its insanely awesomeness). And as for all what I have seen in
the last years, the majority of Scala programmers aren't utterly
mainstream programmers.
Thus, while again I can only speak for myself (but don't think I will),
I can only convince you to please reduce the amount of posts throwing to
the lists as you did lastly - they are just annoying and don't
contribute to Scalas further development in an amount you may think.
The
majority of Scala developers don't share your problems understanding its
syntax or your way on how to solve problems or with the colleagues you
are working together.
Feel free to open another list for Copute after you brought your points
to an end and I wish you the best to find some contributors. But don't
expect there will be many.
Scala is chosen because it enjoyable to write code in that language but
mainstream programmers will not use Scala because the language is cool -
they will use it because they are told so.
BTW, is this the repo for Copute: https://code.google.com/p/copute/
There is no code there. Perhaps you can put the code at Github or somewhere to start with. As with any open source project you have to expect about 90% will just use it, 9% will give feedback and 1% will contribute. So don't expect a following from the on set.
Ideal way to start would be to have a source to source compiler to a more established language, perhaps scala where the output code is clean and human readable also. Also perhaps retaining comments in the Copute source for more comprehension and some additional comments on the potential transforms if not readily clear. Perhaps this would give you more feedback from the user base of the other language also.
Of course, we went on to build Stack Overflow in Microsoft .NET. That's a big reason it's still as fast as it is. So one of the most frequently asked questions after we announced Discourse was:
Why didn't you build Discourse in .NET, too?
Like any pragmatic programmer, I pick the appropriate tool for the job at hand. And as much as I may love .NET, it would be an extraordinarily poor choice for an 100% open source project like Discourse...
...Nobody accepts your patch to a core .NET class library no matter how hard you try. It always feels like you're swimming upstream...
...As I wrote five years ago:
I'm a pragmatist. For now, I choose to live in the Microsoft universe. But that doesn't mean I'm ignorant of how the other half lives. There's always more than one way to do it, and just because I chose one particular way doesn't make it the right way – or even a particularly good way. Choosing to be provincial and insular is a sure-fire path to ignorance. Learn how the other half lives. Get to know some developers who don't live in the exact same world you do. Find out what tools they're using, and why...
...However, I'd also be lying if I didn't mention that I truly believe the sort of project we are building in Discourse does represent most future software. If you squint your eyes a little, I think you can see a future not too far in the distance where .NET is a specialized niche outside the mainstream.
But why Ruby? Well, the short and not very glamorous answer is that I had narrowed it down to either Python or Ruby, and my original co-founder Robin Ward has been building major Rails apps since 2006. So that clinched it.
I've always been a little intrigued by Ruby, mostly because of the absolutely gushing praise Steve Yegge had for the language way back in 2006. I've never forgotten this...
...And he somehow made it all work together so well that you don't even notice that it has all that stuff. I learned Ruby faster than any other language, out of maybe 30 or 40 total; it took me about 3 days before I was more comfortable using Ruby than I was in Perl, after eight years of Perl hacking. It's so consistent that you start being able to guess how things will work, and you're right most of the time. It's beautiful. And fun. And practical...
...And of course the Ruby community is, and always has been, amazing. We never want for great open source gems and great open source contributors...
...Even if done in good will and for the best interests of the project, it's still a little scary to totally change your programming stripes overnight after two decades. I've always believed that great programmers learn to love more than one language and programming environment...
Presented for your amusement: Three stereotypical hackers from three different countries, described relative to the American baseline.
The German: Methodical, good at details, prone to over-engineering things, careful about tests. Territorial: as a project lead, can get mightily offended if you propose to mess with his orderly orderliness. Good at planned architecture too, but doesn’t deal with novelty well and is easily disoriented by rapidly changing requirements. Rude when cornered. Often wants to run things; just as often it’s unwise to let him.
Even if working with Scala isn't the plan, it's still the JVM, so people *will* try to use it with other languages, and the type signatures are going to leak. So I'd encourage you not to turn these into Any (which is a gigantic red flag for me), but instead into some sort of wrapper that can enforce at least *some* good behaviour.How is subsumption to Any not a reasonable behavior? You are forced to specialize the type with match-case before you can do anything with it besides pass it along as an Any.
Being too loud mouth may not help you. Some rebuff may be needed in some cases. A more gentler approach can help.
> Avoid closures. Ditching Specs2 for my little JUnit wrapper meant that the> main test class for one of our projects (~600-700 lines) no longer took three> minutes to compile or produced 6MB of .class files. It did this by not capturing> everything as closures. At some point, we stopped seeing lambdas as free and> started seeing them as syntactic sugar on top of anonymous classes and thus> acquired the same distaste for them as we did anonymous classes.That is good to know that closures have a cost. I had been thinking it is better to encourage explicit function parameters.
Scala loses a lot of benefits in this world, because features like closures have overhead on the JVM. Hopefully when Java adopts closures, this overhead can be alleviated, but it is there right now.
--
On Friday, September 13, 2013 4:46:34 AM UTC+8, Justin du Coeur wrote:Interesting, although I'm not seeing the motivation offhand. What's the use case? This isn't something I'd felt the lack of.For example, supporting naming for extractors:
Also, some programmers prefers names instead of field index to make code more self-documenting.
They are typed-checked as subsumed to Any, which is the same as if you had written Any.
This is part of why I'm wondering about how this works with Scala, BTW. If Copute is intended to be used just on its own, and does enough internal type-checking, I might be willing to countenance these; if folks are going to be using Copute side-by-side with Scala, I'd probably say that this is a show-stopper, that would probably prevent me from using the language for any serious work.How would the subsumption to Any stop you from using this with Scala? Scala is subsuming to Any also.
Even if working with Scala isn't the plan, it's still the JVM, so people *will* try to use it with other languages, and the type signatures are going to leak. So I'd encourage you not to turn these into Any (which is a gigantic red flag for me), but instead into some sort of wrapper that can enforce at least *some* good behaviour.How is subsumption to Any not a reasonable behavior? You are forced to specialize the type with match-case before you can do anything with it besides pass it along as an Any.
For example, a disjoint type could become a wrapper around a hidden Any, and at least enforced at runtime that (a) only the expected types can be assigned to it, and (b) it could only be used with a PartialFunction, with some assertions to check that the function isDefinedAt all of the types contained in the wrapper.Runtime typing would be much worse guarantee, opening holes.
The point is you can write the types now, but they aren't checked and then later if ever they are checked, you can expect to find incongruences in your code that you did not know existed. This is no worse than Scala you write now where there may be incongruences that you are not aware of, because all are subsumed to Any.
I think what you hate about Java is single-inheritance (lack of multiple inheritance and mixins). Thus, I think you are conflating this with the benefits on an interface that contains no implementation.
The benefit of an interface is that implementation (concretion or instance construction) is orthogonal to type injection (i.e. abstraction).
Which reminds me: the all-caps thing just plain bugs me. This isn't anything rational -- it's just many years of Internetting, teaching me that all-caps means YELLING. So on a purely aesthetic level, I look at this code and it feels like it's shouting at me, which gives me an instinctive negative gut reaction.The all-caps is much less annoying in the Eclipse IDE with its monospace font and purple syntax highlighting on the keywords. I agree it looks horrendous as displayed in this variable-width font used at google groups. I don't know what font you are viewing with in your email client.
The point is that when the code is displayed in a monospace font without syntax highlighting (e.g. in HTML, PDF, Word documents, etc) then the viewer can detect the keywords more rapidly.
For those who hate it, I will definitely try to get a feature into the IDE (and hopefully Github too) to toggle the keywords to lowercase. And the all-caps is unnecessary when the keywords are colored.
I am also open to abandoning the all-caps keywords entirely if I get feedback from all sectors asking me to do so.
I had the same "ouch" reaction when I saw in this variable-width font display at the google groups' website UI. Caused me to doubt my original decision. But again, I think most code will be displayed in a monospace font, either syntax highlighted or not. It is the latter "not" case that I am trying to address, e.g. when one quickly types some code in a non-IDE text editor.
Indeed, now you understand what the person trying to learn Scala feels when they see all these constructs they don't understand and there is no quick reference card where they can quickly assimilate.
Copute hides that from you. You will only see it if you are looking at the Scala code Copute generates. That is a higher-kinded type. It says that the Sub(type) must be a type of the interface it implements, and the Sub(type) is known to the interface that the Sub(type) implements.That is absolutely necessary if you want to unifying subtyping and typeclasses, as I've shown.
You'd need a scheme by which values of these types were at least boxed in `class Union[T](value: Any, tpe; TypeTag[T])` and where pattern matching could use those reified types at runtime to pick the right case.
Also by simplifying the typeclass as shown in my prior message, which I need to properly do a standard library based on category theory (functor, applicative, monad), which will be very very simple to understand and read (unlike Scalaz) and use.
If everything is going to be a object with a meaningful type, then how does it make any sense that Scala chose not to implement first-class disjunctions? That is really perplexing me how Scala obtained such a large hole
Regarding meta programming, I haven't had time to research the proposals for Scala macros. What ever they do, I hope there is an option to see the generated Scala code. Shouldn't macros just be DSLs?
Shouldn't Copute be a DSL?
Here follows an example of myself recently trying to convince some expert C++ programmers to learn and use Scala for an upstart project that I was interested to contribute programming to:
(discussion continues to the next page of that thread)What you will find is that if most programmers don't know Scala, it is impossible to convince anyone to use Scala for a project.
I have one more retort to my German antagonist, from Eric S. Raymond the self-claimed 150 - 170 IQ genius who wrote The Cathedral and The Bazaar and The Art of Unix Programming.
But I can't:def f(xs: List[Union]) = ...f(List("test"))Thus it isn't really first-class. We need the support in the compiler.
On Thu, Sep 12, 2013 at 5:55 PM, Shelby <she...@coolpage.com> wrote:
[Disjunctions]They are typed-checked as subsumed to Any, which is the same as if you had written Any.Well, yes -- but I generally don't allow Any (or even AnyRef) in my code. I usually consider appearances of AnyRef to be design bugs,
Given that, Any and AnyRef *must* be considered pretty evil in application code, used only in very limited circumstances and with a lot of thought and design around them.
I think what you hate about Java is single-inheritance (lack of multiple inheritance and mixins). Thus, I think you are conflating this with the benefits on an interface that contains no implementation.
It's possible that you mean something very different by "interface" and "mixin" than I do, but I don't think you're listening to what I'm saying.Take an Interface with properties A, B and C. Given those, it is *extremely* common for me to want to define functions D and E, which are entirely functions *over* A, B and C, and are essentially universal to any implementation of the interface. The best place to put those functions, in my experience, is typically in the same trait.Yes, it is *possible* to put the function definitions into a mixin -- but from a factoring POV that's generally inappropriate, and tends to lead to boilerplate, at least in the sense of "if you include this interface you always include this mixin". These functions are defined in terms of these properties; from a code-cohesion POV, they are generally best defined *with* the properties. And I rarely know in advance that this will be the case, until I'm well into development -- it's usually the result of refactoring.
There might be benefits, yes, and I'm open to that argument. But keep in mind that, to me, the price looks *extremely* high, and I have to wonder if it's worth it.
The benefit of an interface is that implementation (concretion or instance construction) is orthogonal to type injection (i.e. abstraction).Dude -- I've programmed most of the OO languages ever written. I've been hearing this argument since the invention of Java.
I think you're extremely focused on high-level type theory, which is lovely. I'm not: I mainly care about day-to-day down-in-the-details application engineering, and very little matters as much to me as good factoring of the application code. In practice, I have *never* found myself wishing that I was using interfaces instead of traits, so I need more convincing about why and when you'd want to use them.
Which reminds me: the all-caps thing just plain bugs me. This isn't anything rational -- it's just many years of Internetting, teaching me that all-caps means YELLING. So on a purely aesthetic level, I look at this code and it feels like it's shouting at me, which gives me an instinctive negative gut reaction.The all-caps is much less annoying in the Eclipse IDE with its monospace font and purple syntax highlighting on the keywords. I agree it looks horrendous as displayed in this variable-width font used at google groups. I don't know what font you are viewing with in your email client.Granted, this sort of thing is in the eye of the beholder. But I have to point out that the purple highlighting is something you can turn off if you dislike it, whereas the all-caps is inherent in the language. (Or is it?? See below.)
The point is that when the code is displayed in a monospace font without syntax highlighting (e.g. in HTML, PDF, Word documents, etc) then the viewer can detect the keywords more rapidly.
For those who hate it, I will definitely try to get a feature into the IDE (and hopefully Github too) to toggle the keywords to lowercase. And the all-caps is unnecessary when the keywords are colored.Okay, now I'm confused: does the case of the keywords matter? Your examples had led me to assume that the all-caps was required. If they are case-insensitive I might have other concerns (mostly that they consume more namespace), but this is a more minor issue.
I am also open to abandoning the all-caps keywords entirely if I get feedback from all sectors asking me to do so.
Fair enough. Consider this a vote against.
I had the same "ouch" reaction when I saw in this variable-width font display at the google groups' website UI. Caused me to doubt my original decision. But again, I think most code will be displayed in a monospace font, either syntax highlighted or not. It is the latter "not" case that I am trying to address, e.g. when one quickly types some code in a non-IDE text editor.Honestly, I think it's too high a price for an edge case. The looks aside, I find all-caps a pain in the ass to type -- either I have to bring in my capslock key (which I am not used to when coding, and isn't built into my touch-typing) or I have to hold down the Shift. Either way, it slows down my typing noticeably, and would likely annoy me a lot in practice.
Indeed, now you understand what the person trying to learn Scala feels when they see all these constructs they don't understand and there is no quick reference card where they can quickly assimilate.
Copute hides that from you. You will only see it if you are looking at the Scala code Copute generates. That is a higher-kinded type. It says that the Sub(type) must be a type of the interface it implements, and the Sub(type) is known to the interface that the Sub(type) implements.That is absolutely necessary if you want to unifying subtyping and typeclasses, as I've shown.Hmm. That may well be fair and appropriate. I will say that, as a down-in-the-trenches engineer, spending 95% of my time writing application code and only 5% writing high-level libraries, it comes across as fairly ethereal and hard to relate to. (Then again, I haven't found scalaz easy going either.)
That's relevant only from an audience POV. You've talked a lot about trying to relate to ordinary engineers coming in from Java. Most of them aren't going to have the slightest *clue* what you're talking about here, and the subject is likely to intimidate the bejeezus out of them.
If you're going to be successful, you need to think about who you're talking to. This construct sounds much more interesting to high-level library programmers than to day-to-day engineers who know nothing of typeclasses and don't particularly want to. It's an intriguing idea (although I'm not well-qualified to evaluate its merits), but I suspect will tend to drive away much of your target audience if you oversell it...
Hmm. But can I?def sum(xs: List[Union]) = xs.head match {case n: Int => n
case s: String => s.length()
case b: Boolean => if (b) 1 else 0
}
If not, it doesn't mean I couldn't fiddle with Shapeless to get it to support the type category theory library I've been coding. I will need to study the Shapeless implementation in more detail.It would be great if this would allow me to support disjunctions seamlessly without a change to the Scala compiler.
On Friday, September 13, 2013 10:17:49 PM UTC+8, Justin du Coeur wrote:Well, yes -- but I generally don't allow Any (or even AnyRef) in my code. I usually consider appearances of AnyRef to be design bugs,Well yes I consider then uni-typed typing holes too. But the point you haven't addressed is how do you write heterogeneous collections without using Any? I really have to see the magic you know that no one else seems to know?
There is no extra boilerplate in Copute, just have your MIXIN extend the INTERFACE, then only extend the MIXIN where you need the default functionality.
If you don't want the default method to be overridden, then you can place it in the INTERFACE as a STATIC that inputs the INTERFACE as its first parameter. You then call these as Name.func(x, ...) where x is an instance of Name. I suppose I could support syntactical sugar for this case so you can call them as x.func(...).
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.
The consumer of an interface should not be partial to any particular implementation of subtypes, yet rather only to its documented semantics. By putting a default overrideable implementation in the interface (trait), the developer is able to avoid writing a complete specification of the interface, and thus consumers of the interface will rely on what they interpret the semantics to be by studying the default implementation. This will destroy code reuse.
One of my big goals with Copute, is I want to change the entire economy of open-source.
No high cost, i.e. no boilerplate when extending the base MIXIN. Major gains in code reuse discipline.
The point is that when the code is displayed in a monospace font without syntax highlighting (e.g. in HTML, PDF, Word documents, etc) then the viewer can detect the keywords more rapidly.
For those who hate it, I will definitely try to get a feature into the IDE (and hopefully Github too) to toggle the keywords to lowercase. And the all-caps is unnecessary when the keywords are colored.Okay, now I'm confused: does the case of the keywords matter? Your examples had led me to assume that the all-caps was required. If they are case-insensitive I might have other concerns (mostly that they consume more namespace), but this is a more minor issue.You can view them in lowercase only by having an IDE or renderer which display them in lowercase for you.
Then we lose the ability to distinguish keywords easily in code that is viewed in a text editor or in html. And how often are you likely to view the code in a non-IDE setting where it would annoy you?
But it impacts what type of library you can use, and I already made arguments that the category theory library is going to be much easier to use and better performant. I have to prove that. And I assume we agree the Scalaz isn't easy for you to use (and I don't know if it is more performant).
Just to give you a hint at the beginning: What do you expect from a guy
coming to a community where he isn't a very well known nor respected
member
and telling that everyone but him is blind in seeing all the
solutions needed to solve all the problems they have.
Now this user
starts in being aggressive due to - in his opinion - existing ignorance
and stupidity of single, but respected members of the community when
they try to contradict.
You can't really assume that anyone is giving much back to such a user.
And just to be clear, the above is exactly how I observe you.
Some quotes:
> I very much disagree with your concept that the world is composed of
people who don't program, and the rest who use some awesome language
such as Scala
> [...] he thinks all programmers are either stupid morons or use a
non-popular language.
> He basically told me to shut up.
> Why he wants discourage someone from fixing the other issues above I
can only assume is territorial myopia.
I have never written anything of the above and never thought so.
other members of a community, regardless who they are, the respect they
deserve,
thus I don't think that you are able to take part of a
discussion led in the sense of trying to improve.
> [...] then Jeff Atwood, the programmer who created StackOverflow, is
a dumb slave [...]
> [...] from Eric S. Raymond the self-claimed 150 - 170 IQ genius who
wrote The Cathedral and The Bazaar and The Art of Unix Programming.
There is no reason, not a single one, to claim the own opinion by famous
people. They are human too and it is possible that such people live
completely wrong their entire life. Normally such quotes are only done
by people not being able to think by themselves, you don't want that
others have this opinion from you.
I'm sure I care much more of Scala and its success than you do -
contributing to the IDE is only one part of what I did to that
community. I know that you wrote that all because you want to contribute
your own part but the way you are interacting is not the way one
interacts with a community - especially not a community you don't know
for depth. Not sure how you react on that, but I need to recommend you
this book:
On Fri, Sep 13, 2013 at 4:01 PM, Shelby <she...@coolpage.com> wrote:On Friday, September 13, 2013 10:17:49 PM UTC+8, Justin du Coeur wrote:Well, yes -- but I generally don't allow Any (or even AnyRef) in my code. I usually consider appearances of AnyRef to be design bugs,Well yes I consider then uni-typed typing holes too. But the point you haven't addressed is how do you write heterogeneous collections without using Any? I really have to see the magic you know that no one else seems to know?
I don't -- that is, I don't try to unify the types. I would live with the limitation, and use, eg, case classes with Option fields. It's clunky, but it works and is safe.
Or other mechanisms -- the best approach depends on the circumstance. The point is, you seem to be assuming that people can't live without heterogeneous collections. That doesn't match my experience -- indeed, it's extremely rare that I want truly heterogeneous collections. Collections whose values are subtyping a parent, sure -- but true heterogeneity? I don't think I've even *wanted* that any time in the past five years. The only times I've ever done it was by accident, fixed as soon as the compiler complained at me.
In other words, you're seeing a massive critical problem that I regard as, at most, a mild nuisance in practical application code. It's possible that you have encountered situations where it *is* a huge problem, but I've built enough complex, huge systems that I'm a tad skeptical that this is a deathly-important weakness.
There is no extra boilerplate in Copute, just have your MIXIN extend the INTERFACE, then only extend the MIXIN where you need the default functionality.That's still boilerplate, and is still poor factoring.
As I said above, if there is only one rational implementation (which is *very* common in my experience), then having to mix it in is unnecessary nuisance, and a recipe for accidental code duplication by people who don't happen to notice that there is One True Implementation existing somewhere else.
If you don't want the default method to be overridden, then you can place it in the INTERFACE as a STATIC that inputs the INTERFACE as its first parameter. You then call these as Name.func(x, ...) where x is an instance of Name. I suppose I could support syntactical sugar for this case so you can call them as x.func(...).
I would recommend that -- it at least provides a workaround to my major concern about code duplication. That said:
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.
Instead, my experience has been that Scala's traits get reuse and modularity exactly correct: implementation is fine on the trait, but should depend on the trait's abstracts. You leave abstract the things that are intended to vary, and provide implementation for the ones that aren't. Whether the trait happens to be a pure interface or not is a matter of happenstance, and not terribly relevant.
The consumer of an interface should not be partial to any particular implementation of subtypes, yet rather only to its documented semantics. By putting a default overrideable implementation in the interface (trait), the developer is able to avoid writing a complete specification of the interface, and thus consumers of the interface will rely on what they interpret the semantics to be by studying the default implementation. This will destroy code reuse.
Stuff and nonsense.
I reuse such traits all the time. Most of Scala's standard library is made up of such traits. Are you claiming that nobody ever reuses them?
And I rarely look at the implementation unless there is a compelling reason to do so -- I use the documentation, exactly the same way I would with an abstract interface.
One of my big goals with Copute, is I want to change the entire economy of open-source.Ambitious, but okay -- I can appreciate ambitious. But you're going to fail if you demand that everybody think like you, and sacrifice functionality that already exists in other languages that they find useful.
No high cost, i.e. no boilerplate when extending the base MIXIN. Major gains in code reuse discipline.If every implementation class has to mix in the mixin, that's still boilerplate. Concise boilerplate is still boilerplate.
The point is that when the code is displayed in a monospace font without syntax highlighting (e.g. in HTML, PDF, Word documents, etc) then the viewer can detect the keywords more rapidly.
For those who hate it, I will definitely try to get a feature into the IDE (and hopefully Github too) to toggle the keywords to lowercase. And the all-caps is unnecessary when the keywords are colored.Okay, now I'm confused: does the case of the keywords matter? Your examples had led me to assume that the all-caps was required. If they are case-insensitive I might have other concerns (mostly that they consume more namespace), but this is a more minor issue.You can view them in lowercase only by having an IDE or renderer which display them in lowercase for you.That seems deeply unwise in the JVM world. Symbols are case-sensitive, so having the IDE lie about case seems like a magnificent recipe for hard-to-understand bugs.
Then we lose the ability to distinguish keywords easily in code that is viewed in a text editor or in html. And how often are you likely to view the code in a non-IDE setting where it would annoy you?Um -- rarely? Never? Again, you're making a gigantic deal out of something that many of us regard as a non-problem. I find it mildly useful to have syntax highlighting in Eclipse, but I edit un-hilighted code in, eg, Notepad++ all the time, and rarely if ever find that to be an issue. The all-caps symbols, OTOH, would probably *always* bother me.
But it impacts what type of library you can use, and I already made arguments that the category theory library is going to be much easier to use and better performant. I have to prove that. And I assume we agree the Scalaz isn't easy for you to use (and I don't know if it is more performant).Correct -- but like I said, my problem there is more the concepts than the symbols. (I am deeply hoping that Greg Meredith's forthcoming book helps me there.)
On Thu, Sep 12, 2013 at 10:08 PM, Shelby <she...@coolpage.com> wrote:If everything is going to be a object with a meaningful type, then how does it make any sense that Scala chose not to implement first-class disjunctions? That is really perplexing me how Scala obtained such a large holeYou've made a big deal about this, but I think you've lost perspective. Again, from my viewpoint as a workaday engineer, this is an edge case. Yes, it is occasionally an annoying edge case that I have to work around, but honestly -- I don't even notice the lack most of the time. So while I agree that it would be lovely to fix this, it's an extremely minor detail in terms of what I look for in a language.
And (back to my previous point), if using it means encouraging my engineers to put Any into their function signatures, I will treat it as a misfeature and absolutely forbid its use.
Regarding meta programming, I haven't had time to research the proposals for Scala macros. What ever they do, I hope there is an option to see the generated Scala code. Shouldn't macros just be DSLs?You have that backwards. Macros are an implementation mechanism by which one *writes* more powerful DSLs. They're the enabling tool.
Shouldn't Copute be a DSL?I've been wondering that, yes. I'd be more likely to take Copute seriously if it was done as a library that plugged into Scala, instead of a separate and somewhat idiosyncratic language. That may or may not match your language goals, but it's a point worth factoring in.
On Thu, Sep 12, 2013 at 11:14 PM, Shelby <she...@coolpage.com> wrote:Here follows an example of myself recently trying to convince some expert C++ programmers to learn and use Scala for an upstart project that I was interested to contribute programming to:
(discussion continues to the next page of that thread)What you will find is that if most programmers don't know Scala, it is impossible to convince anyone to use Scala for a project.That isn't exactly a convincing argument. Indeed, it simply displays the same flaw that Suminda has been trying to point out, which you seem to be ignoring. In both cases, you (metaphorically) wandered into somebody else's house; declared that you are smarter than everyone present; started lecturing them pedantically; and wound up effectively calling other people stupid for disagreeing with you. (And then getting defensive and "you're all picking on me" when you met resistance.)
That is pretty much the *least* effective way to convince anybody of anything. Moving a community is a subtle game, and being right is by no stretch of the imagination sufficient to winning the argument.
It is absolutely essential that you engage in give-and-take, and frankly you have to be pretty damned thick-skinned to play that game successfully, since at *best* you are going to step on ideas that people are invested in.
In other words, it isn't level ground, and wishing that it was doesn't do you any favors. You are trying to change the way other people are doing things,
and the onus is *very* much on you to prove, very concretely, that you are correct -- and to engage very sincerely with the community to understand the shortcomings in your own ideas and find the necessary compromises.
I’m not speaking abstractly here. I’ve always been more interested in doing the right thing than doing what would make me popular, to the point where I generally figure that if I’m not routinely pissing off a sizable minority of people I should be pushing harder. In the language of psychology, my need for external validation is low; the standards I try hardest to live up to are those I’ve set for myself. But one of the differences I can see between myself at 25 and myself at 52 is that my limited need for external validation has decreased.
Which reminds me: the all-caps thing just plain bugs me. This isn't anything rational -- it's just many years of Internetting, teaching me that all-caps means YELLING. So on a purely aesthetic level, I look at this code and it feels like it's shouting at me, which gives me an instinctive negative gut reaction.The all-caps is much less annoying in the Eclipse IDE with its monospace font and purple syntax highlighting on the keywords. I agree it looks horrendous as displayed in this variable-width font used at google groups. I don't know what font you are viewing with in your email client.Granted, this sort of thing is in the eye of the beholder. But I have to point out that the purple highlighting is something you can turn off if you dislike it, whereas the all-caps is inherent in the language. (Or is it?? See below.)The point is that when the code is displayed in a monospace font without syntax highlighting (e.g. in HTML, PDF, Word documents, etc) then the viewer can detect the keywords more rapidly.For those who hate it, I will definitely try to get a feature into the IDE (and hopefully Github too) to toggle the keywords to lowercase. And the all-caps is unnecessary when the keywords are colored.Okay, now I'm confused: does the case of the keywords matter? Your examples had led me to assume that the all-caps was required. If they are case-insensitive I might have other concerns (mostly that they consume more namespace), but this is a more minor issue.I am also open to abandoning the all-caps keywords entirely if I get feedback from all sectors asking me to do so.Fair enough. Consider this a vote against.I had the same "ouch" reaction when I saw in this variable-width font display at the google groups' website UI. Caused me to doubt my original decision. But again, I think most code will be displayed in a monospace font, either syntax highlighted or not. It is the latter "not" case that I am trying to address, e.g. when one quickly types some code in a non-IDE text editor.Honestly, I think it's too high a price for an edge case. The looks aside, I find all-caps a pain in the ass to type -- either I have to bring in my capslock key (which I am not used to when coding, and isn't built into my touch-typing) or I have to hold down the Shift. Either way, it slows down my typing noticeably, and would likely annoy me a lot in practice.
I only claimed one case was unnecessary, which is the only case Copute eliminates:
def len(s: String): Int = {return s.length}
Also Copute gets rid of the = when followed by a braced enclosed block, to make it more consistent with (and familiar to programmers using) those popular languages.
On Saturday, September 14, 2013 4:53:58 AM UTC+8, Justin du Coeur wrote:I don't -- that is, I don't try to unify the types. I would live with the limitation, and use, eg, case classes with Option fields. It's clunky, but it works and is safe.
Don't you mean you use Either fields or OneOfX? I don't see how an Option would help you when you have two or more types (which are not subtypes of common type other than Any) in your collection.
When I say "reuse", I mean where we can't refactor the libraries, because we are interopting with them. Imagine the implausibility of refactoring the Scala std library for example or any popular library.
Disjunctions can eliminate a cartesian product of function overloading, i.e. your function can take N arguments of M types each, or you can write N x M overloaded methods.
I will concede your point here. I may be overestimating their importance. Yet I am not sure, and I don't want to risk it. So if Sergey's suggestion about Shapeless works, then I can have the disjunctions without subsuming them to Any nor the performance overhead (and boilerplate) of boxing them into Either or OneOfX.
As I said above, if there is only one rational implementation (which is *very* common in my experience), then having to mix it in is unnecessary nuisance, and a recipe for accidental code duplication by people who don't happen to notice that there is One True Implementation existing somewhere else.Because I already wrote you can put it in a STATIC in this case where there is only one rational implementation.
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.Please give me an example. I am confident you are confused or conflating some issues.
I remembered the other justification for this design. So that you can read an INTERFACE very cleanly, i.e. it is self-documenting from signatures, without the noise of implementation interspersed.
But as I've gotten older and more serious about my work, I have adopted what appears to match the following quote from ESR. It doesn't mean I won't try to be likeable, but when someone is just playing the beta-male wannabe alpha-male game with me, I just go into my sigma-male mode ("don't care, show me the technicals").
On Fri, Sep 13, 2013 at 6:37 PM, Shelby <she...@coolpage.com> wrote:On Saturday, September 14, 2013 4:53:58 AM UTC+8, Justin du Coeur wrote:I don't -- that is, I don't try to unify the types. I would live with the limitation, and use, eg, case classes with Option fields. It's clunky, but it works and is safe.
Don't you mean you use Either fields or OneOfX? I don't see how an Option would help you when you have two or more types (which are not subtypes of common type other than Any) in your collection.No, I rarely use either of those, precisely because they don't work very well. Instead, I usually resign myself to more complex data structures with distinct, strongly-typed optional fields for the different types that might be captured. That's a weak solution, but it's safe and often permits me to capture other surrounding data that turns out to be relevant.But really -- I just plain don't hit this very often in my ordinary, real-world code, so I don't spend a lot of time worrying about it.
When I say "reuse", I mean where we can't refactor the libraries, because we are interopting with them. Imagine the implausibility of refactoring the Scala std library for example or any popular library.
Disjunctions can eliminate a cartesian product of function overloading, i.e. your function can take N arguments of M types each, or you can write N x M overloaded methods.So is that your intended use case?
One of the challenges I'm having here is that I don't really understand your target applications. For day-to-day coding, Copute looks like it mostly adds features I don't care about, and introduces problems I really don't want to deal with. If your focus is on high-level libraries, it is possible that that's a lesser concern -- that's not my area of expertise. (But it also is largely irrelevant to most folks just starting out in this environment, very few of whom want to move quickly into high-level library design.)
And I have to underscore my previous point: disjunctions that simply become Any at the JVM level are probably a strong net negative to me -- I wouldn't be willing to work with any library that exposed them, because I wouldn't be able to trust the robustness of the resulting application code in the face of enhancement and maintenance. Either is kind of weak, but I at least *mostly* trust it not to introduce subtle bugs.I will concede your point here. I may be overestimating their importance. Yet I am not sure, and I don't want to risk it. So if Sergey's suggestion about Shapeless works, then I can have the disjunctions without subsuming them to Any nor the performance overhead (and boilerplate) of boxing them into Either or OneOfX.I'd recommend exploring this. Frankly, the performance overhead is a relatively minor detail -- boxing is a fact of life in the JVM, and most of us writing application code don't spend a lot of time worrying about it except in pathological cases.
And the boilerplate of Either would be less important if we're talking about generated code. Generated code is allowed to have as much boilerplate as it likes, pretty much. I worry that the resulting *usage* code in Scala, using values from generated Copute code, might be fattier than I like, but that's a lesser evil than exposed Any's.As I said above, if there is only one rational implementation (which is *very* common in my experience), then having to mix it in is unnecessary nuisance, and a recipe for accidental code duplication by people who don't happen to notice that there is One True Implementation existing somewhere else.Because I already wrote you can put it in a STATIC in this case where there is only one rational implementation.Hmm. Makes me twitch on an aesthetic level, and I think it's rather confusing, since what I'm talking about is not *conceptually* static -- after all, if you're passing in an instance, then it's an instance method almost by definition. (The sometime C++ engineer in me thinks quite strongly in those terms -- if you're passing the instance as a parameter, it's a method; if not, it's static.)
I'll grant you that it may be a practical solution to the problem, but I wind up questioning the keyword STATIC -- that isn't what most people *mean* when they say "static", and it's a very unusual usage. I find it unintuitive.
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.Please give me an example. I am confident you are confused or conflating some issues.... no. Sorry, Shelby, but you've gotten my goat, and I'm going to back off from the conversation before I get heated. I'm failing to keep my temper in check, so it's probably time for me to give up on trying to help you.I have to observe that your choice of rhetoric is poor. You've made a big deal about other folks getting ad hominem at you, but the reason that's happening is because your conversational style is full of flamebait like this.
I've been drifting too close to responding in kind, and that means it's time to go away. I've been participating in this conversation because it was intellectually interesting, and might possibly result in something useful, but ultimately I don't actually *care* very much. Trying to help out isn't worth being insulted -- I have better things to do.
Is it not obvious that the above, in the context of this conversation, is insulting? Just how unwelcome it is that you are brushing off my experience (which is almost
certainly at least equal to yours in terms of practical engineering) with "you are confused"? I'm not sure whether you honestly don't really how nasty and arrogant you sound in the aggregate, or if you do it intentionally. Once or twice wouldn't be a big deal -- everyone falls into these traps occasionally -- but you've done things like this rather frequently in the course of talking to various people over the past week or two.
Either way, if you don't learn to control it, you're very likely to self-sabotage any serious efforts you make in the open-source arena. Making an OSS project succeed depends at *least* as much on good management as it does on being right -- working closely with the community, listening very hard to their critiques, and taking their comments with the utmost seriousness.
I probably spend a third of my time doing that for my project, and it's not easy -- it requires swallowing my own ego, constantly asking whether I've gotten the details wrong, and making course adjustments every single day based on the input I'm getting from the folks around me. It doesn't mean that I follow every one of their suggestions -- but in practice I *do* probably incorporate over half of them into my designs, and I never, *ever* insult the intelligence of the members of the community.
Your project, and your call -- you get to manage it as you like. But I point out that you've managed to drive away pretty much everyone here who showed interest. That doesn't bode well, and you may want to learn from it...
Following on from my earlier point:On Fri, Sep 13, 2013 at 8:35 PM, Shelby <she...@coolpage.com> wrote:
But as I've gotten older and more serious about my work, I have adopted what appears to match the following quote from ESR. It doesn't mean I won't try to be likeable, but when someone is just playing the beta-male wannabe alpha-male game with me, I just go into my sigma-male mode ("don't care, show me the technicals").That's not a bad strategy -- heaven knows, I often do it myself. (Both professionally and in the various clubs I'm involved with.) But to play that game successfully, it absolutely *demands* both a thick skin
and the discipline to back off from an argument before it gets heated.
That's *very* hard, but you will generally undermine yourself when you fail to do so.
And keep in mind that it isn't sufficient for running a successful larger-scale project. It's possible for you to design Copute yourself, and maybe even to build it yourself. But *popularizing* it (which I gather is another goal) is in many ways a management / marketing problem, and calls for very different mental tools.
I recommend thinking about how you're going to accomplish that. (I've learned a good deal of that through spending a number of years as Product and/or Project Manager on various projects, as well as technical lead. *Very* different skills.)
And despite Eric's big name in the OSS world, it's worth noting that he hasn't actually *led* any really major OSS projects that I'm aware of. I don't know whether he covers the nitty-gritty problems of leading successful OSS projects in The Cathedral and the Bazaar (I haven't read it), but there are a number of books specifically on the subject of managing and leading such projects that have come out in the past decade. You might do well to internalize a few of them...
On Sunday, September 15, 2013 5:33:21 AM UTC+8, Justin du Coeur wrote:On Fri, Sep 13, 2013 at 6:37 PM, Shelby <she...@coolpage.com> wrote:
When I say "reuse", I mean where we can't refactor the libraries, because we are interopting with them. Imagine the implausibility of refactoring the Scala std library for example or any popular library.Disjunctions can eliminate a cartesian product of function overloading, i.e. your function can take N arguments of M types each, or you can write N x M overloaded methods.So is that your intended use case?
Yes the inability to refactor [that use] case.
For example if I am mashing up a collection of data structures returned by different modules written by different teams all over the world.One of the challenges I'm having here is that I don't really understand your target applications. For day-to-day coding, Copute looks like it mostly adds features I don't care about, and introduces problems I really don't want to deal with. If your focus is on high-level libraries, it is possible that that's a lesser concern -- that's not my area of expertise. (But it also is largely irrelevant to most folks just starting out in this environment, very few of whom want to move quickly into high-level library design.)You entirely miss the point which is that users need to consume libraries and have them all work together. Modularity is what makes that happen.Right now you choose one big monolithic library and you are stuck with what ever is compatible with it. You can't pick and choose granularly, because we don't write libraries nor userland code in a way that compose without refactoring.The Expression Problem or more importantly the fragile base class problem.
Because I already wrote you can put it in a STATIC in this case where there is only one rational implementation.Hmm. Makes me twitch on an aesthetic level, and I think it's rather confusing, since what I'm talking about is not *conceptually* static -- after all, if you're passing in an instance, then it's an instance method almost by definition. (The sometime C++ engineer in me thinks quite strongly in those terms -- if you're passing the instance as a parameter, it's a method; if not, it's static.)My quick explanation of type theory in layman's terms today from first principles may elucidate:In particular, note that `this` plays not role in subtyping with typeclasses. I quote myself, "Type classes are a form of polymorphism (i.e. extensibility) where the `this` parameter is a type parameter of the operations. The operations for a concrete type can be matched nominally (by name) as shown in the OP or even structurally (although this can be erased and sometime cause reflection on the JVM and thus is probably inefficient)". Then read the rest of that post to compare to the role or `this` in subtyping.So either it is an overrideable method (in which case put it in a mixin) or it is not (so you can put it in a static).The point of putting overrideable methods in the mixin is separation-of-concerns, a fundamental design principle. Your point is to favor SPOT (single-point-of-truth), but putting it in the mixin maintains SPOT and factors the code more correctly.First you think about interface which means specification. Then you think about implementation, c.f. ESR's famous book The Art of Unix Programming.
Aesthetically it is beautiful, because the boundary is clea[r]-- overrideable implementation goes in the mixin and/or class. Specification goes in the interface.
I'll grant you that it may be a practical solution to the problem, but I wind up questioning the keyword STATIC -- that isn't what most people *mean* when they say "static", and it's a very unusual usage. I find it unintuitive.It is same historical meaning of the static keyword. It is function that doesn't change per instance.Now I add a new capability to static, which is you can implement it in SUB types. That is to support the benefits of typeclasses, c.f. the prior links and also this one:And some new discussion of problems with the std library:The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.Please give me an example. I am confident you are confused or conflating some issues.... no. Sorry, Shelby, but you've gotten my goat, and I'm going to back off from the conversation before I get heated. I'm failing to keep my temper in check, so it's probably time for me to give up on trying to help you.I have to observe that your choice of rhetoric is poor. You've made a big deal about other folks getting ad hominem at you, but the reason that's happening is because your conversational style is full of flamebait like this.I was correct.
On Sunday, September 15, 2013 5:33:21 AM UTC+8, Justin du Coeur wrote:On Fri, Sep 13, 2013 at 6:37 PM, Shelby <she...@coolpage.com> wrote:Please give me an example. I am confident you are confused or conflating some issues.... no. Sorry, Shelby, but you've gotten my goat, and I'm going to back off from the conversation before I get heated. I'm failing to keep my temper in check, so it's probably time for me to give up on trying to help you.I have to observe that your choice of rhetoric is poor. You've made a big deal about other folks getting ad hominem at you, but the reason that's happening is because your conversational style is full of flamebait like this.I was correct. Now we see above you were conflating `this` with subtyping.Sorry I speak matter-of-factly. I didn't intend it as an insult.
On Sat, Sep 7, 2013 at 11:24 PM, √iktor Ҡlang <viktor...@gmail.com> wrote:
> Would you so kindly sum it up for me?
"Your ideas are intriguing to me and I wish to subscribe to your newsletter" ;-)
On Mon, Sep 9, 2013 at 10:13 AM, Shelby <she...@coolpage.com> wrote:
> On Monday, September 9, 2013 4:57:50 PM UTC+8, Miles Sabin wrote:
>
>> On Sat, Sep 7, 2013 at 11:24 PM, √iktor Ҡlang <viktor...@gmail.com> wrote:
>> > Would you so kindly sum it up for me?
>>
>> "Your ideas are intriguing to me and I wish to subscribe to your
>> newsletter" ;-)
>
>
> Please don't tell me I am going to get that same sh$t here. Cripes, it is
> scala-DEBATE.
Exactly ... I think you're looking for scala-troll.
On Monday, September 9, 2013 5:34:48 PM UTC+8, Miles Sabin wrote:On Mon, Sep 9, 2013 at 10:13 AM, Shelby <she...@coolpage.com> wrote:
> On Monday, September 9, 2013 4:57:50 PM UTC+8, Miles Sabin wrote:
>
>> On Sat, Sep 7, 2013 at 11:24 PM, √iktor Ҡlang <viktor...@gmail.com> wrote:
>> > Would you so kindly sum it up for me?
>>
>> "Your ideas are intriguing to me and I wish to subscribe to your
>> newsletter" ;-)
>
>
> Please don't tell me I am going to get that same sh$t here. Cripes, it is
> scala-DEBATE.
Exactly ... I think you're looking for scala-troll.Why does it harm you that I want to elaborate on my thoughts about symbols, responding to two people who were discussing that with me?Why do you wish to discourage open discussion about Scala?I was not rude to anyone, yet you were rude to me.Thus I conclude you have an anti-social personality.According to the rules that govern this forum, you should now receive a warning, because you violated two of the rules:1. Being discourteous2. Using the "troll" word.
On Sun, Sep 8, 2013 at 1:34 PM, Shelby <she...@coolpage.com> wrote:
In fact, my brain wants to filter anything that is not direct-to-the-point and intuitively, readily grasped.I can entirely understand that, but I don't think it's an excuse. This stuff *is* deep, and excelling at it requires a time investment to understand it. And yes, that does mean reading the documentation -- not every detail of the spec, but at least the good overview books like the ones I mention above.I really do get what you're saying. But I think you're just plain taking the argument to the point of reductio ad absurdam -- it's coming across as "I don't personally get that symbol immediately, so it is Bad", which is subjective to the point of useless. And I find it poorly aimed ...
On Thu, Sep 12, 2013 at 11:14 PM, Shelby <she...@coolpage.com> wrote:Here follows an example of myself recently trying to convince some expert C++ programmers to learn and use Scala for an upstart project that I was interested to contribute programming to:(discussion continues to the next page of that thread)What you will find is that if most programmers don't know Scala, it is impossible to convince anyone to use Scala for a project.That isn't exactly a convincing argument. Indeed, it simply displays the same flaw that Suminda has been trying to point out, which you seem to be ignoring. In both cases, you (metaphorically) wandered into somebody else's house; declared that you are smarter than everyone present; started lecturing them pedantically; and wound up effectively calling other people stupid for disagreeing with you. (And then getting defensive and "you're all picking on me" when you met resistance.)
On Saturday, September 14, 2013 12:45:32 AM UTC+8, Justin du Coeur wrote:On Thu, Sep 12, 2013 at 11:14 PM, Shelby <she...@coolpage.com> wrote:Here follows an example of myself recently trying to convince some expert C++ programmers to learn and use Scala for an upstart project that I was interested to contribute programming to:(discussion continues to the next page of that thread)What you will find is that if most programmers don't know Scala, it is impossible to convince anyone to use Scala for a project.That isn't exactly a convincing argument. Indeed, it simply displays the same flaw that Suminda has been trying to point out, which you seem to be ignoring. In both cases, you (metaphorically) wandered into somebody else's house; declared that you are smarter than everyone present; started lecturing them pedantically; and wound up effectively calling other people stupid for disagreeing with you. (And then getting defensive and "you're all picking on me" when you met resistance.)Triangulation helps formulate rationality w.r.t. to our interpretations, because we are often blinded by biases and emotion.Actually they were invading my house, as evident by myself winning the poll with 85% support for my technical arguments:Both of the principles asked for my feedback:And I was asked by others in that forum on that day to go present my analysis in that thread.And I did not belabor the point about languages and conceded it to them:And even convinced the person who was arguing with me to take interest in Scala:
See where sulking causes rationality to go ;)
On Wed, Sep 11, 2013 at 8:47 PM, Shelby Moore <she...@coolpage.com> wrote:
3. Traits are called INTERFACE (and there is a separate MIXIN syntax),
because they do not contain non-static implementation. This restraint and
optimization is allowed because Copute is supporting pure functional
(immutable, referentially transparent) programming only.
I've been staring at this for hours now, and I'm not getting it. Why is this a good thing? I mean, the power of traits is one of the things I love about Scala, specifically because years of working with Java led me to *despise* pure interfaces. They sound good, sure, but in practice I've had too many times that I've built an interface that seemed like it was pure, only to gradually find that there were several methods I wanted that really, deeply, belonged on the interface. It always led to annoying duplicate code in the implementations.
On Friday, September 13, 2013 4:46:34 AM UTC+8, Justin du Coeur wrote:I've been staring at this for hours now, and I'm not getting it. Why is this a good thing? I mean, the power of traits is one of the things I love about Scala, specifically because years of working with Java led me to *despise* pure interfaces. They sound good, sure, but in practice I've had too many times that I've built an interface that seemed like it was pure, only to gradually find that there were several methods I wanted that really, deeply, belonged on the interface. It always led to annoying duplicate code in the implementations.So I guess the question is -- why would I ever want an INTERFACE instead of a MIXIN? I *hate* interfaces with a burning passion, mostly due to Java experience.I think what you hate about Java is single-inheritance (lack of multiple inheritance and mixins). Thus, I think you are conflating this with the benefits on an interface that contains no implementation.
On Thu, Sep 12, 2013 at 5:55 PM, Shelby <she...@coolpage.com> wrote:
I think what you hate about Java is single-inheritance (lack of multiple inheritance and mixins). Thus, I think you are conflating this with the benefits on an interface that contains no implementation.It's possible that you mean something very different by "interface" and "mixin" than I do, but I don't think you're listening to what I'm saying.Take an Interface with properties A, B and C. Given those, it is *extremely* common for me to want to define functions D and E, which are entirely functions *over* A, B and C, and are essentially universal to any implementation of the interface. The best place to put those functions, in my experience, is typically in the same trait.
Yes, it is *possible* to put the function definitions into a mixin -- but from a factoring POV that's generally inappropriate
On Thu, Sep 12, 2013 at 10:08 PM, Shelby <she...@coolpage.com> wrote:If everything is going to be a object with a meaningful type, then how does it make any sense that Scala chose not to implement first-class disjunctions? That is really perplexing me how Scala obtained such a large holeYou've made a big deal about this, but I think you've lost perspective.
it's an extremely minor detail in terms of what I look for in a language.
Regarding meta programming, I haven't had time to research the proposals for Scala macros. What ever they do, I hope there is an option to see the generated Scala code. Shouldn't macros just be DSLs?You have that backwards.
On Fri, Sep 13, 2013 at 4:01 PM, Shelby <she...@coolpage.com> wrote:
In other words, you're seeing a massive critical problem that I regard as, at most, a mild nuisance in practical application code. It's possible that you have encountered situations where it *is* a huge problem, but I've built enough complex, huge systems that I'm a tad skeptical that this is a deathly-important weakness.
There is no extra boilerplate in Copute, just have your MIXIN extend the INTERFACE, then only extend the MIXIN where you need the default functionality.
That's still boilerplate, and is still poor factoring. As I said above, if there is only one rational implementation (which is *very* common in my experience), then having to mix it in is unnecessary nuisance, and a recipe for accidental code duplication by people who don't happen to notice that there is One True Implementation existing somewhere else.
If you don't want the default method to be overridden, then you can place it in the INTERFACE as a STATIC that inputs the INTERFACE as its first parameter. You then call these as Name.func(x, ...) where x is an instance of Name. I suppose I could support syntactical sugar for this case so you can call them as x.func(...).I would recommend that -- it at least provides a workaround to my major concern about code duplication. That said:
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.
Instead, my experience has been that Scala's traits get reuse and modularity exactly correct: implementation is fine on the trait, but should depend on the trait's abstracts. You leave abstract the things that are intended to vary, and provide implementation for the ones that aren't. Whether the trait happens to be a pure interface or not is a matter of happenstance, and not terribly relevant.
The consumer of an interface should not be partial to any particular implementation of subtypes, yet rather only to its documented semantics. By putting a default overrideable implementation in the interface (trait), the developer is able to avoid writing a complete specification of the interface, and thus consumers of the interface will rely on what they interpret the semantics to be by studying the default implementation. This will destroy code reuse.Stuff and nonsense. I reuse such traits all the time. Most of Scala's standard library is made up of such traits. Are you claiming that nobody ever reuses them? And I rarely look at the implementation unless there is a compelling reason to do so -- I use the documentation, exactly the same way I would with an abstract interface.
One of my big goals with Copute, is I want to change the entire economy of open-source.Ambitious, but okay -- I can appreciate ambitious. But you're going to fail if you demand that everybody think like you, and sacrifice functionality that already exists in other languages that they find useful.
No high cost, i.e. no boilerplate when extending the base MIXIN. Major gains in code reuse discipline.If every implementation class has to mix in the mixin, that's still boilerplate. Concise boilerplate is still boilerplate.
On Saturday, September 14, 2013 4:53:58 AM UTC+8, Justin du Coeur wrote:
In other words, you're seeing a massive critical problem that I regard as, at most, a mild nuisance in practical application code. It's possible that you have encountered situations where it *is* a huge problem, but I've built enough complex, huge systems that I'm a tad skeptical that this is a deathly-important weakness.
I will concede your point here. I may be overestimating their importance. Yet I am not sure, and I don't want to risk it. So if Sergey's suggestion about Shapeless works, then I can have the disjunctions without subsuming them to Any nor the performance overhead (and boilerplate) of boxing them into Either or OneOfX.
There is no extra boilerplate in Copute, just have your MIXIN extend the INTERFACE, then only extend the MIXIN where you need the default functionality.
That's still boilerplate, and is still poor factoring.
No, because...As I said above, if there is only one rational implementation (which is *very* common in my experience), then having to mix it in is unnecessary nuisance, and a recipe for accidental code duplication by people who don't happen to notice that there is One True Implementation existing somewhere else.
Because I already wrote you can put it in a STATIC in this case where there is only one rational implementation.
If you don't want the default method to be overridden, then you can place it in the INTERFACE as a STATIC that inputs the INTERFACE as its first parameter. You then call these as Name.func(x, ...) where x is an instance of Name. I suppose I could support syntactical sugar for this case so you can call them as x.func(...).
I would recommend that -- it at least provides a workaround to my major concern about code duplication. That said:
So why did you continue to argue above? Let's try to keep the noise level of our posts down and focus on points that are still in contention.
The main justification is that interface is orthogonal to implementation. This is crucial design concept for code reuse and modularity.
I can see that you are making this as a fundamental assumption. I don't happen to agree, and I no longer find the arguments persuasive. I spent many years going down that road, and found that in practice, it leads to *poorer* code reuse and *worse* modularity. It's an assertion that sounds nice, but in my experience doesn't match reality.
Please give me an example. I am confident you are confused or conflating some issues.
(2) Please give me an example. I'm pretty sure that this isn't actually the case.
(3) Please give me an example. I'm confident you're confused.
(4) Give me an example. I'm sure you're confused; you've barely thought about this at all.
Shelby,
>
> P.S. it is time-consuming for me to write this post. I hope all will
> feel a little bit embarrassed at wasting my time, forcing me to
> enumerate what is obvious from the objective evidence.
that is exactly your problem. No one forces you to do anything.
Being
respectful doesn't mean to tell others how great they are (I can assure
you these guys already know that they have your respect), it means being
friendly to the ones you think are wrong or even worse the ones you know
are dump as hell.
It can also be form of respect to tell someone you are wrong even if you
know that it's not the case.
Another form of respect is to not respond
at all (for example because you know you can't end a discussion
otherwise, which would just waste peoples time).
>
> I rest my case your honor.
The question is not if we can forgive you, the question is if you can
forgive others.
On Monday, September 16, 2013 2:10:13 PM UTC+8, Simon Schäfer wrote:It can also be form of respect to tell someone you are wrong even if youknow that it's not the case.I will never do that. Sorry.Another form of respect is to not respondSorry I don't adhere to political rigor mortis.I want to eliminate politics entirely with technology. Realistic or not, I see that as the only way to a highly prosperous future.at all (for example because you know you can't end a discussion
otherwise, which would just waste peoples time).Not responding because it is redundant, is something I do. If you repeat the same arguments, I will not respond.
Hi Rex,I've read many of your posts in the Scala discussion groups, the bug tracker, and also at stackoverflow and else where. So I know you are one of those extremely knowledgeable (more than me in many areas apparently) people that I had in mind when I wrote at the bitcointalk.org forum that the IQ level in the Scala community is extremely high, with Haskell's community perhaps higher. I wrote that before coming back to participate in these discussion groups recently following a more than several month hiatus. So the idea that I don't give respect to people here doesn't match the demonstrated reality of my behavior.You are clearly biased by your emotions in this case. Let's review the evidence.This is going to be illustrative (and I hope will cause people to apologize to me for misjudging me and to more carefully triangulate their subjectiveness in the future)...
--
> It is the open-source code that matters.I don't think this could be farther from the truth, in any situation.
If you don't think empathizing and convincing people matters, you really shouldn't be surprised if other people show no empathy and remain unconvinced...
For all the times you've talked about code mattering, all I've seen is lengthy lectures, with no code at all!
If you came in with a prototype that demonstrated the correctness and value of your ideas, I'm pretty sure it would generate some interest.
5. More concise way to write typeclasses, that integrates well with
subtyping. Before I describe the syntax and translation to Scala, this
feature as well as #4 above are primarily motivated by the desire to
support category theory (a la Scalaz) in a more intuitive, less verbose,
and less tsuris syntax.
Basically I understand a typeclass to be a structural type, such that code
can reference a member (e.g. method) of that structural type on any
instance of a type which implements that structure. Some incorrectly refer
to this as ad-hoc polymorphism but I understand the latter term be to a
more general concept of combining structural typing with function
overloading.
Daniel Spiewak wrote the first example Scala typeclass I encountered:
http://www.codecommit.com/blog/ruby/monads-are-not-metaphors
trait Monad[+M[_]] {
def unit[A](a: A): M[A]
def bind[A, B](m: M[A])(f: A => M[B]): M[B]
}
implicit object ThingMonad extends Monad[Thing] {
def unit[A](a: A) = Thing(a)
def bind[A, B](thing: Thing[A])(f: A => Thing[B]) = thing bind f
}
implicit object OptionMonad extends Monad[Option] {
def unit[A](a: A) = Some(a)
def bind[A, B](opt: Option[A])(f: A => Option[B]) = opt bind f
}
sealed trait Option[+A] {
def bind[B](f: A => Option[B]): Option[B]
}
case class Some[+A](value: A) extends Option[A] {
def bind[B](f: A => Option[B]) = f(value)
}
case object None extends Option[Nothing] {
def bind[B](f: Nothing => Option[B]) = None
}
def sequence[M[_], A](ms: List[M[A]])(implicit tc: Monad[M]) = {
ms.foldRight(tc.unit(List[A]())) { (m, acc) =>
tc.bind(m) { a => tc.bind(acc) { tail => tc.unit(a :: tail) } }
}
}
For comparison, I will show some proposed equivalent functionality Copute
code, which you shall note is drastically more concise as well unified
with subtyping:
INTERFACE Monad[A] {
STATIC unit: A Sub[A]
bind[B]: (A Sub[B]) Sub[B]
}
INTERFACE Option[A]: Monad[A] {
Monad.unit(a) = Some(a)
}
CLASS Some[A](value: A): Option[A] {
bind(f) = f(value)
}
OBJECT None: Option[All] {
bind(_) = None
}
OBJECT sequence {
apply[M[A]: Monad[A],A](ms): List[M[A]] M[List[A]] {
ms.foldRight(M.unit(List[A]()))( (m, acc) {
m.bind( (a) {acc.bind( (tail) {M.unit(a :: tail)} )} )
})
}
}
The proposed translation to Scala:
trait Monad[+Sub[A] <: Monad[Sub,A], +A] {
def bind[B](_1: (A) => Sub[B]): Sub[B]
}
trait Option[+A] extends Monad[Option,A]
case class Some[A](value: A) extends Option[A] {
def bind[B](f: (A) => Option[B]): Option[B] = f(value)
}
case object None extends Option[Nothing] with staticOption {
def bind[B](_1: (Nothing) => Option[B]): Option[B] = None
}
trait staticMonad[+Sub[Any] <: Monad[Sub,Any]] {
def unit[A](_1: A): Sub[A]
}
trait staticOption extends staticMonad[Option] {
def unit[A](a: A): Option[A] = Some(a)
}
object Option extends staticOption
object Some extends staticOption
object Implicits {
implicit object OptionImplicit extends staticOption
}
import Implicits._
object sequence {
def apply[M[A] <: Monad[M,A],A](ms: List[M[A]])(implicit tc:
staticMonad[M]): M[List[A]] = {
ms.foldRight(tc.unit(List[A]()))( (m, acc) =>
m.bind( (a) => acc.bind( (tail) => tc.unit(a :: tail) ) )
)
}
}
(Tangentially, if I can figure out how to elegantly support Scala's =>
anonymous function syntax in Copute's LL(k) grammar, then I will.)
Note the subtypes which did not implement a STATIC extend the same
staticOption, and they don't create conflicting implicits.
Note how the Copute compiler must be smart enough to see that the common
Monad subtype of Some[A] and None is Option[A], so that it subsumes Sub[A]
to Option[A] since Sub[A] can't have two types Some[A] and None if they
both have have a common subtype which is a Monad.
Note that Copute STATIC methods that are implemented in the INTERFACE
where they are first declared, can go directly in the companion (same name
as trait) Scala object without the above named static* hierarchy.
Also note that where Copute STATIC methods are not all implemented in the
same subtype, then the static* hierarchy will have Sub2 etc for each fork,
e.g. assuming bind was a STATIC, then:
trait staticMonad[+Sub[Any] <: staticMonad[Sub,Sub2,Any], +Sub2[Any] <:
staticMonad[Sub,Sub2,Any]] {
def unit[A](_1: A): Sub[A]
def bind[A,B](_1: (A) => Sub2[B]): Sub2[B]
}
trait staticOption[+Sub2[Any] <: staticOption[Sub2,Any]] extends
staticMonad[Option,Sub2,Any] {
def unit[A](a: A): Option[A] = Some(a)
}
...
I believe I have worked out all the issues with this, yet if anyone sees a
corner case or flaw, please tell me.
However, at the use site, I could not without do the transformation without access to Scala's AST for the computed types (i.e. I would need to figure out how to write Copute as a Scala plugin). Yet I could do the following in Copute syntax instead (note that : is an upper bound in Copute i.e. <: in Scala):def f[M[A]: Seq[A], A](xs: Seq[A]) = M.map(xs) ...
At that point, I decided to disallow non-familiar, non-left associative symbol operators. I don't even like List.++, why not just overload the List.+.
On Tue, Sep 17, 2013 at 4:57 PM, Shelby <she...@coolpage.com> wrote:
At that point, I decided to disallow non-familiar, non-left associative symbol operators. I don't even like List.++, why not just overload the List.+.Or, to put it another way, at that point you decided to throw the baby out with the bathwater.There is a reason that mathematicians use symbols, and it is because they clarify what is going on. It's easier to conceptualize ∇v when written compactly like that than grad(v_x, v_y, v_z). You can work with it symbolically.
And there is a reason why mathematicians don't just use + over and over again; sometimes you need to distinguish between different kinds of addition. (Note ⊕ used for XOR, which is addition-like.)
I think you're looking at this backwards. + is not a great thing to use just because everyone's familiar with addition. It's great because it is a great concept to have. You want that sort of convenience when dealing with something so fundamental yet powerful. It's so useful that we teach it to everyone.
But just because you decide + is useful for everyone, it doesn't follow that ++ should be avoided by all programmers. Instead, one should ask: is there a concept that we could powerfully associate with ++? It had better be +-like, or it will confuse everyone. But it had also better be very important to keep distinct from +.
And that is exactly what concatenation vs. addition of lists is: another kind of addition, one with different but equally valuable behavior.
And this doesn't go just for lists. There are a variety of things that either mirror widely (if not universally) used mathematical notation (e.g. f: a -> b) which should be used.
Now, you can try to guess them all, or you can allow users to use the ones that make sense. I've yet to see a language that hasn't forbidden operators that make sense, thereby transmuting difficult-to-understand material into an impossible morass of huge words or strange abbreviations. Take list cons:
case x :: y :: more =>What would you replace :: with in order to make this any more clear? What would you replace it with to avoid making it way _less_ clear?
Anyway, there is certainly a school of thought that says that you should forbid anything that can be confused. But I'm much more interested in languages that make it possible for me to do something that otherwise would be beyond my reach than languages that keep me from reaching far enough to confuse newcomers.
Of course, the two goals overlap to some extent, since if I can't understand my old code, anything that would require such understanding will be beyond me (the maximum size of the projects I can handle will be reduced). But sometimes there is a tradeoff, and then I am not much interested in having my wings clipped.
P.S. IDEs can turn symbols into lengthy descriptors too.--Rex
And there is a reason why mathematicians don't just use + over and over again; sometimes you need to distinguish between different kinds of addition. (Note ⊕ used for XOR, which is addition-like.)
Now you are talking about overloading, and not compaction.
I think you're looking at this backwards. + is not a great thing to use just because everyone's familiar with addition. It's great because it is a great concept to have. You want that sort of convenience when dealing with something so fundamental yet powerful. It's so useful that we teach it to everyone.
But just because you decide + is useful for everyone, it doesn't follow that ++ should be avoided by all programmers. Instead, one should ask: is there a concept that we could powerfully associate with ++? It had better be +-like, or it will confuse everyone. But it had also better be very important to keep distinct from +.
And that is exactly what concatenation vs. addition of lists is: another kind of addition, one with different but equally valuable behavior.It is more confusing that + is overloading in some scenarios yet not in others.I can add two strings, I can add an element to a list, but I can't add two lists.
And ++ is traditionally a unary post and/or pre-increment operator, so that makes it very, very confusing for everyone coming from C-like languages (which is the mainstream).
And this doesn't go just for lists. There are a variety of things that either mirror widely (if not universally) used mathematical notation (e.g. f: a -> b) which should be used.
Now, you can try to guess them all, or you can allow users to use the ones that make sense. I've yet to see a language that hasn't forbidden operators that make sense, thereby transmuting difficult-to-understand material into an impossible morass of huge words or strange abbreviations. Take list cons:
case x :: y :: more =>What would you replace :: with in order to make this any more clear? What would you replace it with to avoid making it way _less_ clear?What is wrong with case List(x, y)?
On Tue, Sep 17, 2013 at 11:22 PM, Shelby <she...@coolpage.com> wrote:
And there is a reason why mathematicians don't just use + over and over again; sometimes you need to distinguish between different kinds of addition. (Note ⊕ used for XOR, which is addition-like.)
Now you are talking about overloading, and not compaction.Kind of. a + b + c where all types are the same either way but the +'s mean different things is incredibly confusing or just plain won't work.
But if you now need two plus-like symbols and you're only allowed one, you have thrown away compaction because overloading is impossible.
I think you're looking at this backwards. + is not a great thing to use just because everyone's familiar with addition. It's great because it is a great concept to have. You want that sort of convenience when dealing with something so fundamental yet powerful. It's so useful that we teach it to everyone.
But just because you decide + is useful for everyone, it doesn't follow that ++ should be avoided by all programmers. Instead, one should ask: is there a concept that we could powerfully associate with ++? It had better be +-like, or it will confuse everyone. But it had also better be very important to keep distinct from +.
And that is exactly what concatenation vs. addition of lists is: another kind of addition, one with different but equally valuable behavior.It is more confusing that + is overloading in some scenarios yet not in others.I can add two strings, I can add an element to a list, but I can't add two lists.Adding two strings is the weird one here.
And ++ is traditionally a unary post and/or pre-increment operator, so that makes it very, very confusing for everyone coming from C-like languages (which is the mainstream).Yes, but unfortunately that operator is a really poor choice since it eats an extremely valuable symbol that can distinguish between addition/appending and concatenation.
Which is important unless all your collections are invariant.
And this doesn't go just for lists. There are a variety of things that either mirror widely (if not universally) used mathematical notation (e.g. f: a -> b) which should be used.
Now, you can try to guess them all, or you can allow users to use the ones that make sense. I've yet to see a language that hasn't forbidden operators that make sense, thereby transmuting difficult-to-understand material into an impossible morass of huge words or strange abbreviations. Take list cons:
case x :: y :: more =>What would you replace :: with in order to make this any more clear? What would you replace it with to avoid making it way _less_ clear?What is wrong with case List(x, y)?
How do you access more?
--RexP.S. Web interfaces can play all the IDE tricks. Who learns a language by typing around in text editors without expecting to be exposed to all the guts? If you want things pretty and highlighted and so on, you use an IDE.
On Wednesday, September 18, 2013 2:40:51 PM UTC+8, Rex Kerr wrote:On Tue, Sep 17, 2013 at 11:22 PM, Shelby <she...@coolpage.com> wrote:
And there is a reason why mathematicians don't just use + over and over again; sometimes you need to distinguish between different kinds of addition. (Note ⊕ used for XOR, which is addition-like.)
Now you are talking about overloading, and not compaction.Kind of. a + b + c where all types are the same either way but the +'s mean different things is incredibly confusing or just plain won't work.
But if you now need two plus-like symbols and you're only allowed one, you have thrown away compaction because overloading is impossible.I am trying to understand what you wrote. Perhaps your concern is similar to the famous Javascript standard library design error where if I understand correctly + is overloaded for both Strings and Number, yet there is an implicit conversion between both types, thus the overload is ambiguous when adding String and Number, yet Javascript silently chooses one of the ambiguous choices. I think there may be a tendency to conflate this implicits ambiguity problem with using + for the concatenation operator.
Or perhaps you mean that it is confusing when we need to know the types of the operands to know what plus is doing, but I think that is the case in many instances in idiomatic Scala.Or perhaps you mean where for any two operand types, we may need distinct additive semantics thus we need distinct operators. In this case, yes I agree but do you have any examples? I can't think of one of the top-of-my-head.
I think you're looking at this backwards. + is not a great thing to use just because everyone's familiar with addition. It's great because it is a great concept to have. You want that sort of convenience when dealing with something so fundamental yet powerful. It's so useful that we teach it to everyone.
But just because you decide + is useful for everyone, it doesn't follow that ++ should be avoided by all programmers. Instead, one should ask: is there a concept that we could powerfully associate with ++? It had better be +-like, or it will confuse everyone. But it had also better be very important to keep distinct from +.
And that is exactly what concatenation vs. addition of lists is: another kind of addition, one with different but equally valuable behavior.It is more confusing that + is overloading in some scenarios yet not in others.I can add two strings, I can add an element to a list, but I can't add two lists.Adding two strings is the weird one here.Is there any reason (besides the Javascript implicits ambiguity) why concatenation can't be thought of as an additive operation and thus use the + operator?
And ++ is traditionally a unary post and/or pre-increment operator, so that makes it very, very confusing for everyone coming from C-like languages (which is the mainstream).Yes, but unfortunately that operator is a really poor choice since it eats an extremely valuable symbol that can distinguish between addition/appending and concatenation.Why do we need to distinguish? As far as I can see so far, additive operation on two lists can't have any other meaning, unless it is a typeclass that only operates on Lists that contain elements which implement the + operator, then we define + as adding element-wise. But this is the wrong way to do it, since we should use Applicative for that any way since it is more general.
Which is important unless all your collections are invariant.I don't understand. Do you mean immutable, yet that would apply only to += not +.
Another option would be to abandon Copute's sugar for writing typeclasses, yet I find it enables the quite SPOT and concise syntax to specify the default typeclasses for a type. And it gives the illusion (for easier comprehension) that they are subtypes even if I change my proposal to not make them subtypes (so they can be reimplemented thus avoids the fragile base class problem).
On Wednesday, September 18, 2013 3:44:38 PM UTC+8, Rex Kerr wrote:Seq() + 5
What does this do?Seq() + x
What does this do (for any type of x)?Seq() + Seq(5)What does this do?Ah yes, seems I knew this in the past and forgotten the issue.As I explained in my recent post in scala-language, we can't prevent the input types of the method of a covariant collection from subsuming to Any unless we restrict the type of elements of the collection:So we can fix the problem above by forcing collections to have an element type restriction which is not Any, c.f. the C type parameter at my link above.
Being explicit wouldn't be so onerous and would be require rarely as far as I can see:Seq().append(...)Seq().concat(...)I am really thinking out-of-the-box. Nothing is sacred.Note I am thinking List(0, "", List(...)) is same as List(0, "") + List(...) which is the same as List(0, "") ++ List(...) because the non-null tail of a list is always a List. Nevertheless that doesn't hold true for other collections.
On Wed, Sep 18, 2013 at 1:32 AM, Shelby <she...@coolpage.com> wrote:On Wednesday, September 18, 2013 3:44:38 PM UTC+8, Rex Kerr wrote:Seq() + 5
What does this do?Seq() + x
What does this do (for any type of x)?Seq() + Seq(5)What does this do?Ah yes, seems I knew this in the past and forgotten the issue.As I explained in my recent post in scala-language, we can't prevent the input types of the method of a covariant collection from subsuming to Any unless we restrict the type of elements of the collection:So we can fix the problem above by forcing collections to have an element type restriction which is not Any, c.f. the C type parameter at my link above.Seq() + "5" vs. Seq() + Seq("5")Now it's AnyRef.Or maybe Serializable. Or Product."Not Any" really isn't a robust solution.
Being explicit wouldn't be so onerous and would be require rarely as far as I can see:Seq().append(...)Seq().concat(...)
I am really thinking out-of-the-box. Nothing is sacred.Note I am thinking List(0, "", List(...)) is same as List(0, "") + List(...) which is the same as List(0, "") ++ List(...) because the non-null tail of a list is always a List. Nevertheless that doesn't hold true for other collections.Now you have three things to remember: +, append, and concat. Before you had two: + and ++. Why is this better?
And you still have the problem of Product with Serializable, or whatever other ancestor happens to pertain to both branches.val o: Object = ...Seq() + oSeq() + Seq(o)Now what?
--Rex
Now you have three things to remember: +, append, and concat. Before you had two: + and ++. Why is this better?Astute that you deduced I would make the overloaded + perform differently than append and concat w.r.t. to subsumption.As far as I can see, the casual user will almost never use append nor concat directly, instead use the List.apply constructor and + when being explicit.
If we don't have the non-subsumed choice, then we can force
And you still have the problem of Product with Serializable, or whatever other ancestor happens to pertain to both branches.
val o: Object = ...Seq() + oSeq() + Seq(o)Now what?No problem, c.f. my link.
On Friday, September 13, 2013 2:34:58 PM UTC+8, Jason Zaugg wrote:On Thu, Sep 12, 2013 at 11:55 PM, Shelby <she...@coolpage.com> wrote:Even if working with Scala isn't the plan, it's still the JVM, so people *will* try to use it with other languages, and the type signatures are going to leak. So I'd encourage you not to turn these into Any (which is a gigantic red flag for me), but instead into some sort of wrapper that can enforce at least *some* good behaviour.How is subsumption to Any not a reasonable behavior? You are forced to specialize the type with match-case before you can do anything with it besides pass it along as an Any.I haven't been following this too closely, but I'll chime in here. Apologies is I've missed the point.Previous efforts to support unboxed unions (ie without using Either or hypothetical OneOf3, .., OneOfN. have run into problems with erasure. We've had a compiler around at one stage (caveat: said compiler was cobbled together in a bar by a certain profilic scalac hacker) that would type `if (true) a: A else b: B` as `A|B`. But when it comes time to typecase, you can only do so if the erased types of `A` and `B` are rich enough. So you can't write:def foo[A, B](ab: A|B) = ab match { case _: A => case _: B }
You'd need a scheme by which values of these types were at least boxed in `class Union[T](value: Any, tpe; TypeTag[T])` and where pattern matching could use those reified types at runtime to pick the right case.
* Items in the collections aren’t required to implement Ordered,
but Ordered uses are still statically type checked.
* You can define your own orderings without any additional library support
I am retracting the quoted prior post. On further thought (actually remembering my original logic from before I got sidetracked with illness), it appears Copute's design employing F-founded polymorphism instead of typeclasses is correct. Btw, I remembered that I had before concluded that Copute's design was F-bounded polymorphism, that isn't new to me, I just had forgotten.Referring to the `sequence` example in the quoted post, the problem with typeclasses when the list is heterogeneous, so the input implicit `tc` must have an untyped Monad[Any] or an infeasible type Monad[first-class-union-of-types-in-the-list]. Monad[Any] is unimplementable even if it employs a match-cases for known types there would still be a case _ => that must throw a runtime exception. And the variants of Monad[first-class-union-of-types-in-the-list] would be Cartesian product of existent types.