3

I want to substitute a specific AST node into another, and this substituted node is specified by interactive user input.

In non-functional programming, you can use mutable data structure, and each AST node have a object reference, so when I need to reference to a specific node, I can use this reference.

But in functional programming, use IORef is not recommended, so I need to generate id for each AST node, and I want this id to be stable, which means:

  1. when a node is not changed, the generated id will also not change.
  2. when a child node is changed, it's parent's id will not change.

And, to make it clear that it is an id instead of a hash value:

  1. for two different sub nodes, which are compared equal, but corresponding to different part of an expression, they should have different id.

So, what should I do to approach this?

luochen1990
  • 3,689
  • 1
  • 22
  • 37
  • Instead of `IORef` you can just use `STRef`. – Shersh Jun 21 '18 at 06:03
  • @Shersh thx, but I'm still wondering is there any more pretty and functional approach? – luochen1990 Jun 21 '18 at 06:05
  • 2
    So I think what you’re trying to do is a poor mans reference, where you have some mapping of id to node and a reference is an id that you can look up in the mapping? The functional way is to not have circular references on things you want to change. One way to be able to generate ids is to use a state monad: read the state and put back state*1 whenever you want a new id. – Dan Robertson Jun 21 '18 at 09:03
  • "interactively editing a tree" is what zippers were invented for – Benjamin Hodgson Jun 22 '18 at 16:17
  • @BenjaminHodgson Only if the edits are always "localized", always defined in relation to the previous one. But for "random" arbitrary edits, I'm not sure maintaining zipper would help. – danidiaz Jun 22 '18 at 18:35

1 Answers1

2

Perhaps you could use the path from the root to the node as the id of that node. For example, for the datatype

data AST = Lit Int
         | Add AST AST
         | Neg AST

You could have something like

data ASTPathPiece = AddGoLeft
                  | AddGoRight
                  | NegGoDown

type ASTPath = [ASTPathPiece]

This satisfies conditions 2 and 3 but, alas, it doesn't satisfy 1 in general. The index into a list will change if you insert a node in a previous position, for example.

If you are rendering the AST into another format, perhaps you could add hidden attributes in the result nodes that identified which ASTPathPiece led to them. Traversing the result nodes upwards to the root would let you reconstruct the ASTPath.

danidiaz
  • 26,936
  • 4
  • 45
  • 95