3 ms·
> Arguably, that doesn't count as type classes because it REQUIRES call-site awareness of the implementation Yes. I should have stated it's not real type-class
by louthy 10y ago
> Arguably, that doesn't count as type classes because it REQUIRES call-site awareness of the implementation
Yes. I should have stated it's not real type-classes, but it is real ad-hoc polymorphism, which does get you much of the way there. The type-system won't infer the instance, you need to specify it manually. That doesn't make the solution less powerful from a generic programming point-of-view (if anything it makes it more powerful, because you can provide the instance you want to use); however it does make it more awkward to use.
> and fully impractical as a useful abstraction.
I disagree with this though. Finally being able to define a numeric type, or make types into real monads where a function can declare a constraint that requires an argument to be a monad is a good thing.
> At that point you might as well call a static method without polymorphic parameter types
From what I can tell, you're thinking of just the call site, but a whole call stack can carry through the constraint, and therefore the call site doesn't need the concrete implementation, it just needs a type that is constrained to the 'type class' (interface). The call site wouldn't know the type of the static method to call in your case.
For example with a numeric type:
public static T Add<T>(T lhs, T rhs) =>
// what static method can you call here?
If you don't use polymorphic types, then you're not writing generic code, which is what this is for:
public static class Math
{
public int Add(int lhs, int rhs) => lhs + rhs;
public double Add(double lhs, double rhs) => lhs + rhs;
public float Add(float lhs, float rhs) => lhs + rhs;
public decimal Add(decimal lhs, decimal rhs) => lhs + rhs;
}
What about when you forget: BigInteger, short, byte, long, etc.
But with the method I explained above:
public interface Num<A>
{
A Add(A lhs, A rhs);
}
public struct NumInt : Num<int>
{
public int Add(int lhs, int rhs) => lhs + rhs;
}
public struct NumBigInt : Num<BigInteger>
{
public BigInteger Add(BigInteger lhs, BigInteger rhs) => lhs + rhs;
}
etc.
Any code that works with numeric values can then be written once. I'm sure many C# programmers over the years have cursed over having to write N variants of something that should work with IArithmetic, or INumber, or something similar. This gets around that problem (and without causing any boxing either). So, personally, I think it has value. Even if it's only needed rarely.
> because it's just as (in)convenient and 100% more readable to the developer maintaining the project after you move on to a better programming language.
My library isn't about the mindset of programmers who are stuck in OO-land with all the conservative nonsense that goes along with it. So although I agree there's a learning curve, I don't think this is too problematic for those who want to write truly generic code.
By the way, Microsoft are experimenting with this technique[1] (and new grammar) for future versions of C#. Whether it makes it or not, who knows, but I think it would be a super valuable feature for C#.
[1] https://github.com/CaptainHayashi/roslyn/blob/master/concepts/docs/concepts.md https://github.com/CaptainHayashi/roslyn/blob/master/concept...