Skip to content

Instantly share code, notes, and snippets.

@cryzed

cryzed/test3.nim Secret

Last active December 7, 2015 17:27
Show Gist options
  • Save cryzed/cc42a92ab57a8c140a5c to your computer and use it in GitHub Desktop.
Save cryzed/cc42a92ab57a8c140a5c to your computer and use it in GitHub Desktop.
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