What's the difference between abstraction and generalization?

后端 未结 8 1160
滥情空心
滥情空心 2021-01-30 13:26

I understand that abstraction is about taking something more concrete and making it more abstract. That something may be either a data structure or a procedure. For example:

相关标签:
8条回答
  • 2021-01-30 14:09

    Abstraction

    Abstraction is specifying the framework and hiding the implementation level information. Concreteness will be built on top of the abstraction. It gives you a blueprint to follow to while implementing the details. Abstraction reduces the complexity by hiding low level details.

    Example: A wire frame model of a car.

    Generalization

    Generalization uses a “is-a” relationship from a specialization to the generalization class. Common structure and behaviour are used from the specializtion to the generalized class. At a very broader level you can understand this as inheritance. Why I take the term inheritance is, you can relate this term very well. Generalization is also called a “Is-a” relationship.

    Example: Consider there exists a class named Person. A student is a person. A faculty is a person. Therefore here the relationship between student and person, similarly faculty and person is generalization.

    0 讨论(0)
  • 2021-01-30 14:09

    I'd like to offer an answer for the greatest possible audience, hence I use the Lingua Franca of the web, Javascript.

    Let's start with an ordinary piece of imperative code:

    // some data
    
    const xs = [1,2,3];
    
    // ugly global state
    
    const acc = [];
    
    // apply the algorithm to the data
    
    for (let i = 0; i < xs.length; i++) {
      acc[i] = xs[i] * xs[i];
    }
    
    console.log(acc); // yields [1, 4, 9]

    In the next step I introduce the most important abstraction in programming - functions. Functions abstract over expressions:

    // API
    
    const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
    const concat = xs => ys => xs.concat(ys);
    const sqr_ = x => [x * x]; // weird square function to keep the example simple
    
    // some data
    
    const xs = [1,2,3];
    
    // applying
    
    console.log(
      foldr(x => acc => concat(sqr_(x)) (acc)) ([]) (xs) // [1, 4, 9]
    )

    As you can see a lot of implementation details are abstracted away. Abstraction means the suppression of details.

    Another abstraction step...

    // API
    
    const comp = (f, g) => x => f(g(x));
    const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
    const concat = xs => ys => xs.concat(ys);
    const sqr_ = x => [x * x];
    
    // some data
    
    const xs = [1,2,3];
    
    // applying
    
    console.log(
      foldr(comp(concat, sqr_)) ([]) (xs) // [1, 4, 9]
    );

    And another one:

    // API
    
    const concatMap = f => foldr(comp(concat, f)) ([]);
    const comp = (f, g) => x => f(g(x));
    const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
    const concat = xs => ys => xs.concat(ys);
    const sqr_ = x => [x * x];
    
    // some data
    
    const xs = [1,2,3];
    
    // applying
    
    console.log(
      concatMap(sqr_) (xs) // [1, 4, 9]
    );

    The underlying principle should now be clear. I'm still dissatisfied with concatMap though, because it only works with Arrays. I want it to work with every data type that is foldable:

    // API
    
    const concatMap = foldr => f => foldr(comp(concat, f)) ([]);
    const concat = xs => ys => xs.concat(ys);
    const sqr_ = x => [x * x];
    const comp = (f, g) => x => f(g(x));
    
    // Array
    
    const xs = [1, 2, 3];
    
    const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
    
    // Option (another foldable data type)
    
    const None =      r => f => r;
    const Some = x => r => f => f(x);
    
    const foldOption = f => acc => tx => tx(acc) (x => f(x) (acc));
    
    // applying
    
    console.log(
      concatMap(foldr) (sqr_) (xs), // [1, 4, 9]
      concatMap(foldOption) (sqr_) (Some(3)), // [9]
      concatMap(foldOption) (sqr_) (None) // []
    );

    I broadened the application of concatMap to encompass a larger domain of data types, nameley all foldable datatypes. Generalization emphasizes the commonalities between different types, (or rather objects, entities).

    I achieved this by means of dictionary passing (concatMap's additional argument in my example). Now it is somewhat annoying to pass these type dicts around throughout your code. Hence the Haskell folks introduced type classes to, ...um, abstract over type dicts:

    concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
    
    concatMap (\x -> [x * x]) ([1,2,3]) -- yields [1, 4, 9]
    concatMap (\x -> [x * x]) (Just 3) -- yields [9]
    concatMap (\x -> [x * x]) (Nothing) -- yields []
    

    So Haskell's generic concatMap benefits from both, abstraction and generalization.

    0 讨论(0)
提交回复
热议问题