Premetto che a mio avviso AmigaOS dimostra in che modo dovrebbero essere usate le librerie: copia unica, versionata, e retrocompatibile.
Messo un attimo da parte questo, il problema su Windows è stato risolto esattamente come hai descritto in "Ciò che abbiamo a disposizione". Infatti da un po' di anni Microsoft, per risolvere il problema del "DLL Hell", ha suggerito fortemente agli sviluppatori di NON copiare le dll (oppure ocx, o altro) nella cartella System32, e quindi di lasciarle all'interno della cartella del programma, in modo che l'applicazione abbia e si porti dietro esattamente la versione che le serve per funzionare bene.
Con .NET ciò non è sufficiente, perché gli assembly sono in formato IL (Intermediate Language), per cui a runtime devono essere compilati (in maniera del tutto trasparente per l'applicazione) per l'architettura sulla quale si stanno eseguendo il codice, per cui Microsoft ha creato la cartella WinSXS (se ricordo bene il nome) dove mantiene una copia del binario per quella precisa architettura, e il tutto versionato (si usa anche il numero di build allo scopo). Internamente mantiene anche una lista delle dipendenze di ogni applicazione e dei relativi file che per essa ha generato.
Il sistema funziona bene, e in memoria è possibile che ci siano n copie della stessa libreria, a seconda della precisa versione che una particolare applicazione sta usando in quel momento.
Il problema è che così si occupa parecchio spazio su disco (e in memoria, ma soltanto a runtime). Se andate a controllare la cartella WinSXS, sono diversi i GB che si mangia. Mi direte: embé, con hd che si misurano ormai in TB, si sta ancora a elemosinare per lo spazio?
Beh, sì, perché vanno molto di moda gli SSD, che non hanno capienze enormi, e che inoltre è meglio non tartassare con le scritture (hanno un ciclo di vita molto basso, sulle migliaia di scritture, e man mano che il processo produttivo migliora, abbassando i prezzi, peggiora però il ciclo di vita, che si continua ad abbassare).
Questa soluzione, applicata ad AmigaOS, consentirebbe di mantenere le vecchie librerie senza far ricorso alle interfacce, ma c'è una duplicazione di codice sia in memoria che su disco.
Un'altra soluzione sarebbe quella di modificare il concetto di interfaccia introdotto su AmigaOS 4, facendolo rientrare in quello delle vecchie librerie, con qualche piccola modifica interna per gestire questo caso, e un flag nel codice della libreria per indicare che si tratta di una speciale libreria che mantiene al suo interno più versioni, in modo che il loader e Open/CloseLibrary possano gestirle correttamente.
Su filesystem c'è una copia unica, l'ultima, ma internamente il codice è duplicato n volte, una per ogni versione supportata, con relativa tabella delle funzioni esposte.
Ogni volta che un'applicazione apre una libreria, lo fa esattamente come ha fatto sempre con il buon vecchio AmigaOS, quindi senza interfacce. Richiama OpenLibrary, specificando la versione minima della libreria; se ha messo 0, il s.o. usa l'ultima versione, altrimenti recupera la LibraryBase interna (o la crea, se è la prima volta) che ha la più piccola versione maggiore o uguale a quella richiesta dall'applicazione, e gliela restituisce.
In questo modo si ha il vantaggio di mantenere lo stesso, identico, modo di programmare a cui ci ha abituato AmigaOS, con quello di usare le interfacce introdotte da OS4, ma il tutto gestito in maniera trasparente.
C'è soltanto un problema, ed è a carico di chi scrive la libreria. Il codice ovviamente è duplicato, e non ci si può fare niente (in realtà sì, ma lo spiego dopo), ma lo è anche LibraryBase che, come sappiamo, è costituito sia dalla tabella delle funzioni (a offset negativi rispetto a LibraryBase) che dalla struttura (struct) coi campi che vengono esposti pubblicamente o gestiti privatamente. Come sappiamo, normalmente LibraryBase è globale alla libreria e a tutte le applicazioni che ne fanno uso, ma qui ce ne sono n copie in memoria, una per ogni versione/interfaccia aperta.
Questo significa che le applicazioni non possono e non devono accedere ai campi di LibraryBase (che poi è quello che si dovrebbe fare normalmente, perché nel resto del mondo a quei campi normalmente si accede tramite apposite funzioni Get e Set), lasciando che sia il codice interno della libreria a farlo. Questo codice si deve occupare e preoccupare di mantenere tutte queste versioni di LibraryBase in maniera coerente, e ciò può risultare complicato, oltre che essere fonte di possibili errori.
Una soluzione pulita e, a mio avviso, buona per questo problema potrebbe essere l'uso dell'MMU. Si alloca una sola copia di LibraryBase, pari alla massima dimensione di tutte le versioni di LibraryBase supportate. Mentre viene generata una copia per ogni tabella dei vettori alle funzioni. Per ogni versione supportata, con l'MMU si mappa in memoria la relativa vector table a cui si fa seguire l'unica copia di LibraryBase condivisa.
L'unico intoppo di questa soluzione è che se c'è un bug in qualche versione che crea casini all'unica copia di LibraryBase, tutte le versioni supportate potrebbero avere problemi.
Poi c'è il fatto che richieda l'uso dell'MMU, che mi è particolarmente indigesto da amighista, ma come giustamente dice Paolo, bisogna tenere conto delle esigenze degli utenti, e poi alle porte del 2013 l'MMU è presente su praticamente tutti i processori. Purtroppo pensando ad AROS, non si potrebbero supportare le vecchie macchine Amiga non dotate di MMU, ma non ci si può far nulla.
Comunque sono idee che mi sono venute di getto, sul momento. Può essere che ci sia qualche falla nei ragionamenti fatti, e se c'è qualcosa di non chiaro possiamo anche parlarne.
Resta il fatto che soluzioni come questa o quella delle interfacce nascono a causa di una cattiva programmazione da parte di chi scrive le librerie e/o le applicazioni che ne fanno uso.
Con AmigaOS c'è sempre stata una sola copia, l'ultima, delle librerie (di sistema, lo specifico, che è meglio), e questa filosofia l'ho apprezzata e ritenuta corretta, per cui non condivido l'uso di altri meccanismi. E non per questione di modernità o meno: è proprio filosofica.
Dimenticavo il discorso sulla duplicazione del codice. Si potrebbe evitare se internamente la libreria provvedesse a riutilizzarlo il più possibile. Ad esempio se per una versione c'è una funzione più avanzata, con qualche parametro in più, le versioni più vecchie potrebbero richiamare quella nuova per risparmiare codice. Il problema è che se la funzione avanzata è bacata, anche quelle vecchie che la usano lo saranno.
E' una cosa che potrebbe funzionare bene se e solo se il codice delle varie funzioni è esattamente lo stesso, e gli eventuali campi di LibraryBase vengono utilizzati allo stesso modo da tutte le versioni della libreria.
Si tratta, comunque, di approcci che sono soggetti a errori e bug, perché il programmatore che ha scritto la libreria deve avere sempre ben presente il funzionamento per ogni versione della stessa. Il che... è un inferno. Per cui meglio duplicare il codice e amen.
P.S. Se proprio vogliamo parlare di futuro, per me si dovrebbe seriamente prendere in considerazione l'idea di usare un Intermediate Language per il codice di librerie e applicazioni, in modo da renderli multiarchitettura. Il s.o. si occuperà di compilarli al volo quando richiesto, ed eseguirne il caching (in modo da evitare nuovamente la compilazione a ogni esecuzione).