Hi, I know that seq[T] only accepts one type of data. Is there any container data type like list in Python ? In which, we can put int, float, string, objects etc at the same time. See this code snippet. This is what i want.
type anObject* = ref object
objList : # Here, i want to declare a list but untyped.
count : int
proc objInit*() : anObject =
new(result)
result.objList = () # Here, i want the list to initialize, but it is still empty.
result.count = 0
var aObj = objInit()
aObj.add("Hello") # Add a string
aObj.add(1589) # add an int
aObj.add(someObject) #add an object
How to make this type of list. I tested with "seq[untyped]", "seq[auto]", "tuple[]" but all are failed.Check out the json module: https://nim-lang.org/docs/json.html
import json
var aObj = %*["Hello", 1589, {"count": 1}]
echo aObj # ["Hello",1589,{"count":1}]
Is there any container data type like list in Python ? In which, we can put int, float, string, objects etc at the same time
If you're using lists in Python for that — you're not using lists, you are abusing them!
Although Python allows lists to have elements of a different type, you should use tuples for that.
In Nim, a tuple will do the trick, too. If that is not flexible enough for you, take a look at json module, as suggested by @flaviu.
Thanks for the reply. Well, i am not using python. But i know that python has a container data type which contains all different types. Anyway, including another module to my project is the last option for me. I just tested with tuple like this. But not succeeded.
list : tuple[] # this is declaration inside a type.
This is the intit section
result.list = ()
This is the usage ;
a.list.add(586) # This gives error msg - Error: type mismatch: got <tuple of (int, int)> but expected 'tuple[]'
a.list[0] = 586 # This also gave error msg - Error: invalid index value for tuple subscript
I dont know how to use tuple in this scenario. Documentation didnt says anything like this. That means declaration in one location, init in another location, populating in third location. I never used the word string in this declaration. Well Nim is a statically typed language safety in mind so its not really designed to do what you request.
If you know what all the possible types are you can use a sequence of object variants. ( This is basically what the JSON library is anyway )
type
NodeKind = enum # the different node types
nkInt, # a leaf with an integer value
nkFloat, # a leaf with a float value
nkString # a leaf with a string value
Node = ref object
case kind: NodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
of nkString: strVal: string
var n = @[
Node(kind: nkFloat, floatVal: 1.0),
Node(kind: nkString, strVal: "Simple")
]
n.add Node(kind: nkInt, intVal: 3)
Hi, thanks for the reply. If thats the case, then i think i should need overloaded functions for this. One string seq and 3 functions to fill the seq. One for string, one for integer, on for decimals. Anyway, i didnt understand the your code properly. Could you please explain a little bit. especially this part.
Node = ref object
case kind: NodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
of nkString: strVal: string
Is it connected to my scenario ? I assume that this the meaning of this code.
Please tell me that my assumption is right or wrong.
In my case, i have list or tuple declared inside a tuple. And initialized it inside a proc. And filled when we use it.
I might be able to help
1.Node is a ref object.
Yes, it is. Acting like a pointer (guessing at the inner workings of this) with the statement
case kind: NodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
of nkString: strVal: string
kind is declared in the type like a variable and the compiler will create separate "Node types" depending on whether or not it is nkInt, nkFloat, or nkString, I assume.
so
2.kind is not declared in this type. Or this case statement is declaring kind as a varibale, i don't know.
kind is declared as a variable
3. If the latter is true then, case statement is checking the data type and allow us to use this type for required data type.
4.And in the last portion, you have declared a varibale "n" which contains a seq of nodes.
yes
If I am wrong please let me know, I'm just really starting to learn Nim myself
For heterogeneous container you need to use some kind of type erasure, this is true in all statically typed languages.
You have 2 ways to do that in Nim:
Either use object variants:
type
NodeKind = enum # Discriminant
nkInt,
nkFloat,
nkString
Node = object
case kind: NodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floatVal: float
of nkString: strVal: string
var a: seq[Node]
let b = Node(kind: nkInt, intVal: 10)
a.add b
Or use "boxed types" (i.e. hide everything behind a pointer using inheritance). All dynamically typed language use the latter but it comes at performance penalty because you have apointer indirection everytime you want to use the data value.
type
Boxer = ref object of RootObj
Boxed[T] = ref object of Boxer
data: T
var a: seq[Boxer]
let b = Boxed[int](data: 10)
a.add b
(But i think you forgot to init the seq with "@[]")
@mratsim is using devel version of Nim, and in it (and in the upcoming v0.19) you don't have to explicitly do that anymore. (The same goes for strings)
@kcvinu @miran, yes I'm using devel.
I also prefer tagged union (also called sum types) in general to avoid the pointer indirection.
Just be aware that with the second solution (inheritance/polymorphism) any user of your library can extend your base type with its own and reuse the methods you define while with the first solution (object variant/tagged union/sum type) adding a new subtype means modifying all proc using this type.
The following blog post "Sum Types Are Coming" summarizes that very well:
With sum types, it’s easy to add uses but hard to add cases. > With interfaces, it’s easy to add cases but hard to add uses.
Uses are new procs: it's easy to add new ones with sum types "proc foo = case ...", while with interfaces you need to update the base interface and add methods for each derived classes.
Cases are new subtypes: just derive from the base type in interfaces, but with sum types you need to update your case statement in all the procs.