[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