[MLton-user] datatypes, opaque ascription and case ... of

Raymond Racine rracine@adelphia.net
Sun, 24 Oct 2004 13:06:25 -0400


Question:

I've been working on (amidst other things) a SML based Rete (forward
production inference engine) a la Jess, OPS5, Clips,...  

Its done and I am trying to clean it up to a releasable state.

The literature and implementations that I have used to guide me have
taken an OO type approach and while I think I can "unravel" this later I
ended up with a rather interesting datatype.

  datatype wme = NilWme |
	   Wme of {subject: S.symbol, 
		   predicate: S.symbol, 
		   object: S.symbol,
		   (* alpha memories containing this wme *)
		   alphaMemItems: item_in_alpha_memory list ref, 
		   (* tokens where the wme is this wme *)
		   tokens: token BaseDList.dlist, 
		   negativeJoinResults: negative_join_result list ref}
		  
  and token =  
      NilToken |
      Token of {(* points to the higher token, for items 1...i-1 *)
		parent: token,
		(* this is the ith item in the token chain *)
		wme: wme,
		(* points to the memory this token is in *)
		node: rete_node,
		(* the ones with parent=this token *)
		children: token BaseDList.dlist, 
		variant: token_variant option,
		(* A Token may participate in 3 double linked lists, we need
snipability *)
		in_memory_prev: token BaseDList.dnode ref,
		in_memory_next: token BaseDList.dnode ref,
		in_wme_prev: token BaseDList.dnode ref,
		in_wme_next: token BaseDList.dnode ref,
		in_parent_prev: token BaseDList.dnode ref,
		in_parent_next: token BaseDList.dnode ref}
	       
  and rete_node = 
      NilReteNode 
    | BetaNode of {guid: int, 
		   parent: rete_node, 
		   children: rete_node BaseDList.dlist, 
		   in_children_prev: rete_node BaseDList.dnode ref,
		   in_children_next: rete_node BaseDList.dnode ref,
		   items: token BaseDList.dlist,  
		   allChildren: rete_node BaseDList.dlist}  
    | JoinNode of {guid: int, 
		   parent: rete_node, 
		   children: rete_node BaseDList.dlist,  
		   in_children_prev: rete_node BaseDList.dnode ref,
		   in_children_next: rete_node BaseDList.dnode ref,
		   alphaMemory: alpha_node, 
		   in_alpha_prev: rete_node BaseDList.dnode ref,
		   in_alpha_next: rete_node BaseDList.dnode ref,
		   tests: JNT.join_node_test list ref,
		   nearestAncestor: rete_node} 
    | ProductionNode of {guid: int, 
			 parent: rete_node, 
			 in_children_prev: rete_node BaseDList.dnode ref,
			 in_children_next: rete_node BaseDList.dnode ref,
			 action: production_action, 
			 bindings : B.wme_binding list}
    | NegativeNode of {items: token BaseDList.dlist,  
		       children: rete_node BaseDList.dlist, 
		       in_children_prev: rete_node BaseDList.dnode ref,
		       in_children_next: rete_node BaseDList.dnode ref,
		       amem: alpha_node, 
		       in_alpha_prev: rete_node BaseDList.dnode ref,
		       in_alpha_next: rete_node BaseDList.dnode ref,
		       tests: JNT.join_node_test list, 
		       nearestAncestor: rete_node}
    | NCCNode of {parent: rete_node,
		  children: rete_node BaseDList.dlist, 
		  in_children_prev: rete_node BaseDList.dnode ref,
		  in_children_next: rete_node BaseDList.dnode ref,
		  items: token BaseDList.dlist, 
		  partner: rete_node}
    | NCCPartnerNode of {parent: rete_node,
			 nccnode: rete_node, 
			 children: rete_node BaseDList.dlist, 
			 in_children_prev: rete_node BaseDList.dnode ref,
			 in_children_next: rete_node BaseDList.dnode ref,
			 conjunctions: int, 
			 resultBuffer: token list ref}
			
  and alpha_node = AlphaNode of {guid: int, 
				items: item_in_alpha_memory 				 BaseDList.dlist, 
				 successors: rete_node BaseDList.dlist, 
				 refcount: int ref}
				
  and negative_join_result = NegativeJoinResult of {owner: token, 
						    wme: wme}
						   
						   
  and item_in_alpha_memory = ItemInAlphaMemory of {wme: wme, 
						   amem: alpha_node,
						   in_alpha_prev: 			item_in_alpha_memory BaseDList.dnode ref,
						   in_alpha_next: 			item_in_alpha_memory BaseDList.dnode ref}
						  
  withtype tokens = token list
  and token_variant = {owner: token ref,
		       joinResults: negative_join_result list ref,
		       nccResults: token BaseDList.dlist,
		       (* participates in a negation or ncc result dll *)
		       in_result_prev: token BaseDList.dnode ref,
		       in_result_next: token BaseDList.dnode ref}


Right, I know what you all must be thinking. But like I said, I hope to
unravel it all later.

In my sig, I do NOT want to declare this monster and wish to treat these
datatypes as ADTs so all I declare currently is

sig
...
type rete_node
...
end

I do not want the tagged records of the rete_node datatype to be
visible. i.e. BetaNode {guid,parent...} but I do want the BetaNode
aspect to be visible for "case ... of". 

So somewhere else, in some other module, I want to say:

case retenode of
	BetaNode bnode => ....

Options that I can see.

1. Put that whole mess above in the signature.  I can "case ... of" now,
but I lose any semblance of an ADT.  I exposed everything.

2. Add my own internal case function that functions in other modules can
call to find out the type of a rete_node datatype.  Ok but seems
redundant (though now that I've typed all this does seem as bad when I
was just thinking about it)

signature RETE_NODE =
sig

datatype rete_node_type = TBetaNode | TJoinNode | TNCCNode | so on

type rete_node

val reteNodeType: rete_node -> rete_node_type

end

structure ReteNode:> RETE_NODE =
struct

datatype rete_node = THAT REALLY HAIRY MESS ABOVE

datatype rete_node_type = TBetaNode | TJoinNode | TNCCNode | so on

fun reteNodeType rnode = case rnode of 
                                    BetaNode _ => TBetaNode
                                  | JoinNode _ => TJoinNode
                                  | so on and so on
end

Is there some other trick??  

In summary, I want to expose a datatype just enough to perform a "case
.. of" on it but all other internal knowledge about the datatype remains
opaque.

God, I hope I got this across ok.

Thanks in advance.

Ray