0

It's possible to manually convert seq[Child] to seq[Parent] but maybe there's a better option?

type
  ParentRef = ref object of RootObj
    a: int

  ChildRef = ref object of ParentRef
    b: int
  
let parents = @[ParentRef()]
let children = @[ChildRef()]

proc process(list: seq[ParentRef]): void = discard list
process(parents)
process(children) # <== error
Alex Craft
  • 13,598
  • 11
  • 69
  • 133
  • ``map`` isn't a generic, so annotating it with types isn't meaningful. It's possible to cast Child to Parent if they are ``ref objects`` e.g process(children.mapIt(it.Parent)) – shirleyquirk Sep 15 '20 at 17:37
  • @shirleyquirk thanks, my bad I intended to use `ref`, updated the example. – Alex Craft Sep 16 '20 at 01:28

2 Answers2

3

Maybe the better option is to use a generic proc?

type
  Parent = object of RootObj
    a: int
  Child = object of Parent
    b: int
  
let parents = @[Parent(a: 2)]
let children = @[Child(a: 3, b: 5)]

proc process[T: Parent](list: seq[T]): void =
  echo repr(list)
  echo "Accessing generic attribute a: ", list[0].a

process(parents)
process(children)

In fact, if you don't add the T : Parent restriction that proc will work for anything as long as the compiler finds all the fields it wants on the type:

type
  Parent = object of RootObj
    a: int
  Child = object of Parent
    b: int

  FooBar = object of RootObj
    a: int
    bar: string
  
let parents = @[Parent(a: 2)]
let children = @[Child(a: 3, b: 5)]
let foobars = @[FooBar(a: 42, bar: "Yohoo")]

proc process[T](list: seq[T]): void =
  echo repr(list)
  echo "Accessing generic attribute a: ", list[0].a

process(parents)
process(children)
process(foobars)
Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78
3

Nim has a stronger type system than many, by default it only implicitly converts types according to these rules.

We can see there that a sub-class is convertible to its superclass, but seq[type1] is only convertible to seq[type2] if type1==type2, i.e. they are identical, not subtypes.

To add another implicit conversion relationship, one defines a type-converter, either case by case:

converter seqChildToSeqParent(c:seq[ChildRef]):seq[ParentRef]= c.mapIt(it.ParentRef)

or generically for any subtype:

converter toSeqParent[T:ParentRef](x:seq[T]):seq[ParentRef]= x.mapIt(it.ParentRef)

With one of those converters defined, the compiler will convert for you automatically, and call to process(children) will compile and run.

shirleyquirk
  • 1,527
  • 5
  • 21