NumberFour / n4js

Type-Safety of Java & Javascript Flexibility
http://numberfour.github.io/n4js/
38 stars 8 forks source link

Afaics N4JS has no compelling market #266

Closed shelby3 closed 7 years ago

shelby3 commented 7 years ago

Per my reply to Jens von Pilgrim, I'm interested in (and I have a need for) a more soundly typed JavaScript variant than TypedScript.

However, I am questioning whether some of the design and target market decisions for N4JS can appeal to me or even to anyone who has a similar interest and need?

Meta: Please receive my criticisms as constructive, because I want to see what you all have to say and how it influences my logic on this issue. I am not writing this to criticize your admirable effort and technical excellence, but rather to determine how resolve the issues I have which are enumerated below. I am not trying to stomp on anyone's orderly orderliness here (not to insinuate that you all would be upset if I did), rather just being a voice from the outside to stimulate brainstorming about what is the use case and market for N4JS and whether some design decisions should change or not. Apologies given born and raised in the USA, although I have some German ancestry (Hartwick) I don't read or understand the German language, so all communication will need to be in English. Also note my avatar is a joke (myself at age 29), I am age 51 with 34+ years of programming experience. Apology if I start posting so forcefully without getting to know you all first, it is because I am very rushed and lacking time so please don't perceive me as an aggressive troublemaker.

I don't understand why I or anyone would to use N4JS instead of GWT (to compile Java) or Scala's Scala.JS compiler? Scala would give me the very desirable brevity of "everything is an expression" which JS doesn't have (yet).

But the reason I don't want to use Scala is because:

So why would I want to use a Java clone which:

What I think I want is a soundly typed typeclass language where "everything is an expression" which otherwise has a syntax and semantics that is as close to ECMAScript as possible, so that the emitted JS code is comprehensible when debugging in the browser even without source mappings. I want it to be able to interopt with JS's structural typing as well, when I want to use that paradigm instead of typeclasses. And it must be a self-hosted compiler asap, and it should not have any dependencies which are not self-hosted.

I've searched for such a language and I can't find it. I've found Haskell clones such as PureScript but that deviates too far from JS semantics and forces a Haskell like purity and monadic effect system.

I think to create a significant enough niche for new programming language, you have to design something that people want which they can't find with a more established choice. Otherwise it is very difficult for you to attract a community of supporters (especially the early adopters) and the network effects that scale up an ecosystem.

What we really need is the simple language I have described above. I think such a language would be incredibly popular over time. And I would certainly become an enthusiastic contributor. Also I would work to popularize such a language by publishing and marketing popular ecosystems that featured this language. I've done million user software more than once in my career. (and I would help to select a more marketable/brandable name than "N4JS")

Thoughts?

spion commented 7 years ago

PureScript doesn't really force purity, it just supports monad syntax extensions. Async IO in JS already forces "monadic code" (with sugar equivalent to do syntax). Sounds like PureScript is a better starting point for you?

shelby3 commented 7 years ago

@spion write:

Sounds like PureScript is a better starting point for you?

Use site syntax

How can it be a starting point when it doesn't even use parenthesis to tuple the function arguments as JavaScript and C-like languages do? That is so foreign to me, because it requires I know how many arguments each function takes at the declaration site, in order to read the usage of the use site.

Although I could write all function applications surrounding with parenthesis, e.g. (max 1 2) instead of the familiar max(1, 2), it is confusing for my second nature that f (9) is not equivalent to f(9) when there is another argument following, e.g. f (9) 8 is equivalent to f(9, 8) except when there is another prefix function identifier such as g f (9) 8 which is equivalent to g(f(9), 8) if g takes two inputs and g(f(9, 8)) if g takes one input.

Global type inference

PureScript apparently has global type interface, which I explained to before, afaik prevents implementation of a typeclass in more than one way.

Confirmed:

Overlapping instances are still disallowed, like in Haskell.

In Haskell, an instance is an implementation of a typeclass.

That is is unacceptable to me and besides I want to declare types on public functions and methods because it maintains binary compatibility of the API when incidental refactoring are done (which in global inference can cause a cascade of inferred type changes every where), which is essential for separate compilation modularity. Haskell (and thus apparently PureScript) breaks all these.

No Anonymous (Structural) Unions

PureScript has product and sum types, but they are nominal. The inference engine can't create an anonymous structural sum type (union) in order to model for example the fact that the logical || JavaScript operator can return a type other than Boolean as explained on page 8 of the white paper for N4JS. Without anonymous unions boilerplate proliferates such as for example @keean's HList example of how to implement extensible heterogeneous collections.

(to be continued, I'm having a flare-up of my autoimmune illness and thus am temporarily unable to read...)


PureScript's forall higher-ranked types can in some cases be simulated with interface (or trait) method type parametrization.

spion commented 7 years ago

The PureScript community is somewhat interested in working on modular typeclasses / modular implicits.

Another option that fits your requirements better is BuckleScript, a clean ocaml-to-js compiler. OCaml are working on adding modular implicits to the mainline language already.

yoosiba commented 7 years ago

TLDR;

@shelby3 thanks for your intrest in N4JS. It would be nice if you would use our language in your project. If you decide not to, we would appriciate to share reasoons why. This would be valuabe input to design of the N4JS. Still, it seems impossible to design any non trivial project, like programming language and dedicated tooling, to everyones individual liking.

The Rant.

Meta: Note that my comments are just my personal comments as one of the co authors of the N4JS. I am just a humble developer, not a NumberFour spokes person. Also, as someone born and rised in Poland, I don't speak german so I appriciate you initiating discussion in english. In general me and my colleges we do all our work in english, and we do not expect anyone to use german or non english when discussing our work.

I would like comment on few things you have mentioned.

"N4JS has no compelling market"

As for N4JS market this is twofold topic. Firstly understand, that N4JS is not the main product of the company. See our web page We are starting with a great collection of small, versatile apps that come together in a single business platform. The company is building business platform and ecosystem of applications. The platform (to some extent) and the applications are being built using N4JS. If you think about N4JS only as internal tool - it has 100% of the market.

Now from the language site JavaScript is the best language for external developers to contribute to our platform but we needed important features (...) and that's why we developed N4JS to provide these missing features. So if the number of apps available is supposed to grow, possibly in cooperation with partners, and even external developers, than it makes sense for N4JS to be publicly available. We want the best experience for people working on our platform, regardless if it is internal developer, or... you.

If N4JS would be the only thing we do, and if our goal would be only to replace TypeScript/PureSecript/VanillaJS/(insert your favourite compile to JS language) then it would be a challenge (i.e. questionable product idea). But it is not the case. N4JS is our internal tool, used by our developers and our partners to work on our platform. We made it public because to make it available for people contributing to our platform, and for everyone that wants JS based ecosystem for their projects, but are missing (similar as we) features.

Note, that it is not uncommon. Take FlowType by Facebook as an example. I am sure it helps them with their platform. They made it publicly available, so anyone can use it for their project. I don't know what 'market share' it has in JS landscape. I assume not so big, still it made sense for them to make it public, still people use it in their non Facebook related projects.

"why I or anyone would to use N4JS instead of GWT"?

GWT is interesting example, we have used GWT internally for some time (~3-4 years or so), but that approach had it drawbacks for us. Issues with modifying type checking or inference, type declarations in comments, maintenance of our extensions/modifications across different GWT releases, building tooling (i.e. IDE) on top of that without proper APIs... at some point it stopped making sense for us. We could have switch to something else like mentioned FlowType, Scala.js or something else but that approach would have the similar problems. We have decided to go created N4JS, because it was best approach for us. We invite everyone to use N4JS, but it is personal decision.

"doesn't offer everything is an expression"

That is nice feature, but as TypeScript developers answered that is not in JS, and adding that would violate their design goals. For us similarly, that is large semantic deviation from JS, that doesn't bring that much value. If this will become part of ECMAScript, then it will (most likely) be added to the N4JS.

"too many paradigms"

That is unfortunate case of many languages. Personally I think mixing class based OO and prototype based OO JavaScript is one those cases. One can either design strict language that many dislike for its strictness or design with flexibility others dislike. Balancing that out is not easy, and it is moving target (trends change, community changes) and conflicting goals (strictness-flexibility, brevity-readability, novice programmer - seasoned programmer, etc.).

"The emitted JS is somewhat incomprehensible in the browser's JS debugger"

There is a lot of effort on our side to avoid that. We emit JS that is debuggable in the browser. On top of that we emit source maps, to allow you debug N4JS directly in the browser. There is some amount of 'noise' that cannot be removed now (until certain JS features are widely supported) or without the tradeoff (loosing backwards compatibility) or without cutting some features (like some reflection api).

"I'm forced to use an IDE even for simple scripts"

Well, no. For any language that is not based on projectional editor (i.e. does not include elements that are not within ASCII/Unicode/etc.) you can use text editor of your choice. As long as you can type it in some text editor, you don't have to use any "IDE". On few occasions I was editing N4JS in the browser directly on GitHub. As long as there is some compiler (i.e. text parser + other parts) to process your code, you can use any text editor.

"It doesn't have typeclasses and I now think subclassing is an anti-pattern"

I am not sure if there are OO languages that have type classes, it seems to be more Functional Programming thing. It is interesting question where JS stands? It is functional, but everything is an object and now it has classes. Maybe it is "too many paradigms", but lets leave theoretic discussion aside.

If you don't like class based inheritance, don't use it. Composition and delegation over inheritance. Use interfaces to define types. In your classes implement multiple interfaces but don't declare superclasses. This way you have no class inheritance, but you get type composition. If you need instances of the same class to use different method implementation, parametrise delegation. This will work, in N4JS, or even in Java. Not everything will be (nicely) supported by the tooling of the language, some things will look awkward (especially to people expecting class based inheritance), but you don't have to use every single feature of the language. Your code, as long as compiler accepts, you can do whatever you want.

Look at emulating type classes in F# Look at ObjectAlgebras with Dart

"emulates Java's lack of brevity"

Well that is a bit unfair, in the end we don't do this on purpose. Everything has to be written down, except things that can be somehow deduced. Type inference allows to skip type declaration, default access modifiers allow to skip access declaration, etc. We will improve type inference, but there will be places where you have to declare some types. Maybe default access modifier should be 'public', I personally like that idea. Real question is what features we need for desired level of brevity (which is subjective). Each mechanisms like that makes tooling more complicated and slower (maybe not much, performance dies by a thousand cuts). Other problem is that allowing to mix many terseness tricks leads to terrible code. On few occasions was amazed how succinct code I can write with Xtend, but in many of those cases I had hard time reading it some time later. Not to mention problems with debugging which forced me to re-write given code to "boring" verbose code. Balancing brevity and readability is matter of taste with real cost in complexity.

"forces me into the Java ecosystem of IDEs and even the source code of N4JS has many Java dependencies"

No and yes. As a N4JS user you don't have to deal with Java. I have mentioned before you can skip using our IDE, and use any text editor. IMHO it makes things harder, but you can do it. You need to have Java when compiling, but you don't have to deal with it directly. Look at our tasks example Our compiler is java based but we wrap it in npm package, so as long as you have node+npm+java, you only invoke a shell script that invokes build process. Or if you look at the branch in my fork you can skip platform depended shell scripts and invoke build process with npm and pure JS.

As a N4JS contributor you have to deal with Java directly. It is required when developing the language and tooling. We did this because we could take advantage of powerful tools, that simply don't (or didn't) exist in JS ecosystem. If we wouldn't go that way, we would have to reimplement lots of things on our own in JS. Once we have that in place we could re-implement that in N4JS. There is a lot of things we could but much less we can do. We are team of few people, getting here took us over 2 years. We think about re-doing some parts in N4JS, but this won't happen over night, and not everything at the same time.

"it must be a self-hosted compiler asap, and it should not have any dependencies which are not self-hosted"

Well, self bootstrapping is not absolute "holly grail". Not everyone agrees with that. Also how do you define self bootstrapping? TypeScript has JS dependency, which implies some JS engine dependency. It also has some IO dependency. This implies some operating system dependency. All of that is written in number of languages. Even if you don't have to deal with them directly then you have runtime dependency to their binaries. One can go (extreme) with unikernel approach (which you can with runtimejs, but how reasonable that is? We have to live with our dependencies, the best we can do is minimise them to the smallest possible number of runtime, binary, dependencies, that are convenient to get. If N4JS compiler gets N4JS implementation (which I hope will happen) than your node+npm+java runtime, dependencies become node+npm. Not much difference for N4JS end user. When that happens, it doesn't get more self hosted, unless you define self hosted in the way, it supports your liking. You can tinker with definition to say it is self hosted right now.

P.S.

Wow, that was a long rant.

P.S.S.

It would be a hassle to rename language now, but please propose more marketable/brandable name. For a good name it might be worth to do it.

shelby3 commented 7 years ago

@yoosiba thanks for the reply. You have some good points which I roughly agree with, and I will respond on those in more detail later.

For now, I want to emphasize the most important point.

Specialty (novelty) programming languages with small ecosystems die because nobody wants to invest their man-hours in a syntax that will become orphaned. Thus I am thinking you need to make your language popular enough that the open source resources will come in to refine and maintain it.

For that goal to be achieved, I am going to strongly suggest you implement typeclasses cleanly and elegantly, so that you have a competitive advantage that NO OTHER PROGRAMMING LANGUAGE has. There is no strict evaluation, optimally targeted to JavaScript, with structural first-class unions, and typeclasses language on earth right now. The closest is PureScript, which has several flaws making it unsuitable for those who want to write in a syntax and semantics as close to EMCAScript as possible while gaining the best typing advantages.

I want to convince that I believe that typeclasses may be the "killer feature". Apple's Swift has implemented them (sort of) and is leading the way. Rust has adopted them but conflated it with other paradigms (global lifetimes tracking) which are unsuitable to the mass market. I believe typeclasses are going to be the next big hot language feature, because they enable extensibility on both axis of Wadler's famous Expression Problem.

Please read my TypeScript Issue thread which explains typeclasses in great detail.

I think if N4JS were show willingness to implement typeclasses, I would likely throw all my support effort to N4JS. I can't speak for @keean (he is a CTO in the UK and co-author of HList paradigm), but I have invited him to this thread.

I will be brainstorming on language names.

I am not trying to push you to do anything. I just hope you will feedback to me why and what fits and doesn't fit your strategies. I am trying to decide what I should do. Do I have to go write my own language? I hope not.

Note the above is an oversimplification. Many details need to be looked at. I need to better understand your goals and rules in terms of what you will and won't add to your language. I need to better understand where the feature set of N4JS is now and the rate of progress. I need to better understand if I would be able to quickly come up to speed on the contributing to the development of N4JS and in what areas of the project.

So the above is just to elicit any off-the-cuff feedback or discussion you would like to enjoin.

yoosiba commented 7 years ago

This discussion is broader than just type classes. You might want to create separate feature request for that purpose only.

Do I have to go write my own language?

This might delay projects in which you intend to use language with type classes ;)

BTW, below is N4JS version of the code proposed by @keean in the TS thread


interface Identifable {
    as_string() : string
}

interface ICar extends Identifable {
    revision :string
    @Override as_string() : string {
        return this.revision
    }
}

interface IAnimal extends Identifable {
    species :string
    @Override as_string() : string {
        return this.species
    }
}

interface IDog extends Identifable, IAnimal {
    breed :string
    @Override species = 'mammal';
    @Override as_string() : string {
        return this.breed
    }
}

class Car implements ICar { @Override revision = 'caddy'; }
class Animal implements IAnimal{ @Override species = 'not only mammal'; }
class Dog implements IDog { @Override breed: string = 'pomeranian'; }

function <T extends Identifable> logIdentity(x : Array<T>) {
    x.forEach(element => console.log(element.as_string()))
}

let all :Array<Identifable> = [new Car(), new Dog(), new Animal()];
logIdentity(all);

interface ICat extends Identifable {
    name :string
    @Override as_string() : string {
        return this.name
    }
}

class MyCat implements ICat{
    @Override name : string = "Widget"
}

all.push(new MyCat());
logIdentity(all);
keean commented 7 years ago

I am sure you realise but the above code lacks the extensibility of the code I posted. With the type-class examples any of the objects (say MyCat) can be extended to incorporate new behaviour without rewriting the class or interface definitions. This can be done by implementing an interface on an existing type, even a primitive type.

shelby3 commented 7 years ago

@yoosiba with your design pattern, if we defined a new trait interface, we wouldn't be able to implement Car on it without changing the source code for Car. That violates the extensibility requirement of Wadler's stated Expression Problem.

Typeclasses are a very exceptional advancement over subclassing. Subclassing is really rigormortis refactoring hell (which is probably why you need those superfluous noisy @Override annotations to protect against refactoring mistakes). I think you would benefit for programming your apps with typeclasses.


Edit: it wasn't my intention to write the above with any implied blame. I was suffering a health ailment while writing the above and simply wasn't able to filter my words for congeniality. My intent is to try to convince about achieving greatness (together! :) and also I agree with your point that we must declare that which the compiler can't infer and/or which we don't want to change when there is refactoring. My intent about brevity is avoiding declaring that which is arguably unnecessary, e.g. public by default. You probably know that Eric S. Raymond has a 166 IQ and is the progenitor of the term "open source" (as opposed to Richard Stallman's "free software" copyleft viral licenses which scared away the corporate s/w realm) in his famous The Cathedral and the Bazaar essay, and I will again link to his recent blog about the fact that globally enforced software development methodologies do not scale. He is the guy who pointed out that decentralized open source is the first software engineering paradigm that scales positively (as opposed to the Mythical Man Month which says that adding more top-down managed programmers scales negatively due to the communication load).

Any software-development methodology works well on sufficiently small projects, but all scale very badly to large ones. The good ones scale slightly less badly.

One thing all methodologies tagged “agile” have in common is that they push developers away from the median. Competent developers work better; mediocre developers don’t change; incompetent ones get worse.

Software metrics have their uses. Unfortunately, their principal use is to create the illusion that you know what’s going on.

Structured development methodologies have their uses, too. Unfortunately, their principal use is to create an illusion of control.

Agile development is efficient only in environments where the cost of flag days is low. Otherwise, slow down, and take time to think and write about your architecture.

Good programmers are difficult to control; great ones are nearly impossible to control. Different methodologies require different kinds and degrees of control; match them to your developers wisely.

Process is not a good substitute for judgment; when you have to use it as one, your project is probably too large. Sometimes this is unavoidable, but don’t fool yourself about what that will cost you.

The difference between O(n**2) and O(n log n) really matters. It’s why lots of small teams working coordinated small projects works better than one big team building a monolith.

A million dollars is roughly a 55-gallon oil drum full of five-dollar bills. Large-scale software development is such a difficult and lossy process that it’s like setting fire to several of these oil drums and hoping a usable product flutters out of the smoke. If this drives you to despair, find a different line of work.

Also the highly respected Paul Graham writes to design a programming language for the best programmers because they are the ones who be the most productive and determine the success of your language:

And when I say languages have to be designed to suit human weaknesses, I don't mean that languages have to be designed for bad programmers. In fact I think you ought to design for the best programmers, but even the best programmers have limitations. I don't think anyone would like programming in a language where all the variables were the letter x with integer subscripts.

2​. Design for Yourself and Your Friends.

If you look at the history of programming languages, a lot of the best ones were languages designed for their own authors to use, and a lot of the worst ones were designed for other people to use.

When languages are designed for other people, it's always a specific group of other people: people not as smart as the language designer. So you get a language that talks down to you. Cobol is the most extreme case, but a lot of languages are pervaded by this spirit.

It has nothing to do with how abstract the language is. C is pretty low-level, but it was designed for its authors to use, and that's why hackers like it.

The argument for designing languages for bad programmers is that there are more bad programmers than good programmers. That may be so. But those few good programmers write a disproportionately large percentage of the software.

I'm interested in the question, how do you design a language that the very best hackers will like? I happen to think this is identical to the question, how do you design a good programming language?, but even if it isn't, it is at least an interesting question.

3​. Give the Programmer as Much Control as Possible.

Many languages (especially the ones designed for other people) have the attitude of a governess: they try to prevent you from doing things that they think aren't good for you. I like the opposite approach: give the programmer as much control as you can.

4​. Aim for Brevity.

Brevity is underestimated and even scorned. But if you look into the hearts of hackers, you'll see that they really love it. How many times have you heard hackers speak fondly of how in, say, APL, they could do amazing things with just a couple lines of code? I think anything that really smart people really love is worth paying attention to.

I think almost anything you can do to make programs shorter is good. There should be lots of library functions; anything that can be implicit should be; the syntax should be terse to a fault; even the names of things should be short.

And it's not only programs that should be short. The manual should be thin as well. A good part of manuals is taken up with clarifications and reservations and warnings and special cases. If you force yourself to shorten the manual, in the best case you do it by fixing the things in the language that required so much explanation.

5​. Admit What Hacking Is.

A lot of people wish that hacking was mathematics, or at least something like a natural science. I think hacking is more like architecture. Architecture is related to physics, in the sense that architects have to design buildings that don't fall down, but the actual goal of architects is to make great buildings, not to make discoveries about statics.

What hackers like to do is make great programs. And I think, at least in our own minds, we have to remember that it's an admirable thing to write great programs, even when this work doesn't translate easily into the conventional intellectual currency of research papers. Intellectually, it is just as worthwhile to design a language programmers will love as it is to design a horrible one that embodies some idea you can publish a paper about.

It is not my aim to remove the compiler's checking for correctness. Whereas, it is my aim to use clever to design of a programming language to make some checks irrelevant, e.g. afaics the need for @Override largely disappears when we implement interfaces orthogonally from each class, because there is not global rigor mortis refactoring pressure in the orthogonal nature of typeclasses as opposed to the jail of subsclassing. Typeclasses enable a modular and localized set of dependencies, so that one programmer's mistake in his module doesn't cascade into refactoring mistakes throughout the ecosystem.

P.S. anyone who figures out how I made the numbered list above display correctly in markdown starting from "2" instead of "1" is a hacker, hehe.

shelby3 commented 7 years ago

@keean and @yoosiba I have made a strong logical argument that if TypeScript or N4JS can add private members for classes, then adding typeclasses is the same category of "type system behavior" which EMCAScript will never be able to add because runtime encapsulation of class private members is impossible for the EMCAScript Private Fields proposal to implement without creating massive programmer error (thus not encapsulation because there will be leakage due to this programmer error). Some features can't be done without static typing and runtime secure data encapsulation across all class instances is one of them.

keean commented 7 years ago

@shelby3 I have decided to start a new project to develop a simple trait based, single-paradigm compile to JavaScript language for data-type driven generic programming as defined in "Elements of Programming" by Alexander Stepanov and Paul McJones . I would very much appreciate the input of anyone who is interested in trying to create something simple, type-safe and expressive, and I have created an initial issue to discuss the big-picture stuff here: https://github.com/keean/traitscript/issues/1

yoosiba commented 7 years ago

@keen I know the difference between mine code and your proposal. It is just something I would do in N4JS if I would want to add new behaviour to the class without touching the class and without adding the superclass. Before N4JS we used solution that allowed thing you are proposing with Role (on class) and Trait (on instance) by using Joose object system for JS . Check it out, maybe it will give what you are looking for. Or look presentation at ObjectAlgebras with Dart I have linked before. You might go that way with N4JS or TS.

N4JS doesn't have type classes. At least for now, in N4JS you can't modify type structure from the outside of that structure. We have static polyfils, but that is a bit different see 11.4.2 Static Polyfill Definitions in our spec (pdf)

spion commented 7 years ago

@shelby3 regarding PureScript:

g f (9) 8 which is equivalent to g(f(9), 8) if g takes two inputs and g(f(9, 8)) if g takes one input.

These sort of statements clearly show that you haven't even tried to use the language. Please do that and find out in what way you are wrong.

shelby3 commented 7 years ago

@yoosiba apparently Joose traits bind new behavior to the instance. Afaics, that is roughly the same as extension methods, which I explained are not the same as typeclasses. Typeclasses enable varying the behavior at the function invocation (application) use site.

Object Algebras bind the data types to each operation, e.g. the construction of a literal data object is in the context of IPrint or IEval separately and that data for literal in each can't interact with the other. Thus it doesn't solve Wadler's Expression Problem, because the variants aren't independent of the operations. Yes we can add variants to an operation independent of adding operations to a variant, but the variant data is conflated with each operation dependently.

abstract class ExpAlg<E> {
  E lit(int n);
  E add(E lhs, E rhs);
}

abstract class IPrint {
  String print();
}

class PrintExp implements ExpAlg<IPrint> {
  @override
  IPrint add(IPrint lhs, IPrint rhs) => new PrintAdd(lhs, rhs);
  @override
  IPrint lit(int n)  => new PrintLit(n);
}

class PrintAdd implements IPrint {
  IPrint lhs, rhs;
  PrintAdd(this.lhs, this.rhs);
  @override
  String print() => lhs.print() + " + " + rhs.print();
}

class PrintLit implements IPrint {
  int value;
  PrintLit(this.value);
  @override
  String print() => value.toString();
}

makeExp(ExpAlg a) => a.add(a.lit(1), a.let(2));
print(makeExp(new PrintExp()));
print(makeExp(new EvalExp()));

abstract class MulAlg<X> extends ExpAlg<X> {
  X mul(X lhs, X rhs);
}

class PrintMulExp extends PrintExp implements MulAlg<IPrint> {
  @override
  IPrint mul(IPrint lhs, IPrint rhs) => new PrintMul(lhs, rhs);
}

class PrintMul implements IPrint {
  IPrint lhs, rhs;
  PrintMul(this.lhs, this.rhs);
  @override
  String print() => lhs.print() + " * " + rhs.print();
}

makeMulExp(MulAlg a) => a.add(a.lit(1), a.mul(a.let(2), a.let(3)));
print(makeMulExp(new PrintMullExp()));

Additionally without mixins providing multiple inheritance for extends a la Scala linearilization, the Object Algebras bind the inheritance hierarchies together, thus making it impossible to reuse variants in all permutations of possible variants that a function might want to constrict its input argument type to.

Additionally Object Algebras appear to require the boilerplate of manually dispatching each operation to its nominal member, e.g. IPrint.print() and IEval.eval(), i.e. the function that wants to call IPrint.print and IEval.eval must manually track either two input arguments (e.g. print and eval, thus print.print() and eval.eval()) or one object with two members (e.g. arg.print and arg.eval, thus arg.print.print() and arg.eval.eval()).

And this doesn't even touch on the concept of virtualized typeclasses, aka trait objects in Rust.

Afaics, there isn't any substitute for typeclasses. We can't emulate them with subclassing and generics.

shelby3 commented 7 years ago

It is amazing how much energy a young guy has to waste on unproductive ego battles. When I was his age, I was too busy coding and there wasn't any Internet to draw me away from production.

@spion wrote:

g f (9) 8 which is equivalent to g(f(9), 8) if g takes two inputs and g(f(9, 8)) if g takes one input.

These sort of statements clearly show that you haven't even tried to use the language. Please do that and find out in what way you are wrong.

Did you entirely miss the point of the comment post you are quoting from out-of-context? Or are you just trying to win some ego contest? (I presume both)

The statement clearly shows that I am making a generalized statement about difference in grammar between mandated parenthetical grouping of arguments and otherwise.

It is irrelevant to my generalized point, the left-to-right or right-to-left precedence choice for function application of the language which is the aforementioned "otherwise" case. And other factors such as the type signatures of g and f. That precedence direction and argument grouping dependent on types is not explicit on function application is another one of its flaws, as it adds cognitive load.

But nice try any way. Keep digging for irrelevant details and finding the bark instead of the way through the forest.

If you phrase your correspondence in an amicable and open-minded manner seeking production and constructiveness, then so will I. If you seek to destroy and belittle, then you will be looking in a mirror and eventually simply ignored. I received too much hate from several of you guys from the TypeScript community. Sorry if I don't agree with its unsound type system. Let the "language wars" begin, if that is your chosen mode of interaction.

These sort of statements clearly show that you haven't even tried to use the language.

Damn right I don't have time to try every language that is unsuitable for my needs. I never claimed I used PureScript. Heck I haven't even written any Haskell for some years now. I am trying to get something accomplished before hell freezes over. But if I write down precedence ordering that is different from a particular language or set of languages in my haste and sleeplessness, while also not diminishing the point I was making, then what utility does it serve to try to ridicule me with an irrelevant detail. You could simply point it out without trying to implying that I haven't done something that I should do (use the language). What gives you the omniscience to choose my priorities?

I'll remember how to treat you when you have Multiple Sclerosis and struggle to have the energy to get all the various things done that you want to get done and used to be able to do before you got chronically ill. You inconsiderate fool. You have no fucking clue what I am dealing with on a daily basis. Try doing what I am doing with the brain fog I have. Imagine your head wants to be on the keyboard and you can barely hold your eyes open. I deal with this during most of my waking hours. I don't go around making excuses but it just shows how socially inept you are about things that other people may be going through in their life. It was so bad yesterday that I couldn't even make it through the video on Object Algebras. My eyes couldn't even focus on the code samples. Was a struggle to get through the video when I awoke. I might feel reasonably good for a couple of hours a day and other hours I am struggling to have any semblance of normal cognition and function. If you have no idea what Chronic Fatigue Syndrome is, what is does in reality, and the other symptoms of autoimmune disease, then you have no way to understand.

Do you have any clue what the stress you dish out does to a person with MS? You are actually using a weapon which has physical ramifications. Warning:

https://en.wikipedia.org/wiki/Cyberstalking#Distinguishing_cyberstalking_from_other_acts

Apologies to N4JS for the choice of words. But some of these TypeScript packdogs (and it by no means appears to be representative of all TypeScript community members, although these few bad apples originate from there) just don't seem to get the clue, no matter how many times I have hinted to them to please stop the trolling.

I told you several times that I am interested in your technical details. But you are always trying to find a way to ridicule me and fault me for not being an expert user of every possible language which I comment on. That is your agenda. It is not objectivity. It is a political agenda. Engineering is about stating facts, and there is no benefit to injecting ad hominem agendas. That crap destroys production.

I suppose you are trying to get revenge for where I pointed out that you conflated subclassing and nominal typing, and yet you continued to deny it like a weasel. And do notice that I was considerate to you when I pointed out your error.

You guys push me and push me until you can bait me into writing something like this. I try and try to ignore your continual trolling.

I will need to find a place to have discussions about language design where people are considerate of each other.

@spion I realize you are probably too young to understand wisdom, but again I will advise you to read and understand the following. It is written by a man with a 166 IQ who coined the term "open source":

Ego is for little people

The B-list people, the bright second-raters, may be all sharp elbows and ego assertion, but there’s a calm space at the top that the absolutely most capable ones get to and tend to stay in.

When you are ready to mature and focus on interesting engineering discussions without the ad hominem noise, then I will be happy to enjoin you.

JensN4 commented 7 years ago

I'm going to close this issue. That doesn't mean that I (or the N4JS team) will ignore the topics discussed here. I'm closing this issue for the following reasons:

  1. As already explained in #265 I want to separate bug reports and feature requests from discussions, mainly for project management reasons. "Afaics N4JS has no compelling market" is neither a bug report nor a feature request.
  2. The topic discussed in this issue is rather about typeclasses than N4JS's compelling market. This can be treated as a feature request. Typeclasses really look like a cool and powerful concept, and I really appreciate the discussion about that. However adding this to N4JS would be quite tricky (i.e., lot of work), e.g., combining that with generics, nominal and structural typing, primitives vs. objects, scoping etc. Unfortunately we (the N4JS team at NumberFour) do not have the resources to add this feature at the moment.
  3. The nature of this discussion has become too personal and inappropriate for such a forum.
shelby3 commented 7 years ago

@JensN4 regarding my reply to you and your follow-up reply at the Google group for Google's StrengthenJS initiative, I hence explained what I meant about the solution to eliminating covariance with type-classes. I'm not sure if I am correct, so if anyone can point out a flaw in that design reasoning, please do.

  1. I replied at #265.
  2. Yep. It would really be better to start from scratch and not try to support all the other paradigms which N4JS does. I am coming to the realization that @keean and I have to create a new language. I was trying to find a way to avoid it. I really think subclassing needs to be removed from the language and I now view it as an anti-pattern virus (lol my penchant for drama, and it seems to offend but heck that is why I spent some past years thinking about the correct way to do extension before throwing a lot of resources into some language project). Am I incorrect? Perhaps (or even likely, except we are borrowing from the design of Haskell). Having an implementation of what I envision would help to start the comparison. Perhaps the upside for you all may be if you also in parallel test out our new language and then over time decide where to prioritize your resources? (presuming @keean and I have the resources and garnish enough outside interest to even get the project to alpha stage, which I am not yet sure about)
  3. Nothing I can add that I haven't already written. I did apologize. I guess I can add that I learned it is better to never propose contentious features in someone else's project. Go create my own project instead. Open source doesn't mean we don't have to be mavericks when we want to go against the prevailing inertia (but how can I be a maverick when I am not even healthy and can barely think much of the day). I was really hoping I could leverage someone else's investment and prior effort. But apparently I can't.

Edit: oh and back on the title of the Issue— the compelling market for N4JS. On further thought, the market is probably corporations who have some need to do soundly typed JS development, but don't want the lossy dissonance of Java →GWT →JS. Corporations are more likely to view subclassing and agile development principles within that prehistoric coding paradigm to be of great value. Whereas, the language @keean and I are potentially designing would be the cutting edge, but possibly even mainstreaming a unification and simplification of concepts from functional programming, Haskell, and making it closer to JavaScript. Let's see what comes. P.S. I am not trying to advertise in your project space, just my rebellious penchant for hyperbolic, colorful words and deprecating hacker humor (which seems to get me into trouble).

shelby3 commented 7 years ago

@yoosiba and @JensN4 , you may find interesting or useful this summary of variance issues w.r.t. to subclassing, typeclass objects, and my new union solution to the Expression Problem.

shelby3 commented 7 years ago

Thought of a potential hack to simulate typeclasses in N4JS.

shelby3 commented 7 years ago

FYI, we already bumped into TypeScript's unsound "bivariance" (example in TypeScript's Playground provided). N4JS considered as an emit target. :grinning: