10
votes

Immutabilité et références partagées - Comment se réconcilier?

Considérez ce domaine d'application simplifié:

  • Base de données d'enquête criminelle LI>
  • personne code> est une personne impliquée dans une enquête li>
  • rapport code> est un peu d'informations qui fait partie d'une enquête li>
  • A rapport code> Références a primaire personne code> (objet d'une enquête) li>
  • A rapport code> a des complices qui sont secondaires (et pourrait certainement être primaire dans d'autres enquêtes ou rapports li>
  • Ces classes ont des identifiants utilisés pour les stocker dans une base de données, car leur information peut changer au fil du temps (par exemple, nous pourrions trouver de nouveaux alias pour une personne ou ajouter des personnes intéressantes à un rapport) LI> ul>

    domaine http://yuml.me/13fc6da0 p>

    si ceux-ci sont stockés dans certains Une sorte de base de données et em> Je souhaite utiliser des objets immuables, il semble y avoir un problème concernant l'état et le référencement. p>

    supposant que je modifie certaines métadonnées sur une personne code>. Depuis mon personne code> immuable, je pourrais avoir du code comme: p> xxx pré>

    de sorte que mon personne code> avec un nouvel alias devient un nouvel objet, également immuable. Si un rapport code> fait référence à cette personne, mais l'alias a été modifié ailleurs dans le système, mon rapport code> fait maintenant référence à la personne "ancienne", c'est-à-dire la personne sans le nouvel alias . p>

    De même, je pourrais avoir: P>

    class Report(val id:UUID, val content:String) {
      /** Adding more info to our report */
      def updateContent(newContent:String) = new Report(id,newContent)
    }
    


2 commentaires

Il est possible de mettre en œuvre une liste liée au lien doublé à Haskell. haskell.org/hakellwiki/tying_the_knot Si vous comprenez cela et qu'il est applicable à votre problème, Ce serait bien si vous rapportez. :-)


@Thomas Jung: Je pense que la solution ci-dessus profite de quelques bizarreries que vous obtenez avec une évaluation paresseuse. Scala, étant évalué avec impatience, doit sauter à travers des cerceaux pour obtenir le même effet. Je pense que la solution la plus simple ici est simplement d'éviter les dépendances cycliques avec des structures de données immuables.


3 Réponses :


10
votes

Si X se réfère à Y, les deux sont immuables, et Y change (vous le remplacer par une copie à jour), alors vous avez pas d'autre choix que de remplacer X aussi (parce qu'il a changé, puisque les nouveaux points de X à la nouveau Y, et non l'ancien).

Cela devient rapidement un casse-tête pour maintenir des structures de données hautement interconnectées. Vous avez trois approches générales.

  • Forget immuabilité en général. Faire les liens mutable. les corriger au besoin. Assurez-vous vraiment les corriger, ou vous pourriez obtenir une fuite de mémoire (X fait référence à l'ancienne Y, qui se réfère à l'ancien X, qui se réfère à plus Y, etc.).
  • Ne pas stocker des liens directs, mais plutôt des codes d'identification que vous pouvez rechercher (par exemple une clé dans une carte de hachage). Vous devez ensuite traiter le cas d'échec de la recherche, mais sinon les choses sont assez robustes. Ceci est un peu plus lent que le lien direct, bien sûr.
  • Changer le monde entier. Si quelque chose a changé, tout ce qui des liens vers elle doit également être modifié (et d'effectuer cette opération simultanément sur un ensemble de données complexes est délicate, mais théoriquement possible, ou tout au moins les aspects mutables de celui-ci peut être caché par exemple avec beaucoup de vals paresseux) .

    Ce qui est préférable dépend de votre taux de mises à jour et les recherches, je me attends.


8 commentaires

Dans la plupart des cas, la deuxième option est la meilleure (utilisez ID comme lien, pas d'adresse)


L'option 2 n'est-elle pas répliquée à quoi le temps d'exécution de la langue fait déjà via des références d'objet? Qu'est-ce qui est gagné par cela? Le temps d'exécution de la langue est probablement mieux implémentant la gestion REF que moi.


@Dave: Non, c'est une façon fondamentalement différente de penser au problème. Une solution est, "x a un y là-bas là-bas". L'autre est, "X a un nom de quelque chose que j'espère être un Y". L'immuabilité dit: "Peu importe que z a maintenant, z aura toujours." Si y est immuable et est remplacé, alors x n'a plus le bon . Mais X a toujours son nom, et peut-être quelque chose d'autre a ce nom maintenant (et cette chose avec le même nom est ce que vous voulez). Ce découplage met à jour sur X et Y, qui est très utile si vous avez réellement de nombreuses choses différentes qui utilisent toutes les unes des autres.


Alors, quel est l'avantage de le faire sur permettre au runtime de le faire pour moi? En faisant juste que mes objets persistés mutables, je reçois tout cela fondamentalement gratuitement.


+1: l'option n ° 2 semble fonctionner mieux. Les dépendances cycliques sont impossibles à mettre en œuvre avec des structures de données évaluées avec impatience et évaluées. Il est beaucoup plus facile de stocker un identifiant et de signaler une personne est liée, puis recherchez ces rapports chaque fois que vous en avez besoin.


@Dave: Il vous suffit de changer une carte de hachage lorsque vous modifiez, disons, un rapport, mais si vous avez des données mutables, vous devez changer chaque personne personne (et tout autre objet) qui se réfère à elle. . Ce n'est pas gratuit. C'est potentiellement beaucoup de logique à avoir raison. Jetez des threads et vous avez maintenant une logique de synchronisation ainsi que la mise à jour de la logique à suivre. Ick!


Qu'en est-il des monades d'État?


@Kaisellgren - qu'en est-il d'eux?



3
votes

Je pense que vous essayez de faire carré du cercle. La personne est immuable, la liste des rapports sur une personne fait partie de la personne et la liste des rapports peut changer.

serait-il possible qu'une personne immuable ait une référence à un personnage mutable qui garde des éléments tels que des rapports et des alias?


2 commentaires

1) La personne n'est pas nécessaire immuable - E.G. Un nom d'une personne peut changer et l'application doit y gérer quelque chose. 2) Il ne sert à rien de faire référence à des objets mutables de l'immutable. L'avantage principal de l'objet immuable est une transparence référencieuse des expressions contenant des fonctions pures et des objets immuables. E.I. De telles expressions renvoient toujours la même valeur, ne cèdent aucune modification de l'état et ne dépendent de modifications de l'état. Cela les rend fiables et thread-coffre-fort. Lorsque vous vous référez à un objet mutable de l'immutable, il n'est plus transparent de manière transparente. Donc - pas de point


Si (1) est le cas, son problème disparaît. Ce que vous dites sous (2) est convaincant sauf considérer l'autre solution proposée: «Ne stockez pas les liens directs, mais plutôt une pièce d'identité». Dans ce cas, un rapport ne signalerait pas une personne mais se réfère à la personne 341, pour laquelle une persondscription actuellement valide et immuable existe. Acceptable, mais si vous utilisez le rapport soi-disant immuable, il est possible qu'une nouvelle persondscription serait générée et POOF, votre transparence référentielle est de nouveau écoutée. Peut-être qu'un modèle transactionnel serait mieux, mais cela a également ses problèmes.



5
votes

Je vous suggère de lire comment ils traitent avec le problème de Clojure et Akka. Lisez à propos de Mémoire transactionnelle logicielle . Et certaines de mes pensées ...

L'immuabilité n'existe pas pour le plaisir de lui-même. L'immuabilité est une abstraction. Il ne "existe pas" dans la nature. Le monde est mutable, le monde change de manière permanente. Il est donc assez naturel que les structures de données soient mutables - ils décrivent l'état de l'objet réel ou simulé à un moment donné. Et ça ressemble à une poule de poudre ci-dessus ici. Au niveau conceptuel, le problème de cette attitude est cet objet dans la RAM! = Objet réel - Les données peuvent être inexactes, il est livré avec le délai, etc.

Ainsi, dans le cas de la plupart des exigences triviales, vous pouvez aller avec tout ce qui est mutable - les personnes, les rapports, etc. Des problèmes pratiques surgiront lorsque:

  1. Les structures de données sont modifiées à partir de threads simultanés
  2. Les utilisateurs fournissent des modifications confinquantes pour les mêmes objets
  3. Un utilisateur fournit une donnée non valide et doit être roulée sur

    avec modèle naïf mutable, vous finirez rapidement avec des données incohérentes et un système de concassage incohérent. L'utabilité est sujette d'erreur, l'immuabilité est impossible. Ce dont vous avez besoin est une vue transactionnelle du monde. Dans le programme de transaction voit un monde immuable. Et STM gère des modifications à appliquer de manière cohérente et filetée.


0 commentaires