Explicit method tables in C # instead of OO - okay? Poorly?

I hope the name does not seem too subjective; I absolutely do not want to start a discussion on OO as a whole. I just would like to discuss the main pros and cons for different ways to solve the following problems.

Take this minimal example: you want to express an abstract data type T with functions that can take T as input, output, or both:

  • f1: takes T, returns int
  • f2: takes a string, returns T
  • f3: takes T and double, returns another T

I would like to avoid omitting any other dynamic input. I would also like to avoid mutations whenever possible.

1: abstract class based attempt

abstract class T {
  abstract int f1();
  // We can't have abstract constructors, so the best we can do, as I see it, is:
  abstract void f2(string s);
  // The convention would be that you'd replace calls to the original f2 by invocation of the nullary constructor of the implementing type, followed by invocation of f2. f2 would need to have side-effects to be of any use.

  // f3 is a problem too:
  abstract T f3(double d);
  // This doesn't express that the return value is of the *same* type as the object whose method is invoked; it just expresses that the return value is *some* T.
}

2: Parametric polymorphism and auxiliary class

(all implementing TImpl classes will be singleton classes):

abstract class TImpl<T> {

  abstract int f1(T t);

  abstract T f2(string s);

  abstract T f3(T t, double d);

}

, - - - Foo, TImpl. : , , - :

// Say we want to return a Bar given an arbitrary implementation of our abstract type
Bar bar<T>(TImpl<T> ti, T t);

3

class /* or struct, even */ TDict<T> {
  readonly Func<T,int> f1;
  readonly Func<string,T> f2;
  readonly Func<T,double,T> f3;

  TDict( ... ) {
    this.f1 = f1;
    this.f2 = f2;
    this.f3 = f3;
  }
}

Bar bar<T>(TDict<T> td; T t);

# 2 # 3.

class MyT { 
  /* raw data structure goes here; this class needn't have any methods */
}

// It doesn't matter where we put the following; could be a static method of MyT, or some static class collecting dictionaries
static readonly TDict<MyT> MyTDict 
  = new TDict<MyT>(
      (t) => /* body of f1 goes here */ ,
      // f2
      (s) => /* body of f2 goes here */,
      // f3
      (t,d) => /* body of f3 goes here */
    );

? № 3 , . , - . , , , . №2 ?

+3
2

Hurm. , , : , , -OO- ad-hoc- , , , this. , .

, , , , a la Wadler et al. , Microsoft SPJ, , Haskell # - .

-, , , , , , #. , , , #.

, - , , , , , , .

: , , , , , .

+10

#, Haskell, -, #. f1 f3 ad-hoc-. , :

interface INumber
{
    int Sign { get; }
    INumber Scale(double amount);
}

, : Sign - f1, Scale() - f3. f2, , . factory, , :

interface INumberFactory
{
    INumber Parse(string text);
}

Parse(), , f2. , , - . , ?

: FunctorSalad:

, , ( ) . , :

interface INumber<T> where T : INumber<T>
{
    int Sign { get; }
    T Scale(double amount);
}

interface INumberFactory<T> where T : INumber<T>
{
    T Parse(string text);
}

, , , , . :

class Rational : INumber<Rational>
{
    public int Sign { get { /* ... */ } }
    public Rational Scale(double amount) { /* ... */ }
}

class RationalFactory : INumberFactory<Rational>
{
    Rational Parse(string text);
}

, , , , "raw" INumber INumberFactory. , # . Rational, INumber<Rational> , Scale() INumber, Scale(), INumber. , . # , .

+1

Source: https://habr.com/ru/post/1749438/


All Articles