My Position on Type Classes

This is the most thumbed-up suggestion in fslang-suggestions and is over 7 years old. Is there any hope this will ever happen?

From https://github.com/fsharp/fslang-suggestions/issues/243#issuecomment-916079347

My position is pretty clear. I’ll recap it here.

The utility of type classes for the kind of “functions + data” coding we aim to support in F#, in the context of interoperating with .NET and Javascript, is largely over-rated and has many negative aspects that are rarely considered in a balanced way by those advocating these kinds of features.

Some examples of the negative consequences are listed below:

  • Simple type-classes are never sufficient and result in a consistent stream of requests for more and more type-level programming and type-level abstraction (higher kinds, higher sorted kinds, type families, whatever).
  • Any advanced combination of type-class-like features can very often result in obfuscated, subtle code
  • Adding features in this space leads to a trajectory towards more and more type-level programming. This has serious compilation performance downsides. You end up needing a profiler for your type-level computations, to find out why your compilations are taking so long, or you complain to the compiler implementers about it
  • Adding features in this space leads to a need for compile-time debugging. This is absolutely real – the C++ compiler gives “stack traces” of template instantiation failures, for example. Yet this is a kind of debugging that’s completely unsupported in any IDEs today.
  • Adding hierarchical type classification can result in programming communities that spend most their time talking about the “right” way to organise the universe of type classes, and experience surprisingly dogmatic discussions about that
  • Adding hierarchical type classification can result in programming libraries exposed to the “fragile type classification” problem – and repeatedly “rejig” and “tweak” their basic type classifications. This is not possible in a binary compatible ecosystem, meaning we’d be unlikely to ship any hierarchy of classifications in FSharp.Core.
  • Adding type-level programming of any kind can lead to communities where the most empowered programmers are those with deep expertise in certain kinds of highly abstract mathematics (e.g. category theory or abstract algebra). Programmers uninterested in this kind of thing are disempowered. While I have great respect for these as mathematical and computational theory, I don’t want F# to be the kind of language where the most empowered person in the discord chat is the person who knows the most category theory or abstract algebra.
  • The most effective use of these features require end-to-end design of the entire API surface area with these features in mind. Everything becomes tied up into a particular view on type-level abstractions and type-level programming. It quickly becomes impossible to program without an understanding of these abstractions, or without referencing a library which encodes this All in all this combination can actually be worse than deeply nested object-oriented hierarchies, for example, which F# also strongly discourages.

Next, we will eventually progress this approved RFC https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1043-extension-members-for-operators-and-srtp-constraints.md. A pre-requisite to this was https://github.com/fsharp/fslang-design/blob/main/FSharp-5.0/FS-1071-witness-passing-quotations.md and, while that’s in F# 5.0, we’ve still got one outstanding issue related to that. However I am aware that it is an opportunity to resolve all outstanding issues regarding SRTP. I also won’t progress it until I am certain that it won’t lead to a considerable rise in attempts to use type-level programming in F# for activities outside those designed to be supported by the RFC.

Next, C# has already added features relevant to this space, e.g. interfaces with static abstract methods. We must consider the ramifications of these for F# and integrate these.

So, we will not progress a type class design independently of C# (beyond SRTP, FS-1043, static abstract methods and so in)

TypeScript and typed Python are incorporating type-level programming programming features that are practically based, for interop/API-typing purposes. These features have many of the problems described above, but are at least using a methodology far more relevant to F# than the “hey, let’s make programming maximally abstract, maximally inferred and as much like category theory as possible” agenda sometimes pursued. That is, if we add more type-level programming features they would be much more likely to be the sort of thing to support the interop needs of Fable or F#-interop-to-Python or similar.

    As an aside, something strange happens when one tries to have rational conversations about the above downsides with people strongly advocating expansion of type-level programming capabilities – it’s almost like they don’t believe the downsides are real (for example they might argue the downsides are “the choice of the programmer” or “something we should solve” – no, they are intrinsic). It’s like they are prioritising type-level programming (computation that happens at compile-time) over actual programming (computation that happens at runtime). Indeed I believe psychologically this is what is happening – people believe that programming in the pure and beautiful world of types is more important than programming in the actual world of data and information. In contrast, the downsides are real and can have a thoroughly corrosive effect on the simple use of a language in practice in teams.

    As one example, I’ve translated Swift code using protocols to F#, and the use of Swift protocols was needless, and the resulting F# was much, much simpler (and much more like the original Python). It was very obvious to me that the person translating the original Python samples used Swift protocols to “show off” what so-called “value” Swift was bringing to the table. Well they failed – the use of protocols was simply obfuscating, not helpful. This happens all the time once extensive type-level programming facilities are available in a language – they immediately, routinely get mis-applied in ways that makes code harder to understand, excludes beginners and fails to convince those from the outside.

    Leave a comment