Skip to content

Instantly share code, notes, and snippets.

@MarkRBM
Last active February 20, 2019 00:13
Show Gist options
  • Save MarkRBM/71004abd0d5f379632da235fe946aad4 to your computer and use it in GitHub Desktop.
Save MarkRBM/71004abd0d5f379632da235fe946aad4 to your computer and use it in GitHub Desktop.
trees spec outline
I want to create a generalised tree datastructure that I can use to create arbitrary 'domain specific trees of a specifc type'. some of these `domain specific trees` can be nexted within different `domain specific trees`. some of them cannot and exist in isolation or in a group of tree types that are prevented from being in other tree types
step 1: a datastructure that is a Tree[A] where A is defined as a property of its root Node[A]
step 2: a special instance of the above which is Tree[EntityType] where an entity is a class that extends a specific trait, lets call that trait Entity and we can think of an entity as a row in a db with an id and EntityType as a table in a db. An example tree of this nature might look like so
-EntityTypeTree
-Country
-User
-Building
-Floor
-Room
-User
step3: the ability to use the above tree as the schema for the creation of a different tree a Tree[Assignment]. A example of such a tree might be
-AssignmentTree
-Ireland
-Johnny
-downtown hq
-floor 1
-room 22
-mary
-tomas
-floor 2
-room 85
-timmy
-uptown hq
-floor 1
-room 76
-mark
step4: add a new node type lets call it a BehviorNode that when added to a spot in an assignment tree it creates a subtree of that behavior type and the entity instance nodes inherit the behavior of that particular type of Behavior node
eg a ReservationBehaviorNode will give its child nodes the following behaviors in the following circumstances:
if its a User node that is not a leaf it will be given the ability to create reservations in any rooms in that User Nodes subtree
if its a UserNode that is a subnode of a room it will be given the ability to allow or block reservations made in that room by other users
if its a Room Node it will be able to have reservations made in it
Adding A ReservationBehavior Node to the tree in step 3 specifically might look like this
-AssignmentTree
-Ireland
-Johnny
-downtown hq
-ReservationBehaviorNode
-floor 1
-room 22
-mary
-tomas
-floor 2
-room 85
-timmy
-uptown hq
-floor 1
-room 76
-mark
step 5 I now want to be able to ask the tree above the following two questions
`Is johnny allowed to reserve room 22` and the answer should be no
but if the answer was yes I would then want to ask
`who should johhny ask to approve his reservation in room 22`
I should only be able to ask these questions of a tree of type ReservationBehavior Node
step 6 ultimately there should be arbitrary entity types arbitrary entity instances
and arbitrary behavior nodes, you can ask specific questions of specific behavior nodes
but in the short term even direction on how to create a specific tree with these properties
(has a structure that is defined by another tree, instances of entities in it and a way to
ask it questions about its entities would be a huge help.
abstract class NodeType
sealed trait StructureTreeNodeType extends NodeType
sealed trait EntityTreeNodeType extends NodeType
//Some Enumberation that describes our entity Types
sealed trait EntityType extends StructureTreeNodeType
case object CountryT extends EntityType
case object BuildingT extends EntityType
case object FloorT extends EntityType
case object RoomT extends EntityType
case object UserT extends EntityType
//these can map to our Entities
sealed trait Entity extends EntityTreeNodeType {
def typee = this match {
case Country(_) => CountryT
case Building(_) => BuildingT
case Floor(_) => FloorT
case Room(_) => RoomT
case User(_) => UserT
}
}
case class Country(id: Long) extends Entity
case class Building(id: Long) extends Entity
case class Floor(id: Long) extends Entity
case class Room(id: Long) extends Entity
case class User(id: Long) extends Entity
//This can probably map to a center instance
sealed trait Behavior extends EntityTreeNodeType {
def centerId: Long
}
case class ServiceRequest(centerId: Long) extends Behavior
//this can map to a plain old category as they exist currently
sealed trait Arbitrary extends StructureTreeNodeType with EntityTreeNodeType
//generic representation of a tree parameterised by some type
// from now on they will be paramterised by their node type but they dont have to be
sealed trait Tree[A] {
def label: A
}
sealed trait Node[A] {
def children: Tree[A]
}
sealed trait Leaf[A]
//A tree that represents the structure of other trees
// its nodes and leaves can only be of type StructureTreeNodeType
sealed trait EntityStructureTree extends Tree[StructureTreeNodeType]
sealed trait StructureNode[A] extends EntityStructureTree with Node[A]
sealed trait StructureLeaf[A] extends EntityStructureTree with Leaf[A]
case class EntityStructureNode(label: EntityType, children: EntityStructureTree) extends StructureNode[StructureTreeNodeType]
case class EntityStructureLeaf(label: EntityType) extends StructureLeaf[EntityType]
case class ArbitraryStructureNode(label: Arbitrary, children: EntityStructureTree) extends StructureNode[StructureTreeNodeType]
case class ArbitraryStructureLeaf(label: Arbitrary) extends StructureLeaf[Arbitrary]
//A tree describing an entity assignments its nodes
// and leaves can only be of type EntityTreeNodeType
sealed trait AssignmentTree extends Tree[EntityTreeNodeType]
sealed trait AssignmentNode[A] extends AssignmentTree with Node[A]
sealed trait AssignmentLeaf[A] extends AssignmentTree with Leaf[A]
case class EntityNode(label: Entity, children: AssignmentTree) extends AssignmentNode[EntityTreeNodeType]
case class EntityLeaf(label: Entity) extends AssignmentLeaf[Entity]
case class ArbitraryNode(label: Arbitrary, children: AssignmentTree) extends AssignmentNode[EntityTreeNodeType]
case class ArbitraryLeaf(label: Arbitrary) extends AssignmentLeaf[Arbitrary]
case class BehaviorNode(label: Behavior, children: AssignmentTree) extends AssignmentNode[EntityTreeNodeType]
case class BehaviorLeaf(label: Behavior) extends AssignmentLeaf[Behavior]
// A structured version of the above Assignment tree,
// each node carries with it a EntityStructureTree that
// describes the tree structure from that node down
// you should only be able to create a tree of this type by
// providing an assignment tree and a structure tree but right now
//there is nothing stopping you from doing that
sealed trait StructuredAssignmentTree extends Tree[EntityTreeNodeType] {
def structure: EntityStructureTree
}
object StructuredAssignmentTree {
// take a structure and an assignment tree and return Either a Structured Assignment tree or an error message
def apply(structure: EntityStructureTree, t: AssignmentTree): Either[String, StructuredAssignmentTree] = (structure, t) match {
case (sn: EntityStructureNode, en: EntityNode) if (sn.label == en.label.typee) => apply(sn.children, en.children).map(c => StructuredEntityNode(en.label, sn, c))
case (sl: EntityStructureLeaf, el: EntityLeaf) if (sl.label == el.label.typee) => Right(StructuredEntityLeaf(el.label, sl))
case (asn: ArbitraryStructureNode, an: ArbitraryNode) => apply(asn.children, an.children).map(c => StructuredArbitraryLeaf(an.label, asn))
case (sl: ArbitraryStructureLeaf, al: ArbitraryLeaf) => Right(StructuredArbitraryLeaf(al.label, sl))
case (_, bn: BehaviorNode) => apply(structure, bn.children).map(c => StructuredBehaviorNode(bn.label, structure, c))
case (_, bl: BehaviorLeaf) => Right(StructuredBehaviorLeaf(bl.label, structure))
case _ => Left(s"${t.label} is not a ${structure.label}")
}
}
sealed trait StructuredNode[A] extends StructuredAssignmentTree with Node[A]
sealed trait StructuredLeaf[A] extends StructuredAssignmentTree with Leaf[A]
case class StructuredEntityNode(label: Entity, structure: EntityStructureTree, children: StructuredAssignmentTree) extends StructuredNode[EntityTreeNodeType]
case class StructuredEntityLeaf(label: Entity, structure: EntityStructureTree) extends StructuredLeaf[Entity]
case class StructuredArbitraryNode(label: Arbitrary, structure: EntityStructureTree, children: StructuredAssignmentTree) extends StructuredNode[EntityTreeNodeType]
case class StructuredArbitraryLeaf(label: Arbitrary, structure: EntityStructureTree) extends StructuredLeaf[Arbitrary]
case class StructuredBehaviorNode(label: Behavior, structure: EntityStructureTree, children: StructuredAssignmentTree) extends StructuredNode[EntityTreeNodeType]
case class StructuredBehaviorLeaf(label: Behavior, structure: EntityStructureTree) extends StructuredLeaf[Behavior]
//Our first structure, matches the one described in step 2 of the readme
val structure = EntityStructureNode(
UserT,
EntityStructureNode(
CountryT,
EntityStructureNode(
BuildingT,
EntityStructureNode(
FloorT,
EntityStructureNode(
RoomT,
EntityStructureLeaf(
UserT
)
)
)
)
)
)
val userOne = User(1)
val country = Country(1)
val building = Building(1)
val floor = Floor(1)
val room = Room(1)
val userTwo = User(2)
//our first assignment tree
val assignment = EntityNode(
userOne,
EntityNode(
country,
EntityNode(
building,
EntityNode(
floor,
EntityNode(
room,
EntityLeaf(
userTwo
)
)
)
)
)
)
// our first structured assignment tree, matches the one in step 4 of the read me
val structuredAssignment = StructuredAssignmentTree(structure, assignment)
println(structuredAssignment)
//This will contain functions that know how to traverse our StructuredTree and pluck out various
//paths, nodes and leaves
object BlackBox {
def getStructuredTree(nt: EntityTreeNodeType)(t: StructuredAssignmentTree): Option[StructuredAssignmentTree] = (nt, t) match {
case (e: Entity, en: EntityNode) if (e.typee == en.label.typee) => Some(en)
case (e: Entity, el: EntityLeaf) if (e.typee == el.label.typee) => Some(el)
case (a: Arbitrary, an: ArbitraryNode) => Some(an)
case (a: Arbitrary, al: ArbitraryLeaf) => Some(al)
case (b: Behavior, bn: BehaviorNode) => Some(bn)
case (b: Behavior, bl: BehaviorLeaf) => Some(bl)
case (_, n: StructuredNode[_]) => getStructuredTree(nt)(n.children)
case _ => None
}
}
//the rest of the iofficeconnect app will only
// have access to this interface object
//it will know what entities it needs to answer certain questions
//iofficeconnect needs to know and it will use the methods in Blackbox to
//get those answers
sealed trait RequestType
sealed trait Operator
object Interface {
def getOperatorForRequestType(rt: RequestType): Operator = ???
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment