Live data from Hacker News

Official proposal for Type Unions in C#

github.com

51–60 of 315 posts

Re: Official proposal for Type Unions in C#

#51

Every time the question of preferred programming languages comes up, I'm usually in the extreme minority with my preferences being Rust (for small/fast) and C# (for productive and easy, where GC is acceptable), a combination that doesn't seem to appeal to too many people. But for the projects that fall right about in the middle where it could go either way and I could see either language working, I almost always pick…

> It's incredibly hard to go back to a language without even basic ADTs after seeing the light.

How did we ever prioritize implementation inheritance over ADTs?

I don't know, but it was a mistake.

Re: Official proposal for Type Unions in C#

#52
post #32

I find it strange and slightly confusing that tey call it "union types". The correct term would be "sum types" or "discriminated union types", union types being the union of two types with no distinctive tag between the two cases. E.g. |Int ∪ Int| ≡ |Int|, while |Int + Int| ≡ 2 |Int|

> I find it strange and slightly confusing that tey call it "union types". The correct term would be "sum types" or "discriminated union types"...

You mean like this?

> A proposal for type unions (aka discriminated unions) in C#.

Discriminated unions are a type of union type, hence the name, and in terms of how they're used in everyday development they fill a very similar role. If they have no intention of supporting pure TypeScript-style unions (which I'm sure they don't) then I don't know what the harm is of shortening "discriminated union" to "union" in the context of C#.

Re: Official proposal for Type Unions in C#

#53

I’ve lost track of all of the red/blue/white/black pill color metaphors, but unions with exhaustive pattern matching is one of the toughest of all programming language features to live without once you become aware of it. I’ve never felt like I’ve fully understood the implications of the expression problem https://en.wikipedia.org/wiki/Expression_problem but my best/latest personal hypothesis is that providing extens…

Maybe I'm off, but to me the gist of the expression problem can be explained by contrasting how code extensibility is achieved in OOP/FP.

OOP Approach with interface/inheritance:

Easy: Adding new types (variants) of a base class/interface. Hard: Adding new functionality to the base class/interface, as it requires implementing it in all existing types.

FP Approach with Discriminated Unions:

Easy: Adding new functions. Create a function and match on the DU; the compiler ensures all cases are handled. Hard: Adding new types to the DU, as it requires updating all existing exhaustive pattern matches throughout the codebase.

Here's some Kotlin code. Kotlin is great because it can do both really well.

  // Object-Oriented Approach
  interface Shape {
      fun area(): Double
      fun perimeter(): Double
  }

  class Circle(val radius: Double) : Shape {
      override fun area() = Math.PI \* radius \* radius
      override fun perimeter() = 2 \* Math.PI \* radius
  }

  class Rectangle(val width: Double, val height: Double) : Shape {
      override fun area() = width \* height
      override fun perimeter() = 2 \* (width + height)
  }

  // Easy to add new shape
  class Triangle(val a: Double, val b: Double, val c: Double) : Shape {
      override fun area(): Double {
          val s = (a + b + c) / 2
          return Math.sqrt(s \* (s - a) \* (s - b) \* (s - c))
      }
      override fun perimeter() = a + b + c
  }

  // Hard to add new function (need to modify all existing shapes)
  // interface Shape {
  //     fun area(): Double
  //     fun perimeter(): Double
  //     fun draw(): String  // New function
  // }

  // Functional Approach
  sealed class ShapeFP {
      data class CircleFP(val radius: Double) : ShapeFP()
      data class RectangleFP(val width: Double, val height: Double) : ShapeFP()
  }

  fun area(shape: ShapeFP): Double = when (shape) {
      is ShapeFP.CircleFP -> Math.PI \* shape.radius \* shape.radius
      is ShapeFP.RectangleFP -> shape.width \* shape.height
  }

  fun perimeter(shape: ShapeFP): Double = when (shape) {
      is ShapeFP.CircleFP -> 2 \* Math.PI \* shape.radius
      is ShapeFP.RectangleFP -> 2 \* (shape.width + shape.height)
  }

  // Easy to add new function
  fun draw(shape: ShapeFP): String = when (shape) {
      is ShapeFP.CircleFP -> "O"
      is ShapeFP.RectangleFP -> "[]"
  }

  // Hard to add new shape (need to update all existing functions)
  // sealed class ShapeFP {
  //     data class CircleFP(val radius: Double) : ShapeFP()
  //     data class RectangleFP(val width: Double, val height: Double) : ShapeFP()
  //     data class TriangleFP(val a: Double, val b: Double, val c: Double) : ShapeFP()
  // }

Re: Official proposal for Type Unions in C#

#54
post #44

Earlier quoted context omitted.

I get the feeling F# is a second-class citizen in the ecosystem. How likely is it (or is it at all possible) that I'm going to run into some library that doesn't work with F#?

Just to add to the other comments, in my experience a large majority of libraries work fine. Maybe a little tweak here or there, or maybe you need to use some C# esq syntax (which is harder in my case because I so rarely do now). Generally though, it's pretty easy. Sometimes you'll want to make bindings around various things (not that you need to, but it'll make things smoother). It's not actually awful, it's just th…

My experience mirror yours. I really lean into the functional core, imperative shell because of this. Im mostly interacting with C# flavored libraries at the edges and I don’t try to force F# paradigms there, I just roll with the punches.

Re: Official proposal for Type Unions in C#

#55

Every time the question of preferred programming languages comes up, I'm usually in the extreme minority with my preferences being Rust (for small/fast) and C# (for productive and easy, where GC is acceptable), a combination that doesn't seem to appeal to too many people. But for the projects that fall right about in the middle where it could go either way and I could see either language working, I almost always pick…

> It's incredibly hard to go back to a language without even basic ADTs after seeing the light. How did we ever prioritize implementation inheritance over ADTs? I don't know, but it was a mistake.

How widely understood and appreciated were algebraic data types at the time Cfront was first floated as the precursor to C++? To what degree had they shown up in mainstream languages?

Re: Official proposal for Type Unions in C#

#56
post #14
post #11

Earlier quoted context omitted.

I feel the same about Scala. I use Scala3 daily and almost every other language is such a step back. I have looked at F# and it looks like one of the only other languages I would enjoy as much as Scala. Haskell is nice too, but the ecosystem is just not quite there. F# being able to tap into the rich C# ecosystem and Scala being able to tap into the Java ecosystem is such a win and makes them feel a lot less niche wh…

I do like Scala, and I haven't touched Scala3, but I've found it pretty unwieldy for actual use. It feels like I'm constantly fighting with the type system in a way that ends up being annoying an unpleasant. To be clear, it's not like I'm new to functional languages and their type systems, I've been doing Haskell for forever, and I did F# for a multiple years, but Scala has never really clicked for me. Personally, fo…

> I definitely recommend giving F# a try. I think it's an extremely underrated language.

Do you have any recommendations for people who do not have C#/.NET experience who want to rip their toes in F#? The last time I tried the language I bumped my head on the .NET parts. :/

Re: Official proposal for Type Unions in C#

#57
post #29
post #18

What would the rough timeframe be for seeing adoption of this into the language? I was considering introducing the OneOf library into our codebase, but if this is < a year or so away it might not be worth the effort.

You won't be able to leverage C#'s pattern-matching effectively with a library. Really though, you don't need a library to do sum-types in C#: It's best to just use records for now: public abstract record Maybe { private Maybe() { } public sealed record Just(A Value) : Maybe ; public sealed record Nothing : Maybe ; } The private constructor and sealed case-types stops anybody else deriving from `Maybe `, so the type…

Not quite closed. The compiler generated protected constructor can still be used.

    public record UhOh : Maybe
    {
        public UhOh() : base(new Maybe.Nothing())
        {
        }
    }

    Maybe m = new UhOh();

Re: Official proposal for Type Unions in C#

#58
post #12

Every time the question of preferred programming languages comes up, I'm usually in the extreme minority with my preferences being Rust (for small/fast) and C# (for productive and easy, where GC is acceptable), a combination that doesn't seem to appeal to too many people. But for the projects that fall right about in the middle where it could go either way and I could see either language working, I almost always pick…

Instead of C#, why not F#? You get proper ADTs among many other great features, but you still get all the utility of the .NET ecosystem.

F# has it's own warts, tooling isn't as polished, stuff like type providers sound really cool but suck in practice IMO, file ordering being relevant for compilation is bleh...

Every time I try to use it I'm left with a feeling it's not worth the hassle over C#.

C# has been quite nice for 10 years and they keep improving consistently with every version.

Re: Official proposal for Type Unions in C#

#59
post #32

I find it strange and slightly confusing that tey call it "union types". The correct term would be "sum types" or "discriminated union types", union types being the union of two types with no distinctive tag between the two cases. E.g. |Int ∪ Int| ≡ |Int|, while |Int + Int| ≡ 2 |Int|

AFAICT from a quick skim of the proposal is actually includes both sum types (called union types) and union types (called ad hoc unions) under the same proposal. This is, to me, very confusing. They work in very different ways.

Re: Official proposal for Type Unions in C#

#60
post #12

Earlier quoted context omitted.

Instead of C#, why not F#? You get proper ADTs among many other great features, but you still get all the utility of the .NET ecosystem.

The benefits of F# only really apply when using a 100% F# codebase. Try doing WinForms in F#...

That F# has no native GUI framework is the reason why I stick with C#. When coding something with a GUI, the majority of my development time is typically spent in the GUI layer. The rest is some more or less straightforward object relational mapping with some validation an calculations in a business layer where I use Linq to emulate functional programming in C#.

Adding F# to the game would only complicate the scenario, because I would need to connect the F# interfaces somewhere to C# anyway, requiring an additional layer of mappings in many cases.

Post reply on HN