Je ne peux pas vraiment répondre # 2 sans me perdre (il y a trop de dimensions le long desquelles vous pouvez comparer ces structures), mais pour # 3 la réponse est assez simple.
Utilisez une structure de données impérative si: (a) il n'y a absolument aucun alias, ou (b) vous avez vraiment besoin d'utiliser l'alias pour une diffusion efficace.
S'il n'y a aucun alias de votre structure de données, vous ne profitez pas du fait que les structures de données fonctionnelles sont persistantes. Il n'y a donc aucune raison de payer leur coût. Il y a deux mises en garde à ce conseil. Tout d'abord, vous préférerez peut-être la simplicité de mise en œuvre d'une structure de données fonctionnelle: la mise en œuvre de la suppression pour un arbre rouge-noir fonctionnel vous fera malédire, mais la mise en œuvre de la suppression dans un arbre rouge-noir impératif avec des pointeurs parent vous laissera envisager le suicide. Deuxièmement, l'affectation peut être plus coûteuse que ce à quoi vous vous attendez dans un langage gc'd, car les écritures peuvent faire sortir les structures de données de la jeune génération. Nous n'avons vraiment pas une bonne théorie des effets de cache et de gc, vous n'avez donc pas d'autre choix que de faire un benchmarking.
Deuxièmement, si vous avez besoin d'un canal de diffusion, une structure de données partagée est un excellent moyen de le faire. Avec une mise à jour à temps constant, vous pouvez indiquer arbitrairement à de nombreuses autres personnes qu'une valeur a changé. (C'est pourquoi union-find est une excellente structure de données.) Avec une configuration purement fonctionnelle, vous devez soit modifier toutes ces autres personnes, soit leur donner des pointeurs abstraits dans un état que vous codez manuellement (ce qui est une sorte d'obus chose à faire).
Si vous ne voulez pas raisonner sur l'alias et la propriété des objets, ou si vous avez besoin de plusieurs versions de la même structure de données (vous avez besoin d'une nouvelle et d'une ancienne version, par exemple), alors utilisez simplement une structure de données fonctionnelle.
L'endroit où je trouve le plus difficile à suivre ces conseils est avec les algorithmes de graphes. Il existe de nombreux algorithmes de graphes impératifs vraiment élégants, mais il arrive souvent (par exemple, lors de l'écriture de compilateurs) que vous souhaitiez également la persistance. Les gens essaient généralement de diviser la différence et utilisent l'algorithme impératif cool mais essaient de boulonner le versioning sur le côté pour obtenir la persistance. C'est généralement assez horrible, plein de bugs, et enclin à perdre l'avantage de performance de l'algorithme impératif.