3 ms·
AFAICT, this means you won’t be able to define Either<string, string>, which is definitely a thing you sometimes want to do.
by moomin 5mo ago
AFAICT, this means you won’t be able to define Either<string, string>, which is definitely a thing you sometimes want to do.
- throw1234567891 5mo agobut can you define T1 and T2 of string, then use Either<T1, T2>?
- eterm 5mo agoCould you be clearer about what you mean, since string is a sealed type in C#, so what exactly do you mean T1 and T2 of string?
- rawling 5mo agoA record wrapping a string, indicating what the string represents, so you can't mix it up with a different thing also represented by a string.
- eterm 5mo agoYes, you can have two different record types which both wrap a string value. As a (bad) trivial example, you could wrap reading a file in this kind of monstrosity: var fileResult = Helpers.ReadFile(@"c:\temp\test.txt"); Console.WriteLine("Extracted:"); Console.WriteLine(Helpers.ExtractString(fileResult)); public record FileRead(string value); public record FileError(string value); public union FileResult(FileError, FileRead); public static class Helpers { public static FileResult ReadFile(string fileName) { try { var fileResult = System.IO.File.ReadAllText(fileName); return new FileRead(fileResult); } catch (Exception ex) { return new FileError(ex.Message); } } public static string ExtractString(FileResult result) { return result switch { FileError err => $"An Error occured: {err.value}", FileRead content => content.value, _ => throw new NotImplementedException() }; } } Now, such an example would be an odd way to do things ( particuarly because we're not actually avoiding the try/catch inside ), but you get the point. Both FileRead(string value) and FileError(string value) wrap strings in the same way, but are different record types, and the union FileResult ties them back together in a way where you can tell which you have. It's more useful implemented a level deeper, so that the exception is never raised and caught, because exceptions aren't particularly cheap in .NET.
- throw1234567891 5mo agotype T1 string type T2 string type Meh Either<T1, T2>
- deleted 5mo ago[deleted]
- jzebedee 5mo agoC# is strongly-typed, not stringly-typed. The point of the union is to list possible outcomes as defined through their respective types. The idiomatic way to do this would be to parse, don't validate [1] each string into a relevant type with a record or record struct. If you just wanted to return two results of the same type, you'd wrap them in a named tuple or a record that represented the actual meaning. [1] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
- nesarkvechnep 5mo agoI guess C# is more strongly-typed than Haskell then... /s
- goto11 5mo agoYou cant have a `type Foo = String | Strimg` in Haskell either.
- bertylicious 5mo agoBut you can have an `Either String String` which is what GP was talking about.
- bertylicious 5mo agoMy mistake. I see my oversight now. `Either String String` is not equivalent to `String | String`, but to `Left String | Right String`. The same must be done for the C# version.
- goto11 5mo agoYes, you must have individual constructors for the left and right cases in order to distinguish them. In C# you would use two distinct record types for this. Haskell’s syntax is more concise though, since you define the constructors inline in the declaration of the sum type.
- sheept 5mo agoIt seems like if you wrap both in a record then it should be possible: public record Left<T>(T Value); public record Right<T>(T Value); public union Either<L, R>(Left<L>, Right<R>);
- goto11 5mo agoWell it is a type union. The union of string and string is just string.
- bertylicious 5mo agoNo, it's a union of a left value (that happens to be a string) and a right value (that happens to be a string). But the compiler-generated code can't tell them apart.
- jafffsuds 5mo agoWhat you are describing is something different called a disjoint union which will maintain the identities of the left and right values when there is overlap. The C# unions appear to behave like unions, not disjoint unions.
- bertylicious 5mo agoMy mistake. I see my oversight now. `Either String String` is not equivalent to `String | String`, but to `Left String | Right String`. The same must be done for the C# version. You are correct that this requires support for disjoint unions (aka tagged unions), which Haskell always had and C# will soon have.
- twodave 5mo agoYou can use implicit operators or a library like Vogen to accomplish the same thing in a way that they can be coerced as strings. This isn’t a real issue.
- Metasyntactic 5mo agoHi there. One of the C# lang designers here. You're correct. The unions we're working on right now are 'type unions'. So the type is inherent in the union distinction, and you would not be able to distinguish that case. That said, we're also looking at full blown discriminated unions (you can look at one of my proposals for that here: https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/discriminated-unions/extended-enums.md https://github.com/dotnet/csharplang/blob/main/meetings/work...), which would allow for that. Syntax entirely tbd, but you'd do something like: enum struct Either<T1, T2> // or enum class { First(T1 value), Second(T2 value) } We view these features as complimentary. Indeed, if you look at the extended enum proposal, you'll see it builds on top of unions and closed types (another proposal coming in the next version of the lang).
- moomin 5mo agoThank you! Even what’s there is going to be really useful to me but I’ll continue to look forward to a full-fat implementation.