-
-
Save cryzed/cc42a92ab57a8c140a5c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import strutils | |
type | |
NodeKind = enum | |
NodeKind1, NodeKind2, NodeKind3 | |
# Only some Nodes should have children. You can't name two attributes within a | |
# type definition using a case statement the same as of yet (probably a bug). | |
# You will get a redefinition error for the attribute, even though both | |
# branches are never active at the same time. | |
Node = ref object | |
value: string | |
case kind: NodeKind | |
of NodeKind1: | |
internalChildren: seq[Node] | |
of NodeKind2: | |
internalChildren2: seq[Node] | |
of NodeKind3: | |
something: string | |
# So we write a custom accessor to transparently grab the correct children | |
# attribute for the Node and raise an exception for Node types that do not have | |
# a children attribute. | |
proc children(node: Node): var seq[Node] = | |
case node.kind: | |
of NodeKind1: | |
return node.internalChildren | |
of NodeKind2: | |
return node.internalChildren2 | |
else: | |
raise newException(ValueError, "$1 can't have children".format(node.kind)) | |
proc main() = | |
let node = Node(kind: NodeKind1, value: "Hello World", internalChildren: @[]) | |
let childNode = Node(kind: NodeKind1, value: "Child 1", internalChildren: @[]) | |
node.internalChildren.add childNode | |
# Works exactly as expected! But we have to use internalChildren and | |
# internalChildren2 depending on type. Now let's attempt to use our accessor | |
# proc to hide that complexity from us: | |
echo node.repr | |
# This transparently calls the children proc defined above and returns the | |
# correct internalChildren field of the given node type variant: | |
var referenceToChildren = node.children | |
let childNode2 = Node(kind: NodeKind1, value: "Child 2", internalChildren: @[]) | |
referenceToChildren.add childNode2 | |
# Child 2 doesn't appear in the object structure, node.children was copied | |
# during assignment to referenceToChildren and then modified, even though it | |
# is marked as a var (mutable) return type | |
echo node.repr | |
# What was supposed to be modified: | |
echo "node.internalChildren: ", node.internalChildren.repr | |
# What was modified: | |
echo "referenceToChildren: ", referenceToChildren.repr | |
# When leaving out the intermediate assignment to a temporary variable: | |
node.children.add childNode2 | |
# This works! | |
echo node.repr | |
# The rationale behind wanting this to work as I expected: I want to avoid | |
# having to execute the children proc twice (even if the overhead is | |
# negligible). Computing the same thing twice just doesn't make sense, and who | |
# is to say that the proc in question will always be this cheap? | |
# Output: | |
discard """ | |
ref 00328028 --> [value = 00329028"Hello World", | |
NodeKind1internalChildren = 00329048[ref 00328040 --> [value = 00328058"Child 1", | |
NodeKind1internalChildren = 0032a038[]]]] | |
ref 00328028 --> [value = 00329028"Hello World", | |
NodeKind1internalChildren = 00329048[ref 00328040 --> [value = 00328058"Child 1", | |
NodeKind1internalChildren = 0032a038[]]]] | |
ref 00328028 --> [value = 00329028"Hello World", | |
NodeKind1internalChildren = 00329048[ref 00328040 --> [value = 00328058"Child 1", | |
NodeKind1internalChildren = 0032a038[]], ref 003280e8 --> [value = 00328100"Child 2", | |
NodeKind1internalChildren = 0032a028[]]]] | |
""" | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment