\n\"\"\"\n\n# a custom text block that shows markdown source\ntemplate nbTextWithSource*(body: untyped) =\n newNbBlock(\"nbTextWithSource\", false, nb, nb.blk, body):\n nb.blk.output = body\n nb.blk.context[\"code\"] = body\n\nnb.renderPlans[\"nbTextWithSource\"] = @[\"mdOutputToHtml\"]\nnb.partials[\"nbTextWithSource\"] = \"\"\"{{&outputToHtml}}\n
{{code}}
\"\"\"\n\n# how to add a ToC\nvar\n nbToc: NbBlock\n\ntemplate addToc =\n newNbBlock(\"nbText\", false, nb, nbToc, \"\"):\n nbToc.output = \"## Table of Contents:\\n\\n\"\n\ntemplate nbSection(name:string) =\n let anchorName = name.toLower.replace(\" \", \"-\")\n nbText \"\\n## \" & name & \"\\n\\n---\"\n # see below, but any number works for a numbered list\n nbToc.output.add \"1. \" & name & \"\\n\" \n \nnbSection \"INTRO - GREETING\"\nnbText: hlMdf\"\"\"\n#Welcome to another video of my \n #Nim for Beginners video series, \n #Object Variants \n\"\"\"\n\nnbSection \"INTRO - FOREWORDS\"\nnbText: hlMdf\"\"\"\nWhat is the purpose of this video ?\n In this video i will teach you about object variants, what they are and how to use them,\n with concrete examples.\n\n Chapters/sub topics of this video will be:\n What are object variants ?\n Shared fields\n Circle-Ellipse problem\n Object Variants vs Inheritance\n Child A to Child B conversion(inheritance requires casting, which is generally unsafe)\n Performance comparison(Object Variants win by a lot)\n When to use Object Variants vs Inheritance?\n\"\"\"\n\n#TABLE OF CONTENTS\naddToc()\n\nnbSection \"Documented Code - Script - Offline Tutorial\"\n\nnbSection \"TO DO\"\nnbText: hlMdf\"\"\"\n #Example showing circle, ellipse problem\n #Show performance comparison between Inheritance and Object Variants\n #Show the profiling data of callgrind tool and cachegrind in Kcachegrind program,\n #demonstrating the performance benefit of using Object Variants vs Inheritance\n #Explain Methods for a more OOP style programing(not real OOP), which we used for performance comparison\n\"\"\"\n\n\nnbSection \"What are object variants ?\"\nnbText: hlMdf\"\"\"\nObject variants are when making an object and using logic,\nto split it into several unique parts.\nThis is done by using a case statement and enumerators. \nObject variants are an easy way of having as the name suggests variants of the same object.\n\nSometimes or often an object hierarchy would be an overkill, needless complication.\nEXPLAIN THIS\n \nLet's start with a a simple example:\nFirst we make an enumerator withing a type section, \nwith it's enumerations of line, circle, rectangle \n\"\"\"\n\nnbCode:\n type\n Kind = enum\n line, circle, rectangle\n\nnbText: hlMdf\"\"\"\n #Secondly we make our object with it's variants by using a case statement,\n #with the enumerator from above like this: \n\"\"\"\n\nnbCode: \n type\n Draw = object\n case kind: Kind\n of line:\n lx, ly: int\n of circle:\n cx, cy: int\n #pi = 3.14\n of rectangle:\n rx1, ry1, rx2, ry2: int\n \nnbText: hlMdf\"\"\" \nHow this works is simple, you initialize a new variable of type Draw object,\nand then for it's field select one of it's kind enumerations, \nand then based on the kind of the object, you give that kind's field's data,\nand thus you have a variant of that object.\nAlso note how we used different field names for each of the variants,\nthat is because they cannot be the same, \nsince they are declared in the same object.\n\nLet's demonstrate this by making a variable for each of the object variants \nand echo them: \n\"\"\"\n\nnbCode:\n var drawLine = Draw(kind: line, lx: 10, ly: 50)\n var drawCircle = Draw(kind: circle, cx: 10, cy: 50)\n var drawRectangle = Draw(kind: rectangle, rx1: 10, ry1: 50, rx2: 10)\n\n echo drawLine, \" drawLine's type is: \", drawLine.type \n echo drawCircle, \" drawCircle's type is: \", drawCircle.type \n echo drawRectangle, \" drawRectangle's type is: \", drawRectangle.type \n\nnbText: hlMdf\"\"\" \nHere we go 3 different variants of the same object Draw.\nWe can also display their kind such as line, circle, \nrectangle that the case statement uses,\nso let's display that using \"variableName.kind\" \n\"\"\"\n\nnbCode:\n echo drawLine, \" drawLine's type is: \", drawLine.type, \" of kind: \", drawLine.kind\n echo drawCircle, \" drawCircle's type is: \", drawCircle.type, \" of kind: \", drawCircle.kind \n echo drawRectangle, \" drawRectangle's type is: \", drawRectangle.type, \" of kind: \", drawRectangle.kind \n\nnbText: hlMdf\"\"\"\nHere we go it works. \n\"\"\"\n\nnbSection \"Shared fields\"\nnbText: hlMdf\"\"\"\nWhen using Object Variants we can easily share fields between them all as well,\nthis is as simple as adding those fields before the case statement.\nLet's try this with a different example: \n\"\"\"\n\nnbCode:\n type\n Enemies1 = enum\n footman, mage\n\n Armor = enum\n light, medium, heavy\n\n Spells = enum\n root, heal, arcaneMissiles\n\n Enemy = object\n damage: int\n attackRange: int\n health: int\n\n case kind: Enemies1\n of footman:\n armor: Armor\n of mage:\n spells: seq[Spells]\n\nnbText: hlMdf\"\"\"\nOkay here i have made 3 enumerators, \n1x for the Enemies1 with footman and mage as it's enumerations,\nanother for Armour our footman will be using and Spells for the mage.\n\nIn the object i have added 3 fields, \nthat will be shared between both of the Object Variants,\nof damage, attackRange and health.\nAnd then i gave both of the variants their special field, \nof the enumerator i have made for each of them.\n\nNow let's initialize a variable for each Object Variant kind and then display them both: \n\"\"\"\n\nnbCode:\n var soldier = Enemy(kind: footman, armor: medium, health: 200, damage: 5, attackRange: 100)\n var sorcerer = Enemy(kind: mage, spells: @[root, heal], health: 75, damage: 10, attackRange: 600)\n\n echo soldier, \" soldier's type is: \", soldier.kind\n echo sorcerer, \" sorcerer's type is: \", sorcerer.kind\n\nnbText: hlMdf\"\"\"\nHere we go, that's how you do Object Variants and have shared fields for them. \n\"\"\"\n\n\nnbSection \"Circle-Ellipse problem\"\nnbText: hlMdf\"\"\"\nObject variants, nuff said... no really... xD...To be continued... a bit. This is not an important topic. \n\"\"\"\n\nnbSection \"Object Variants vs Inheritance\"\nnbText: hlMdf\"\"\"\n#Object variants are always stored in memory in it's full size.\n#Now this allows object variants, variants of an object to be stored in containers such as sequences.\n#This also allows changing variants into other variants, which is NOT possible with inheritance. \n\"\"\"\n\nnbSection \"INHERITANCE\"\nnbCode:\n import random\n\n type\n EnemyI = ref object of RootObj\n x, y: float\n parent: EnemyI\n\n HumandoidAttackMovesI = enum\n backstabI, eviscerateI\n\n AnimalAttackMovesI = enum\n biteI, clawI\n\n BanditI = ref object of EnemyI \n attackMoves: seq[HumandoidAttackMovesI]\n\n WolfI = ref object of EnemyI\n attackMoves: seq[AnimalAttackMovesI]\n\nnbText: hlMdf\"\"\"\n#Methods require objects\n#Base methods should be BASE, as minimal as possible\n#base pragma is implicitly placed, but YOU should place it manually! But why ?... i don't know...\n\"\"\"\n\nnbCode:\n method newEnemyI(b: EnemyI): EnemyI {.base.} = b \n method newEnemyI(b: BanditI): BanditI {.base.} = b\n method newEnemyI(b: WolfI): WolfI {.base.} = b\n\n method attackI(this: EnemyI): string {.base.} = \"...\"\n\n method attackI(this: BanditI): string =\n randomize()\n let max = this.attackMoves.high\n result = $this.attackMoves[0#[ rand(0..max) ]#]\n\n method attackI(this: WolfI): string =\n randomize()\n let max = this.attackMoves.high\n result = $this.attackMoves[0#[ rand(0..max) ]#]\n\n var overlordI = EnemyI()\n var enemiesI: seq[EnemyI]\n\n for e in 0..99:\n let x = rand(0..1)\n\n if x == 0:\n enemiesI.add newEnemyI(BanditI(x: e.toFloat, y: e.toFloat, attackMoves: @[backstabI], parent: overlordI))\n if x == 1:\n enemiesI.add newEnemyI(WolfI(x: e.toFloat, y: e.toFloat, attackMoves: @[biteI], parent: overlordI))\n\n enemiesI.add overlordI #Used simply to meet the requirements of methods, otherwise we could use procs instead\n\n #[ if overlordI != nil: ]#\n for e in enemiesI:\n echo e.attackI\n\nnbText: hlMdf\"\"\"\nRequires casting in order to change child A to child B\nType casts are a crude mechanism to interpret the bit pattern of an expression as if it would be of another type. \nType casts are only needed for low-level programming and are inherently unsafe.\nCasting is interpretation, not type conversion, the bit pattern of the data remains the same\n#[ b = cast[BanditI](w)\n#echo b.attack ]\n\"\"\"\n\nnbSection \"OBJECT VARIANTS\"\nnbCode:\n type\n Enemies = enum\n bandit, wolf\n\n HumanoidAttackMovesV = enum\n backstabV, eviscerateV\n AnimalAttackMovesV = enum\n biteV, clawV\n\n EnemyV = object\n x, y: float\n\n case kind: Enemies\n of bandit:\n attackMovesB: seq[HumanoidAttackMovesV]\n of wolf:\n attackMovesW: seq[AnimalAttackMovesV]\n\n proc newEnemyV(x, y: float, aMoves: seq[HumanoidAttackMovesV]): EnemyV =\n result = EnemyV(x: x, y: y, kind: bandit, attackMovesB: aMoves)\n\n proc newEnemyV(x, y: float, aMoves: seq[AnimalAttackMovesV]): EnemyV =\n result = EnemyV(x: x, y: y, kind: wolf, attackMovesW: aMoves)\n\n proc attackV(this: EnemyV): string =\n case this.kind:\n of bandit:\n randomize()\n let max = this.attackMovesB.high\n result = $this.attackMovesB[0#[ rand(0..max) ]#]\n of wolf:\n randomize()\n let max = this.attackMovesW.high\n result = $this.attackMovesW[0#[ rand(0..max) ]#]\n\n echo \"\"\n var enemiesV: seq[EnemyV]\n\n for e in 0..99:\n let x = rand(0..1)\n\n if x == 0:\n enemiesV.add newEnemyV(e.toFloat, e.toFloat, @[backstabV])\n if x == 1:\n enemiesV.add newEnemyV(e.toFloat, e.toFloat, @[biteV])\n\n var overlordV = EnemyV()\n #[ enemiesV.add overlordV ]# #This will crash, overlord.attackV ...\n\n #Figure out how to use overlord to begin attacks, if overlord in enemiesV doesn't work\n #since it's a singular check... we just need a confirmation of some sort...\n\n for e in enemiesV:\n echo e.attackV\n\n\nnbSection \"Child A to Child B conversion\"\nnbText: hlMdf\"\"\"\nEasy changing between variants of an object, no casting required(inheritance)\nStoring into containers, e.g. sequences -> inheritance doesn't appear to have any problems there...\n\"\"\"\n\nnbSection \"Performance comparison\"\nnbText: hlMdf\"\"\"\nReason 1 that object variants are faster:\nInheritance works best with refs(and ptr), at this point inheritance should be very fast,\nprobably even faster than object variants, but then you are required to use methods.\nMethods are dynamic dispatch and it is slow,\ntherefore with a complex Parent - Child object, you will have many methods,\nand with that many method calls which will be much slower than object variants with procs.\nVALGRIND RESULTS: https://postimg.cc/rRz2dHFf\nhttps://dev.to/ringabout/sweet-sweet-sweeeeeet-nim-nh9\n\"\"\"\n\nnbSection \"When to use Object Variants vs Inheritance?\"\nnbText: hlMdf\"\"\"\nUse methods if you want unbounded polymorphism \n(can add new kinds of thing without having to go update a master list of things, \nuseful if you're expecting to be adding new things all the time, \nor if you're making a library and want users to be able to make their own kinds). \nOtherwise use object variants.\n\nProcs use static dispatch, compiler will match a proc to the FIRST object call, \nwith inheritance, the parent, all of it's children's calls will be ignored.\nDynamic dispatch are methods, the matching will be done at run-time, allowing more flexibility,\nat the cost of performance. Methods will not stop at the parent of inheritance, \nbut find the most appropriate matching child.\n\nExplanation of base pragma: base is like virtual in C++. \nIt marks that a method can be overloaded in subclasses and dynamically dispatched.\nFirst, dynamic dispatching pretty much requires ref, \nbecause it requires a situation where the runtime type of the object isn't known at compile time.\nThe pragma seems to be inserted implicitly.\n\nBase pragma was required back when Nim had multi methods. \nNow it's still useful, C# uses an explicit \"override\" keyword to override methods, \noverride in Nim is the default but the downside is that \"base\" methods have to be annotated.\n\"\"\"\n\nnbCode:\n type\n Parent = ref object of RootObj\n a: int\n Child = ref object of Parent\n b: int\n\n method somefn(p: Parent): string #[ {.base.} ]# = \"parent\"\n method somefn(c: Child): string = \"child\"\n\n var p = Parent()\n let c = Child()\n\n echo p.somefn() # \"parent\"\n echo c.somefn() # \"child\"\n\n p = c\n echo p.somefn() # \"child\"\n\nnbText: hlMdf\"\"\"\nNothing changes, except that we get a Warning: use base pragma for base methods; baseless methods are deprecated [UseBase]\nThe pragma seems to be inserted implicitly.\n\nREAD MORE ON METHODS...\nhttps://matthiashager.com/proc-method-nim\nmore links on that link,\nalso: https://nim-by-example.github.io/oop/\n\"\"\"\n\nnbText: hlMdf\"\"\"\n# OUTRO - AFTERWORDS #\n\n Okay, that's it for this video, thanks for watching like, share and subscribe, \n aswell as click the bell icon if you liked it, \n you can also follow me on twitter of the same name, and support me on Patreon. \n If you had any problems with any part of the video, \n let me know in the comment section, \n the code for this video is in the link in the description, \n as well as the link to this video's documentation/script,\n as a form of offline tutorial, have fun.\n\n- Version used: E.G. 1.6.6\n- Compiler settings used: --gc:orc\n- Timestamps:\n - 00:15 Start of video\n\n\nLINKS:\n- [Twitter](https://twitter.com/Kiloneie \"My Twitter\")\n- [Patreon](https://www.patreon.com/Kiloneie?fan_landing=true \"Patreon\")\n- Code link:\n- Code documentation/offline tutorial:\n- [Visual Studio Code Shortcuts](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-windows.pdf \"Visual Studio Code Shortcuts\")\n\nLINKS to this video's subject:\n- [E.G.1. SDL2_nim documentation](https://vladar4.github.io/sdl2_nim/ \"Example link to an example video's subject\")\n- [E.G.2. SDL2 documentation(in case SDL2_nim documentation missed something)](https://wiki.libsdl.org/APIByCategory \"Example link to an example video's subject\")\n\"\"\"\n\nnbSave()","codes":[],"stdin":"","compiler":"nim-1.6.6","options":"","compilerOptionRaw":"","runtimeOptionRaw":"","createdAt":1663950905,"title":"My problematic script","description":"","githubUser":"","isPrivate":false,"compilerInfo":{"name":"nim-1.6.6","version":"1.6.6","language":"Nim","displayName":"nim","templates":["nim"],"compilerOptionRaw":true,"runtimeOptionRaw":true,"displayCompileCommand":"nim c ./prog.nim","switches":[]}},"results":[{"type":"Control","data":"Start"},{"type":"CompilerMessageE","data":"Hint: used config file '/opt/wandbox/nim-1.6.6/config/nim.cfg' [Conf]\nHint: used config file '/opt/wandbox/nim-1.6.6/config/config.nims' [Conf]\n"},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"."},{"type":"CompilerMessageE","data":"\n/home/jail/prog.nim(1, 8) Error: cannot open file: nimib\n"},{"type":"ExitCode","data":"1"},{"type":"Control","data":"Finish"}]}}