Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This truly means we still lack a programming language with:

    1. reasonable support of object oriented programming.
    2. reasonable support of functional programming.
    3. solid concurrency features.
    4. runs comparable to native code.
    5. simple to learn.
Even though it is not difficult to create such a language but we see a new language poping out every now and then and none of them try to solve these issues.


What I'm looking for in a language:

1. Solid mix of functional programming and Object oriented programming (though FP is more important - I'd gladly give up inheritance if I get good support for interfaces and traits instead)

2. Static type checking

3. Good support for efficient immutable data structures.

4. Solid concurrency features

5. Easy cross-compilation to and interaction with JS

5a. Cross-compilation to iOS and Android would be nice but it's not a dealbreaker

6. Good tooling (i.e. IDE-support, package manager)

7. A solid standard library

8. An active open source community around it

9. Said open source community should contain a good web MVC framework (or sensibly pluggable modules to the same effect)

10. A REPL

11. Sensible compilation times

So far it looks to me like only Scala and Haskell are candidates - and it sounds like Scala really struggles with the compilation times. If Reason/ocaml gained more traction it might become a contender. And if I gave up on static type checking then Clojure would be an option. And if I settled for a sprinkling of functional programming instead of having that be the basis, then Kotlin or maybe C#. Any other suggestions?


I think you've covered the main options. At a guess: Maybe F# if you're willing to go with a smaller open-source community. Maybe Rust or Idris if you're willing to deal with something a bit less mature with less tooling. Maybe Swift, Crystal or Nim, though I know less about them and their communities seem smaller.


Sounds like OCaml to me, although I'm not super experienced with it. Not sure exactly how concurrency is handled.


Besides the options you mentioned I really think Typescript is a high contender on that list. Even though it's just Javascript with typing it ticks most boxes you mention. Depending on how you value some features (e.g. nr. 5) it might even be the best option.


Its FP support is even more limited than C#'s, no?


Imho they are about the same, or maybe TS is even a little bit better, since you can really have freestanding functions (C# has either (static) member functions or delegates). The Javascript ecosystem probably also leans a bit more towards FP style in the mainstream (with lodash, ramda, redux, etc.) than C# does. What are you missing in particular? I guess the main thing for both is tail recursion.


Does TypeScript have any kind of "do notation" or "comprehension" syntax? LINQ is not the best by any means but it does seem like a big improvement over nothing.


No, it doesn't. I guess that's because monads are not considered idiomatic typescript. And for the special case of async programming there's async/await support.


This is basically what I'm looking for too.

For me, ES6+ is near ideal in terms of syntax and features if it had parallelism support.


I've heard that Haskell also has a compile time problem. And swift does too. :(


These all exist, you just don't agree with them being reasonable:

Scala, Common Lisp, C++, D, Clojure, F#, OCaml, Kotlin, etc. Those could all count or not depending on your subjective opinion.

The truth is, it is difficult to create the perfect language, and any sufficiently complete language will always start to have part of it that suffer in exchange.


> Scala, Common Lisp, C++, D, Clojure, F#, OCaml, Kotlin, etc.

Out of those, only OCaml really provides “reasonable” support for functional programming. The ability to manipulate compound values (say, lists or trees) directly, without using objects having a queryable physical identity, is of course a prerequisite.

> it is difficult to create the perfect language, and any sufficiently complete language (...)

Maybe the excessive amount of features is the problem in the first place.


While OCaml might be the most reasonable in theory, AFAIK opam on Windows still doesn't work. Also its standard library is too minimal for my liking.


User catnaroek was only talking about support for Functional programming. And they're right: it's hard to reasonably support typed functional programming without sum types.

And we're talking about a language dammit, not libraries or package managers. Let's have a good language first, then build the tools around it.


It's even worse: it's hard to reasonably support functional programming, typed or untyped, when list and tree values aren't directly expressible in the language's semantics. If you need a social convention not to query physical object identities, you have already lost.


The lack of a good standard library can always be fixed: write one. OTOH, language semantics issues can't be fixed without redefining the language, invalidating all code that has already been written in it.


F# allows you to distinguish values by identity?


You can always query the identity of the .NET object that underlies a putative compound value.


> even though that's not difficult

Turns out to be quite arduous:

- You ask for reasonable support for both object-oriented and functional programming, this already contradicts with the notion of state (that objects suppose to contain in OOP) and "stateless" ways of FP, let's ignore that for a moment

- FP means different things for different people - some would argue it has to have well thought type system, but what exactly that means? Is strongly but dynamically typed like in Clojure is okay (with added sugar like Clojure.spec)? Or it has to have static types? Does it have to have then type inference like in Elm? Or definitely it has to have algebraic data types like in Haskell? Or maybe dependent types like in Idris?

- Concurrency models vary too, for example: STM in Clojure and Haskell aren't exactly the same. Actor model in Erlang is a beast and sounds awesome on paper, but in real life process coordination may become a headache. Primitives based on CSP model are also known to have their own quirks. Concurrency in any language is never simple and straightforward.

- You're asking for an fast FP language - with immutable data structures and lazy/non-strict evaluation it's probably unrealistic to get it to run as fast as native code

- And "simple to learn" is also very subjective quality - for some Python is easy, but Clojure is hard. For some Haskell is not that difficult as people describe it. For some it feels like insurmountable mountain.

As you can see - it's not that simple. And we probably not gonna get a "perfect" language that suits everyone anytime soon.


I would argue Scala already has all of them but the last one. However there are shit load of absolutely unnecessary features in the language. As the parent link states, twitter just removed shitty features and there you go: An easy to use Object/Functional language which runs fast and has awesome concurrency features.


Which ones would you remove?


> FP means different things for different people

It's difficult to agree with a definition of FP that doesn't require expressing as much computation as possible as function evaluation. Functions being mappings from values to values, having a rich set of values (not the same thing as objects!) is a prerequisite.


I'd have said the fact that no-one's done it is pretty good evidence it's not as easy as you think. As plenty of people have pointed out, FP means different things to different people. But so does OO. In fact, I'd argue that Scala is about as close as you're going to get to your spec. Not helped by the fact that, as any Scalazzi could tell you, the requirements of 1 and 2 contain contradictory elements and make complexity shoot through the roof. (You can get fewer FP features and less complexity by going for F# instead.)

Personally, I like Haskell. It's great for 1 and 3, 4 is pretty acceptable and 2 gets thrown in the bin.

5 could do with some work, though.


Not a trendy language by any means, but C# meets all your criteria.

1. About the strongest OOP support of any language which doesn’t have a dynamic or Scala-esque exotic type system. Also supports non-OOP imperative, unlike Java.

2. Linq is nigh-on the best FP support offered in any non-FP language’s standard library, and really is one of the best FP libraries period.

3. Invented async/await, by far the most ergonomic concurrency primitive yet created.

4. Fast and easy to tune for performance (stack-allocated value types which can also be passed by reference).

5. If you know Java or C++ you’ll be productive within minutes.


I had used Scala for many years before trying C#, and I have to agree. The only thing that I really missed in C# were higher-kinded types.


I think I'd miss pattern matching the most.

The first time I saw how pattern matching with regular expressions automatically extracted the matching bits I was sold.

I'd also miss block expressions. I keep wanting to write if-expressions (as opposed to if-statements) in every non-scala language I touch.

But I agree, c#/.net is a good alternative if you're used to the expressiveness of scala.


It is not at the same level as Scala, but C# 7 has pattern matching.


There's F# and then there's "F* " language that seem to have higher-kinded types

(I really mean F* as in 'F-star' language)


> 3. Invented async/await, by far the most ergonomic concurrency primitive yet created.

This feels like a bit of hyperbole. The actor model seems much more intuitive than async/await. Indeed, message passing between actors is how concurrency works in the real world (think about how people interact with each other).


I knew I'd get pushback on that one, it is somewhat hyperbolic; I'm here using "most ergonomic" to mean "closest source-level similarity to the synchronous equivalent." Which is perhaps not the "best" way to model concurrency, but has the advantage of being very easy to learn.


Rust, swift and kotlin once they have the llvm backend will fit the bill.


I wouldn't say Rust is simple to learn. The borrow checker alone is quite a steep hill (though the compiler errors are very good).

In my limited experience Rust and Swift both suffer from boilerplate/ceremony you have in many other OOP languages (C# too). Static languages for you I guess - there are benefits too...


Swift is awesome but lacks atomicity guarantees and it allows some race conditions even when using gcd.

https://github.com/apple/swift/blob/master/docs/proposals/Co...


Rust is very neat, but it lacks OO features.


Many would call that a feature in itself.


That depends on your definition of oo.


It seems to me like OCaml has all of those features except for #3 (which admittedly is pretty frustrating to me too). What do you think?


OCaml has great support for concurrency (check out Lwt or Async libraries). Do you mean parallelism, perhaps?


Depends on what you mean by concurrency support. OCaml has nice green threads via Lwt.



Kotlin doesn't really support FP in the modern sense of the term; effects are all special cases in the language (null, async), so you can't abstract over them generically (e.g. you could never implement "traverse" in Kotlin because you can't even write the type signature it's supposed to have) and you can't use a custom effect if you want to do something the language designers haven't thought of. E.g. there's no nice way to do validation where you want to have a message in the case of failure (in some ways this is actually a regression from Java, which at least had checked exceptions for this kind of use case, not that they're a great solution).


The thing that Scala has and Kotlin completely lacks is the tooling to allow you to avoid runtime reflection. Typeclasses and macros (and especially both together) are way too powerful-yet-principled to give up once you're used to them.


This has existed on the JVM for years: annotation processing.

Much less heavyweight than macros and easier to reason about.


Annotation processing is impossible to reason about; it's equivalent to macros but much less visible.

Most Scala doesn't need macros (other than those inside shapeless) because Scala has a) higher-kinded-types, for/yield and an existing library for working with context-like types, and b) generic traversal of object graphs via shapeless. Between them those cover virtually all the use cases for annotation processing, macros or anything like that.


I agree that most application-level code doesn't need new macros (you rarely have to develop a new macro to add a feature to an application) but there are definitely a lot of very legitimate use-cases for them outside of Shapeless. To give just a few:

  * https://github.com/adamw/macwire
  * https://github.com/fthomas/refined
  * https://github.com/adamw/quicklens
  * https://github.com/scala/scala-async
  * https://github.com/typelevel/machinist
Even for cases where inductive implicits can do the job, there is still a bit of a push to prototype with shapeless and then rewrite with direct derivations of your instances later on to improve compile times. See e.g. https://github.com/circe/circe-derivation.


macwire and scala-async are bad ideas in my book (having worked on codebases that use them) and I suspect the same of machinist and refined; they're both making too big a change to the language to make sense as a library, the cost/benefit doesn't stack up. (I do think prototyping language improvements is one other thing macros are good for, but that's not something you'd do in production code). quicklens looks like a reimplementation of the same stuff that shapeless does, so not really a different use case.

There are performance problems with inductive implicits in the current compiler; I'd rather see that fixed at the compiler level (I believe there's already a PR?) than see every library try to work around it. Actually I'd like to just build something like Shapeless' functionality into the language proper; I'd say e.g. case classes and sealed traits should have come with the equivalent of LabelledGeneric already.


macwire and scala-async are bad ideas in my book (having worked on codebases that use them) and I suspect the same of machinist and refined

We'll just have to disagree on these sorts of "power tools", I think.

Quicklens does a lot more for you than Shapeless's lenses, which to my knowledge not very many people are using for new projects at this point. I think once the Cats port lands, I'll be moving to Monocle for my own stuff, though (I think this will be the trend).

Of course, the compiler getting better about inductive implicts can only be a good thing, but I do think there may be some upper limit to what can be done short of an effort similar to what Eugene is taking on with Reasonable Scala, since they are using a typechecker as an unwitting computer when we program that way. The work that Miles did to improve inductive implicit performance is tremendously helpful but it does not fully solve the problem. As an example, my very-induction-heavy codebase compiles about twice as fast with his patch enabled. That's nothing to scoff at and is super-impressive for a single patch, but it doesn't make the problem evaporate.

I fully agree with you that it would be great if Generic-like stuff were baked into the language. To me, that sort of thing is the only compelling use-case for whitebox def macros. I feel like things would be better if we could just drop those.


What's wrong with async for you? For me, 95% of the time, it is fantastic, and for the 5%, I just fall back to some other method.


I find continuation-based stuff impossible to reason about; the only way I can understand the flow is in terms of values i.e. futures. And if we're working with futures I'd rather have them work as a plain old monad with for/yield that looks like any other monad, rather than having to remember what some unique future-specific syntax desugars into.


You can add

* https://github.com/zalando/grafter

to your list.


> Annotation processing is impossible to reason about

Maybe for you but given how popular annotation processing is on the JVM (and especially on Android), plenty of people are reasoning with them just fine.

Actually, it's so popular and easy to use that it's slowly replacing reflection in pretty much every library I've seen.


"Better than reflection" is a low bar, and I'd suspect it's more popular on Android precisely because better alternatives like Scala are less popular there (and also because Android codebases are smaller, so can get away with more unreasonableness).


In my experience, annotation processors are definitely way harder to reason about than def macros in Scala (especially blackbox ones). I find them to be about as hard to reason about as macro annotations despite being less powerful. They are also way harder to write and distribute and not at all an analogue to typeclasses.


Scala has scala-native: https://github.com/scala-native/scala-native

Any myriad of FP (scalaz, cats), concurrency libraries (rx, akka)

scastie: https://scastie.scala-lang.org/

scala.js: https://www.scala-js.org/

This FP-focused fork of the Scala compiler: http://typelevel.org/scala/

The next gen Scala compiler called dotty: http://dotty.epfl.ch/

Twitter doesn't mention scala-native...


Kotlin looks very nice. But I think there are languages with better concurrency support - Elixir (cheap green threads in the VM) and Scala (actors and futures) come to mind.

I think it really depends on your use case too. Kotlin is great for mobile apps and games, where coroutines are fantastic (see also C#/Unity there). For writing scalable web services, you need more control and resilience. There Elixir and Scala are the heavyweights.


The language doesn't matter if the eco-system and the actual business reason to adopt it isn't there.


Common Lisp comes to mind (ducks) ;)


dlang.org? :)

You can write (compiler enforced) pure functional code, and the standard library is somewhat monadic.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: