V3: Which language features should we support?

Created on 10 Nov 2020  路  44Comments  路  Source: exercism/v3

In v3, each track page will have this lovely looking block:

image

The idea of this block is for each track to give students a quick feel for the language by listing six features, each having a little description, title and icon.

One of our goals with v3 is to allow students to filter/search tracks. We want to allowing students to filter on these language features, which could be used e.g. to quickly find all functional languages, so we鈥檙e trying to understand what a superset of all the features looks like.

We're really interested in what six features you'd like to use to highlight your track. Please could you post your track and the features you'd want to apply to your track.

For inspiration, these are the paradigms that we've identified a while ago:

  • Declarative
  • Functional
  • Imperative
  • Logic
  • Object-Oriented
  • Procedural
statuhelp-wanted typdiscussion

Most helpful comment

I know this is a serious topic, and I am being serious.

JavaScript:

  • Cries
  • in
  • JavaScript
  • it's
  • not
  • Java

TypeScript:

  • Cries
  • in
  • JavaScript
  • but
  • with
  • Types

But in all seriousness, I have long lists of what maketh JS and TS, however, I'd like to hear _from you_. I'm in the same boat as @BethanyG . I can either defined super generic terms, or go into specifics that are core to the language, but no-one knows, or uses, because it's abstracted away.

Multi-paradigm says nothing, imo. And yet it's JS and TS too. It's both OO and Functional. It's single-threaded execution context, but multiple contexts (workers anyone?). JavaScript is wee/akly typed, TypeScript is "strong" with inference and optionally weak. It can be declarative (JSX/TSX), imperative. TypeScript has generics, but they both have partial types (duck typing in JS). Prototype based. Ooffff. Someone Help Please.

All 44 comments

(cc @exercism/track-maintainers)

Rust:

  • Strongly-typed
  • Borrow-checked
  • Pragmatic
  • Generic
  • Low-level
  • Compiled

Where I define

  • Pragmatic: supports both imperative and functional paradigms
  • Borrow-checked: has no garbage collection, instead using RAII and strong ownership semantics to deterministically determine when to clean up memory

Gleam could possibly have these, though I'm curious as to see what other features might be suggested:

  • Functional
  • Strongly typed
  • Generic
  • Immutable
  • Concurrent
  • Fault tolerant

@coriolinus

Where I define

Pragmatic: supports both imperative and functional paradigms
Borrow-checked: has no garbage collection, instead using RAII and strong ownership semantics to deterministically determine when to clean up memory

I might suggest that these definitions are a little niche and the terms "GC-less" and "multi-paradigm" would be more widely understood.

The term "pragmatic" seems somewhat loaded as it implies that languages that are not both functional and imperative are not pragmatic, which seems like a slight or otherwise derogatory comment.

More generally I think it would be worthwhile to attempt to use works with technical definitions, without any implication of superiority.

Pragmatic: supports both imperative and functional paradigms

I've seen people refer to this as Multi-paradigm languages. What do you think about that term?

I'm fine with "multi-paradigm". I feel pretty strongly that "gc-less" is an insufficient description: it implies manual memory management in the vein of C, and a big part of the point of Rust is to avoid that. The borrow checker is the language's most unique feature.

How strongly are we tied to precisely 6 options? If we can have N, where the top 6 get highlighted, then we can avoid general-purpose notations like "multi-paradigm".

Against "GC-less". That doesn't explain what the borrow checker is for.

You might or might not want to include:

  • system programming language (instead of low-level, for languages that you can use without an OS (i.e. for programming microcontrollers and writing new OSs) or for writing other low-level system software)
  • multiple dispatch (this is the main paradigm in Julia, but no other language uses it much (tho libraries exist, etc))
  • metaprogrammable
  • something about having good support for (or a focus on) statistics and science? (for python, R, Julia, Matlab, array languages, etc)
  • something about running in a browser? (e.g. it's useful that clojure has a dialect called clojurescript that runs in the browser; and it may one day be useful that WASM exists and some languages can compile to it).

Lots of these are pointing towards what the common or intended uses of the language are, but I think that's a useful categorising property.

Edit: "web language" might be good for languages that are good at running in browsers.

I'm fine with "multi-paradigm". I feel pretty strongly that "gc-less" is an insufficient description: it implies manual memory management in the vein of C, and a big part of the point of Rust is to avoid that. The borrow checker is the language's most unique feature.

Aye, it is for sure important to make that distinction between GC-less languages with and without memory safety. As a professional Rust programmer I feel that the name "borrow checked" is also insufficient as it described only one part of Rust's memory management strategy. The properties of the language here are "GC-less", "memory safe", and "real time" rather than "RIIA", "borrow checking", and "reference counting".

system programming language (instead of low-level,

馃憤 Agree, that's a better general term.

The properties of the language here are "GC-less", "memory safe", and "real time"

Yes, but for now at least, we're limited to 6 descriptors, and I didn't want to use half of them on the memory properties.

"GC-less" is jargony. "Garbage collected" as the positive term is fine (accompanied by an explanation), but the alternative should be spelt out positively as well.

I suggest:

  • Garbage collected (Java, JavaScript, Julia, Go, etc)
  • Manual memory management (C, C++, Zig, etc)
  • Borrowing and lifetimes (Rust)

Borrowing there is the least clear one. Perhaps "Compiler-enforced memory safety" or something would be better. A very brief skim of the Rust ownership docs don't give a pithy name, but maybe there is one out there :)

Borrowing there is the least clear one. Perhaps "Compiler-enforced memory safety" or something would be better.

That was the thought which led me to "borrow-checked". It looks from the description as though all the terms get some amount of description attached, which should be sufficient to clarify it.

It would be nice to find a term for this as there are multiple languages that have this property and they don't all use the term "borrow checker".

Steve Kablink suggested "static garbage collection"
Carp lang uses "Automatic deterministic memory management"
Wikipedia uses "Deterministic management of [memory]"

rust-lang.org opts not to talk about this and instead boasts "memory efficiency" and "memory safety".

The Julia website currently offers 6 features of Julia :) https://julialang.org/

They say:

  • Fast
  • Dynamic
  • Reproducible
  • Composable
  • General
  • Open source

I might pick these for the Julia exercism track:

  • Fast
  • Interactive (or Dynamic, saying that you can use it in interactive notebooks and REPLs)
  • Scientific (talking about reproducibility, distributed computing, and package environment)
  • Multiple dispatch (or Composable)
  • General

And for the last maybe one of these:

  • Multi-paradigm
  • Lispy

Instead of "Interactive" or "Dynamic", maybe "Interpreted"?

They're not necessarily the same thing, there are interactive languages that are not interpreted and interpreted languages that are not interactive.

I don't want to say that because Julia is compiled, it's just compiled very late (the "JIT" compiler is sometimes called very late ahead of time compilation, because there is no interpreter).

Besides, the result for the user is interactivity or dynamism rather than interpretation.

Some suggestions for Go:

  • strongly typed
  • compiled
  • concurrent
  • simple / readable (opinionated, minimal feature set)
  • reliable (memory safe, GC)
  • efficient (compiled, very fast but not super-optimised, very fast compiling, low footprint)
  • object-oriented
  • functional
  • open source
  • general-purpose
  • cross-platform
  • dependency-free executable

Not yet, but in a year or two:

  • generic

Feel free to pick.

  • Compiled
  • Metaprogramming
    These are for Nim, mostly just the ones that weren't mentioned before (kinda), but were just suggestions in general
  • Compile time code execution
  • dependency-free executable
  • asyncable
  • Platform independent
  • Compile to other langs (ie. js)
  • Customizable GC
  • Operator overloading/user-defined
  • Interoperability with other languages (especially C)
  • Friendly stack traces

I like @tehsphinx's suggestion of open-source option too

For Python:

  • Interpreted
  • Object-oriented
  • Declarative
  • Friendly docs
  • Duck-typing
  • Open source
  • Procedural

@BethanyG any others you'd like to add?

@ynfle: which language is your list for? (maybe edit your post)

@ynfle: which language is your list for? (maybe edit your post)

Nim. But they aren't language specific. More just potential options

I had to do something similar while writing my recent comparison of Rust and Go, and it wasn't easy! Ten or fifteen people reviewed the draft and very few of them agreed on what they thought were the salient characteristics that distinguish Rust from Go, or even what they have in common!

For the record, this is roughly what we came up with:

Rust:

  • Memory-safe
  • Compiled
  • Pragmatic
  • Scalable
  • Featureful
  • Real-time

Go:

  • Memory-safe
  • Compiled
  • Pragmatic
  • Scalable
  • Simple
  • Garbage-collected

The problem with trying to think of short, punchy, informative points like this is that _every_ point is arguable (and programmers will argue about everything). Everybody wanted to add caveats like "except in certain circumstances" or "relatively speaking" or "yes, but not in the South", and if I'd indulged them, the resulting piece would have been so hedged about with qualifiers and weasel words that it wouldn't have said anything at all. (Some people think it still doesn't.)

For Exercism, though, these matters won't be tested in court, and as long as the 'features' help people find the language they're interested in, it's all good.

Elixir:

  • Functional
  • Dynamically typed
  • Immutable
  • Concurrent
  • Fault tolerant

Terms that come to mind for Raku are:

  • Gradually typed
  • Multi-paradigm
  • Concurrent
  • General purpose
  • Open source

I don't think we should annotate "popular" on a language since that's subjective and also hard to define... is "popular" defined by LoC written in actual usage? If so, I think only C, Fortran, and Lisp count (but only thanks to a multi-decade head start!). Enthusiasm and uptake amongst new users? Well Rust would count then, in spite of having infamously difficult uptake. Overall user count? All pale before JavaScript.

As already mentioned, the definition of the strategy Rust uses for managing memory is Resource Acquisition Is Initialization (RAII), also called "Destructors Run At End of Scope". C++ actually enables some similar things, and borrowck is a _method_ of implementing this, but I would argue the borrow-checker is "not the thing", it is merely the way the thing works. The name "Scope-based resource management" is also popular here as RAII technically is also what GC languages usually use, but when they do they then unbind object lifetime from scope and reassign it to reference counting, so that when a scope closes, the reference gets removed. Rust's lifetimes are """just""" variables that define the scopes that something must be valid in.

I also suggest that Rust and Haskell should share Type classes as a defining feature. In Rust, they are called Traits, and there are many more rules, but they define the language because they are what is done instead of the familiar Object/Inheritance classes.

I would suggest that instead of "multi-paradigm" that, when a language has not clearly made a choice, that it have no special annotation for being "multi-paradigm" whatsoever and use that space for describing the features it does support more specifically. A common objection against languages that do not commit to functional _or_ object-oriented styles is that they are not either because they do not fulfill common expectations people might have, and here we are basically setting expectations. On a similar note, "imperative" is everyone, including Haskell when you write unsafePerformIO. :^) "Procedural" better defines those languages which typically have no more annotation than "imperative" (e.g. C).

This is not to say that we should never write "imperative" or "multi-paradigm" or "general purpose", but it's something to tack on at the end if you have no more boxes to fill, basically, since we're picking six features that distinguish the language.

since we're picking six features that distinguish the language.

That is not at all what the original post in this issue asks for. It asks for "six features you'd like to use to highlight your track". Further, if every track chose solely features that distinguish the language from the majority of other languages, this won't work:

which could be used e.g. to quickly find all functional languages, so we鈥檙e trying to understand what a superset of all the features looks like.

Being a functional language or supporting functional programming is nothing rare or distinguishing, yet it is probably something one would want to filter for. If it turns out that all 50 tracks choose to have the same feature, that feature is probably not worthwhile listing but that's something that can only be found out once the features are known, which appears to be the whole point of this issue.

when a language has not clearly made a choice

A language can clearly make a choice to support several paradigms. Sure, using different paradigms may be _possible_ in most languages, yet explicitely supporting them with features or syntax that make mixing and matching paradigms powerful is not something all languages have as a goal. One could list out all those specific features of course but that list will be a lot longer than 6 key features :)

@cmcaine I like your suggestions for the Julia track. I'd prefer composable over multiple dispatch as the heading since multiple dispatch likely doesn't mean much to most people unfamiliar with Julia, if I had to guess. The description could then mention that the composability is achieved through multiple dispatch.

I can't really think of a nice word to summarize it but perhaps the "intuitive"/familiar mathematical syntax could be another point? Maybe "expressiveness" or "ease and expressiveness" (as in the manual intro)?

The 2020 dev survey lists popular features that may as well work. Other than the ones you suggested, it includes these:

  • Ease of use
  • Open source (meh)
  • Solves the two language problem
  • Distributed / GPU computing (this falls under scientific I guess)
  • The community
  • Language of the future (this would be fun to list but I don't take responsibility for any language wars that may occcur :P)

This is great so far. Thanks everyone.


if every track choose solely features that distinguish the language from the majority of other languages, this won't work:

Being a functional language or supporting functional programming is nothing rare or distinguishing, yet it is probably something one would want to filter for. If it turns out that all 50 tracks choose to have the same feature, that feature is probably not worthwhile listing but that's something that can only be found out once the features are known, which appears to be the whole point of this issue.

馃挴

These are two points I've been thinking while reading this too.

As a student trying to choose a language, I think I want to know things like:

  • Functional
  • Memory-safe
  • Strongly-typed
  • Compiled
  • Low-level
  • Immutable
  • Concurrent
  • Platform independent

Those quickly enable me to get a feel for what the language is. There will be space (above this, so more prominent - see screenshot from the staging site below), where we can write a paragraph or two to pull out the special/exciting bits of a language not covered by these more "generic" boxes.

Screenshot 2020-11-11 at 00 41 59

I think Homoiconic and Extensible (or maybe specific Extensible Syntax) may be good additions to possible descriptors.

Perhaps it is too granual for OO languages but might it be good to have multi-inheritance vs enforced single inheritance as descriptiors? Or multi-dispatch vs. single-dispatch polymorphism?

I think Homoiconic ...

You're just showing off because Lisp 馃槢

But yes. Both that and Extensible are good ideas.

Perhaps it is too granual for OO languages but might it be

I think too granular for this bit. But it would be a good thing for the description bit above.

I know this is a serious topic, and I am being serious.

JavaScript:

  • Cries
  • in
  • JavaScript
  • it's
  • not
  • Java

TypeScript:

  • Cries
  • in
  • JavaScript
  • but
  • with
  • Types

But in all seriousness, I have long lists of what maketh JS and TS, however, I'd like to hear _from you_. I'm in the same boat as @BethanyG . I can either defined super generic terms, or go into specifics that are core to the language, but no-one knows, or uses, because it's abstracted away.

Multi-paradigm says nothing, imo. And yet it's JS and TS too. It's both OO and Functional. It's single-threaded execution context, but multiple contexts (workers anyone?). JavaScript is wee/akly typed, TypeScript is "strong" with inference and optionally weak. It can be declarative (JSX/TSX), imperative. TypeScript has generics, but they both have partial types (duck typing in JS). Prototype based. Ooffff. Someone Help Please.

It might be that for things like "Multi-paradigm" we want to have a box in the 6 that is "Multi-paradigm" and in the text a track can explain what that is for them. But that in the "filters" they tick "Object Oriented" and "Functional". That's going to involve a little more fiddliness at some part of the stack (be that the json that y'all add for this, or some "magic" in the website).

But I do think it's nice for a student to see a box saying "This language allows you to do OOP or functional depending on the situation" while it still being able to appear under people filtering by either. 馃し

@coriolinus for Rust what does _generic_ mean?

I'm never that comfortable with _low level_ or _systems programming_. They sound like limitations. What do thing of _zero overhead abstractions_ which I think is a killer feature (obviously shared with _C_ and _C++_)?

Generic: supports generics via monomorphization. Like C or Java, which also have generics; unlike go, which doesn't; unlike Python, which uses duck-typing instead.

Agree that low-level sounds like a limitation, particularly as Rust also has a bunch of very high-level tools available. Systems programming, though, I'm comfortable with, because it correctly suggests that this language lets you work very close to the metal.

"Zero-cost abstractions" is a good term that I'd like to see if we have more space, but AFAIK we're still limited to precisely 6, and IMO the existing contenders are more useful for students.

We also approached the filtering question from a different angle here, in case that stimulates any thoughts/ideas.

Swift's Six:

  • Protocol-Oriented
  • Generic
  • Strongly, statically typed
  • Biased towards memory safety
  • Open-source
  • Multi-Paradigm

The top three are the only ones that I think are particularly meaningful.

For R:

  • Interpreted
  • Dynamic
  • Multi-Paradigm (Functional / Procedural / Object-Oriented)
  • Memory-safe (Garbage Collected)
  • Open Source
  • Platform Independent

Shen is:

  • Functional.
  • Optionally typed.
  • Optionally lazy.
  • Homoiconic (has great macro support, not sure how to phrase this better).
  • Hosted on multiple runtimes.
  • Open source.

@SaschaMann Yes, I am aware of everything you are saying, and my point is that when a language is not strongly characterized by a given paradigm but has some incidental features thereof that it is not necessarily very useful to filter by that parameter and see very wildly different languages appear if I have an expectation of what that paradigm means. If "has support for" is qualifying, then that provides the argument that Java, having the barest amount of lambda support in its standard library, should be qualified as a functional programming language.

But mostly my remarks are motivated by the fact that if a student searches for a programming language feature and it reduces a list of 50 languages down to ~48, then it isn't that useful a filter, ne? We agree on that. And my statements stem from a bland prediction that the vast majority of programming languages are going to say multi-paradigm/pragmatic/something-like-that. I didn't realize that anyone didn't have that as an expectation, given that everywhere I have seen such "Writing X PL in Y Style" or "X PL is now incorporating Y features!" articles everywhere. So, either we need to consider paradigms in programming languages to be something other than mere binary values, or we invite "No True Scotsman" arguments (even more dreadful) or we should question the utility of that.

Maybe "pragmatic" could make sense if it is used to indicate a _lack_ of strong opinion from the language on what styles you should use... i.e. you can choose to _not_ use object-oriented programming or _not_ use functional programming styles or _not_ etc. etc. Being able to _avoid_ expressing the program in a way that does not suit your mental model is an underrated feature, I think.

Sooo. I guess instead of trying to figure which 6 to show as featured (which is kinda what the OP @ErikSchierboom is asking), I think we're all in agreement that the _track filters_ shouldn't exclude tracks if a property applies to a filter.

The 6 highlights are just that: highlights. They don't need to be the 6 core, 6 biggest, 6 baddest features.

In other words, I think @iHiD that you sorta hinted on this. What we shows as the 6 featured ones should make sense with _why_ you might choose/_what_ makes the language unique/_what's its fort茅_.

I think for Ruby, at least 4 of the 6 should be something like:

  • Sparking Joy,
  • Humanoid syntax,
  • Everything's an Object,
  • Huge Standard Library

The fact that it's multi-paradigm (like many dynamic/interpreted/higher level languages) is something you can mention; but for languages where it's not something to highlight, it should be tagged, not listed. For example like @workingjubilee says: You can write functional code with Java. It doesn't make that a good item for this list. Java is also not a functional language (having support != it should be _highlighted_).

Of course I won't be determining what is and isn't the language, but if we can determine what's idiomatic and what isn't, we can probably figure out a way to get this sorted as well.

Sounds like the six things in the boxes won't necessarily map nicely to the different filters. That's useful information in itself.


I'm going to "close" this issue here, take all that info and work with the product team on firming up the specification for exactly how this is going to work. Then we'll write up an issue and y'all can PR your track-specific stuff to the relevant places.

If anyone else comes to this late and wants to add thoughts for their own tracks so its documented, feel free, but also feel free to hold off until we firm things up!

Thanks everyone 馃挋

Kotlin is:

  • Pragmatic
  • Statically Typed
  • Multi-Paradigm
  • Platform Independent
  • Garbage collected
  • Open-Source

Just my thoughts as part of @exercism/dart, if anyone has other thoughts, please share

Dart:

  • General Purpose Language
  • Object-Oriented
  • Strongly-typed and Type Sound
  • Open Source
  • Platform independent

I would add Null Safe, but that is still in experimental phase

Spec has been added in https://github.com/exercism/v3-docs/blob/master/anatomy/tracks/config-json.md#key-features

TL;DR Tracks are free to choose what features they like.

Was this page helpful?
0 / 5 - 0 ratings