Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generics + inheritable non-ref objects: type issue #7713

Closed
mratsim opened this issue Apr 28, 2018 · 4 comments
Closed

Generics + inheritable non-ref objects: type issue #7713

mratsim opened this issue Apr 28, 2018 · 4 comments

Comments

@mratsim
Copy link
Collaborator

mratsim commented Apr 28, 2018

Doing InheritedType is RootType returns true when no generics are involved but false when they are.
Concepts don't have such issue.

Test case:

import macros

## 1. Sanity check

echo "\n 1. Sanity check"

type Foo = object {.inheritable.}
type Bar = object of Foo
type Baz = object of Foo

proc do_smth(x: Foo) =
  echo "got Foo"

proc do_smth(x: Bar) =
  echo "got Bar"

proc do_smth(x: Baz) =
  echo "got Baz"

block:
  var a: Baz
  do_smth(a)    # Got Baz
  echo a is Foo # True

##########################################
echo "\n 2. Generics stack inheritable vs concepts"

type
  TrainableLayer[T] = object {.inheritable.}
    weight: seq[T]
    bias: seq[T]

  Conv2DLayer[T] = object of TrainableLayer[T]
  LinearLayer[T] = object of TrainableLayer[T]

  Trainable[T] = concept layer
    layer.weight is seq[T]
    layer.bias   is seq[T]

type ExampleModel[TT] = object
  cv1: Conv2DLayer[TT]
  classifier: LinearLayer[TT]

block:
  var a: ExampleModel[int]

  for field, val in a.fieldPairs:
    echo "\n   Field: " & field.astToStr
    echo "Trainable: " & $(val is Trainable)
    echo "TrainableLayer: " & $(val is TrainableLayer) # <---- Doesn't work
    echo "Conv2D: " & $(val is Conv2DLayer)
    echo "Linear: " & $(val is LinearLayer)

  #    Field: "cv1"
  # Trainable: true        <---- Concepts works
  # TrainableLayer: false  <---- Doesn't work
  # Conv2D: true
  # Linear: false

  #    Field: "classifier"
  # Trainable: true
  # TrainableLayer: false <---- Doesn't work
  # Conv2D: false
  # Linear: true


##########################################
echo "\n 3. Concrete stack inheritable vs concepts"

type
  TrainableLayer2 = object {.inheritable.}
    weight: int
    bias: int

  Conv2DLayer2 = object of TrainableLayer2
  LinearLayer2 = object of TrainableLayer2

  Trainable2 = concept layer
    layer.weight is int
    layer.bias   is int

type ExampleModel2 = object
  ## Holds the Trainable layers
  cv1: Conv2DLayer2
  classifier: LinearLayer2

block:
  var a: ExampleModel2

  for field, val in a.fieldPairs:
    echo "\n   Field: " & field.astToStr
    echo "Trainable: " & $(val is Trainable2)
    echo "TrainableLayer: " & $(val is TrainableLayer2) # <------ Works
    echo "Conv2D: " & $(val is Conv2DLayer2)
    echo "Linear: " & $(val is LinearLayer2)

  #    Field: "classifier"
  # Trainable: true
  # TrainableLayer: true  <------ Works
  # Conv2D: false
  # Linear: true
@Araq
Copy link
Member

Araq commented Apr 28, 2018

Compiler bugs aside, you really should not use .inheritable here but inherit from RootObj. .inheritable is only for C++ interop, losing the runtime type information makes every runtime type conversation unchecked.

mratsim added a commit to mratsim/Arraymancer that referenced this issue Apr 29, 2018
* Batch_size is first param for linear layer bias

* Bias actually need a signleton first dimension

* Add a neural network domain specific language

* Update flatten description. TODO make that private

* We don't need methods for the optimizer

* nor ref objects

* TrainableLayer is now a concept instead of inehritable object: nim-lang/Nim#7713

* BREAKING: Rework the optimizer syntax, start rewriting example 1

* Fix linear layer bias backprop

* Workaround typedesc typechecker

* Workaround - nim-lang/Nim#7734 and nim-lang/Nim#7733

* Deprecate newSGD properly

* Update all the example with the new DSL
@shirleyquirk
Copy link
Contributor

maybe related: https://forum.nim-lang.org/t/7798#49595 https://stackoverflow.com/questions/67118749/nim-invalid-type-in-this-context-error

