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?