Hi,

I wanted to encode entities with an "intrinsic" (existential) type and a thing 
that comes close to it is described in :

<https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>

I had to extend the pattern a bit with a template taking two types, the latter 
for the intrinsic type and the implementation requires three steps : a 
constructor pattern, the definition of constructor classes (being templates 
themselves) relying on the constructor pattern, and finally the instantiations 
of types (classes) that contain the intrinsic type.

I then can "at will" decide if I want to keep the instantiation type abstract 
because it can never be seen from the outside ("we don't now"), or I can "pull 
it out" with type reconstruction, in particular using a template<template> 
construct. The latter can be regarded as a rank-2 type, because the type is not 
given by forehand.

At my surprise, it is possible within C++. It shows that C++ retains the 
abstract part of a template pretty well - at least for the purpose. If C++ 
allowed for better abstractions, we could do more with it. But C++'s templates 
were introduced at a time where these extensions could not be seen - they were 
not understood yet. (Instantiation types can't reflect abstract types in a 
satisfying way...)

However, the implementation is entirely static. Where in other languages, it 
would be done with virtual functions with the help of a runtime table.
    
    
    #include <stdio.h>
    #include <iostream>
    using namespace std;
    
    #define reif (static_cast<T&>(*this))
    
    
    //constructor pattern
    template<class T, typename V = int>
    struct pcset  {
      void show() {     reif.mshow();    }
      void set() {      reif.mset();     }
      void sumop(T& v){ reif.msumop(v);  }
      void notimpl() {  reif.mnotimpl(); }
      void mshow() { printf("dummy: %d\n" , dummy); }
      void msset() { printf("set not implemented yet!\n"); }
      void msumop(T& v) { printf("sumop not implemented yet!\n"); }
      void mnotimpl() { printf("... not implemented yet!\n"); }
      int dummy;  protected:
      V mc;
      static V static_op( V a, V b) { return T::mstatic_op(a,b); }
      static V mstatic_op( V a, V b)  {
           printf("static_op not implemented yet!\n"); return a; }
    } ;
    
    //constructors
    
    template<class A, typename U, typename V>
    struct dvd_ggen : pcset<A,V> {
      U ma,mb;
      void init(U a, U b) { ma = a; mb = b; this->mc = b; this->dummy = 0;    }
      void mshow() { cout << ma << " " << mb << "  " << this->dummy <<"\n" ; }
      void mset() {  ma = mb; }
      void msumop(A& v) { ma = this->static_op(ma,v.mb) ; }
      static V mstatic_op(V a, V b) { return a + b ; }
    } ;
    
    template<typename U>
    struct dvd_gen : dvd_ggen<dvd_gen<U>,U,U> {};
    
    struct dvd_int  : dvd_ggen<dvd_int,int,int> {
      void init (int a,int b) { this->dummy = b; }
      static int mstatic_op(int a, int  b) { printf("I'll do nothing for 
you!\n"); return 0 ; }
      void mnotimpl() { printf("surprise! surprise!\n");}
    } ;
    
    
    //instantiations: bind the intrinsic type
    
    dvd_gen<int> da,db;
    dvd_gen<string> dc,dd;
    dvd_int de;
    
    
    //access with template<template>  a rank-2 quantifier
    template <template<typename> class  A, typename T>
    void binop(A<T>& b, A<T>& c) {
        b.sumop(c) ;   b.show() ;
        T temp = b.mb; b.ma = temp;
    }
    
    
    //some operations
    int main(int argc, char *argv[]) {
        da.init(5,7) ; da.show();
        db.init(1,2); db.sumop(da); db.show();
        dc.init("alpha","beta"); dd.init("delta","epsilon");
        dc.sumop(dd); dc.show();
        de.init(0,42) ; de.set() ; printf("...show\n") ; de.show();
        dd.notimpl();
        de.notimpl();
        //binop<dvd_gen<int>>(da);
        binop(dc,dd);
    }
    
    
    
    
    Run

My question is: How could this be achieved in Nim? Or, alternate question : How 
abstract are instantiation types in Nim?

Reply via email to