type
  Foo = object of RootObj
  Bar = object of Foo
  Baz[T:Foo] = object of RootObj
    val:T
let x = Baz[Bar]()#Error object constructor needs an object type
type
  Qux = object
    val:Baz[Bar]
let y = Qux() #Error: invalid type: 'Baz[Baz.T]' in this context: 'Qux' for let

concepts work:

type
  Foo = object of RootObj
  IsFoo = concept type t
    t is Foo
  Bar = object of Foo
  Baz[T:IsFoo] = object of RootObj
    val:T
  Qux = object
    val:Baz[Bar]
let x = Baz[Bar]()#fine
let y = Qux() #fine

@shirleyquirk
Copy link
Contributor

another workaround:

func init[T:Foo](b: type Baz; c: typedesc[T]):Baz[T] = discard
let x = Baz.init(Bar)

@metagn
Copy link
Collaborator

metagn commented Nov 17, 2024

Original issue is fixed either by #24430 or #24425, the comment above is still not fixed, moved into #24447

Full test file for original case
discard """
  output: '''

 1. Sanity check
got Baz
true

 2. Generics stack inheritable vs concepts

Field: "cv1"
Trainable: true
TrainableLayer: true
Conv2D: true
Linear: false

Field: "classifier"
Trainable: true
TrainableLayer: true
Conv2D: false
Linear: true

 3. Concrete stack inheritable vs concepts

Field: "cv1"
Trainable: true
TrainableLayer: true
Conv2D: true
Linear: false

Field: "classifier"
Trainable: true
TrainableLayer: true
Conv2D: false
Linear: true
'''
"""

# issue #7713

import std/macros

## 1. Sanity check

echo "\n 1. Sanity check"

type Foo {.inheritable.} = object
type Bar = object of Foo
type Baz = object of Foo

proc do_smth(x: Foo) =
  echo "got Foo"

proc do_smth(x: Bar) =
  echo "got Bar"

proc do_smth(x: Baz) =
  echo "got Baz"

block:
  var a: Baz
  do_smth(a)    # Got Baz
  echo a is Foo # True

##########################################
echo "\n 2. Generics stack inheritable vs concepts"

type
  TrainableLayer[T] {.inheritable.} = object
    weight: seq[T]
    bias: seq[T]

  Conv2DLayer[T] = object of TrainableLayer[T]
  LinearLayer[T] = object of TrainableLayer[T]

  Trainable[T] = concept layer
    layer.weight is seq[T]
    layer.bias   is seq[T]

type ExampleModel[TT] = object
  cv1: Conv2DLayer[TT]
  classifier: LinearLayer[TT]

block:
  var a: ExampleModel[int]

  for field, val in a.fieldPairs:
    echo "\nField: " & field.astToStr
    echo "Trainable: " & $(val is Trainable)
    echo "TrainableLayer: " & $(val is TrainableLayer) # <---- Doesn't work
    echo "Conv2D: " & $(val is Conv2DLayer)
    echo "Linear: " & $(val is LinearLayer)

  #    Field: "cv1"
  # Trainable: true        <---- Concepts works
  # TrainableLayer: false  <---- Doesn't work
  # Conv2D: true
  # Linear: false

  #    Field: "classifier"
  # Trainable: true
  # TrainableLayer: false <---- Doesn't work
  # Conv2D: false
  # Linear: true


##########################################
echo "\n 3. Concrete stack inheritable vs concepts"

type
  TrainableLayer2 {.inheritable.} = object
    weight: int
    bias: int

  Conv2DLayer2 = object of TrainableLayer2
  LinearLayer2 = object of TrainableLayer2

  Trainable2 = concept layer
    layer.weight is int
    layer.bias   is int

type ExampleModel2 = object
  ## Holds the Trainable layers
  cv1: Conv2DLayer2
  classifier: LinearLayer2

block:
  var a: ExampleModel2

  for field, val in a.fieldPairs:
    echo "\nField: " & field.astToStr
    echo "Trainable: " & $(val is Trainable2)
    echo "TrainableLayer: " & $(val is TrainableLayer2) # <------ Works
    echo "Conv2D: " & $(val is Conv2DLayer2)
    echo "Linear: " & $(val is LinearLayer2)

  #    Field: "classifier"
  # Trainable: true
  # TrainableLayer: true  <------ Works
  # Conv2D: false
  # Linear: true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants