Dear all,
I am well aware of concepts being experimental. Nevertheless, they have proven to be quite useful. So, I apply them quite regularly ...
The problem I am currently trying to solve:
I have a 3-layer data structure: (a) a catalogue of matrices, (b) a matrix and (c) the elements of a matrix. For practical reasons and to avoid substantial redundancies, there will be some data to be maintained on each level and made available to deeper level concepts.
There will be a certain variation in the specification of level data and element types. Thus, the aim is to get some generic maintenance and structural modification together. This involves making the upper-level information available to lower levels whenever something is added to the model (... allow for copies of elements on each level and less than a handful of additional tasks - omitted here).
Actually concepts should to do the trick here (... I thought - alternatives welcome). Thus, in simplified terms, I did the following:
# concept.nim
type
Concept0* = concept C0, var varC0, type TC0
varC0.copy_C0() is TC0
varC0.init_C0()
proc c0Init*[T: Concept0](x: var T) =
x.init_C0()
proc c0Copy*[T: Concept0](x: var T): T =
result = x.copy_C0()
type
Concept1* = concept C1, var varC1, type TC1
type C0 = Concept0
varC1.copy_C1(C0) is TC1
varC1.init_C1(C0)
proc c1Init*[S: Concept0, T: Concept1](x: var T, y: S) =
x.init_C1(y)
proc c1Copy*[S: Concept0, T: Concept1](x: var T, y: S): T =
result = x.copy_C1(y)
type
Concept2* = concept C2, var varC2, type TC2
type C1 = Concept1
varC2.copy_C2(C1) is TC2
varC2.init_C2(C1)
proc c2Init*[S: Concept1, T: Concept2](x: var T, y: Concept1) =
x.init_C2(y)
proc c2Copy*[S: Concept1, T: Concept2](x: var T, y: Concept1): T =
result = x.copy_C2(y)
# objects.nim
type
CatalogueProperties* = ref object
name*: string
proc init_C0*(
x: var CatalogueProperties) =
new(x)
proc copy_C0*(
x: var CatalogueProperties
): CatalogueProperties {.noSideEffect.} =
result = new CatalogueProperties
type
MatrixProperties* = ref object
name*: string
core_properties*: CatalogueProperties
proc init_C1*(
x: var MatrixProperties,
y: CatalogueProperties) =
new(x)
x.core_properties = y
proc copy_C1*(
x: var MatrixProperties,
y: CatalogueProperties
): MatrixProperties =
result = new MatrixProperties
result.core_properties = y
type
Element* = ref object
name*: string
group_properties*: MatrixProperties
proc init_C2*(
x: var Element,
y: MatrixProperties) =
new(x)
x.group_properties = y
proc copy_C2*(
x: var Element,
y: MatrixProperties
): Element =
result = new Element
result.name = x.name
result.group_properties = y
# main.nim
import
./concepts as m_concepts,
./objects as m_objects
proc isCncpt0(c0: m_concepts.Concept0) = discard
proc isCncpt1(c1: m_concepts.Concept1) = discard
proc isCncpt2(c2: m_concepts.Concept2) = discard
var object_0: m_objects.CatalogueProperties
object_0.isCncpt0()
m_concepts.c0Init(object_0)
discard m_concepts.c0Copy(x = object_0)
object_0.name = "hello"
var object_1: m_objects.MatrixProperties
object_1.isCncpt1()
m_concepts.c1Init(object_1, object_0)
discard m_concepts.c1Copy(x = object_1, y = object_0)
object_1.name = "world"
echo object_1.core_properties.name
echo object_1.name
var object_2: m_objects.Element
This provides hello and world, as expected.
Adding
object_2.isCncpt2()
Leads to the following error:
.\main.nim(25, 9) Error: type mismatch: got <Element> but expected one of: proc isCncpt2(c2: m_concepts.Concept2) first type mismatch at position: 1 required type for c2: Concept2 but expression 'object_2' is of type: Element .\concepts.nim(3, 5) C1: varC0.copy_C0() is TC0 varC0.init_C0() too nested for type matching .\concepts.nim(16, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc init_C1(x: var MatrixProperties; y: CatalogueProperties) first type mismatch at position: 2 required type for y: CatalogueProperties but expression 'C0' is of type: typedesc[C0] expression: init_C1(varC1, C0) .\concepts.nim(15, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc copy_C1(x: var MatrixProperties; y: CatalogueProperties): MatrixProperties first type mismatch at position: 1 required type for x: var MatrixProperties but expression 'varC1' is of type: Alias expression: copy_C1(varC1, C0) .\concepts.nim(15, 18) C1: expression '' has no type (or is ambiguous) .\concepts.nim(14, 5) C1: concept predicate failed .\concepts.nim(16, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc init_C1(x: var MatrixProperties; y: CatalogueProperties) first type mismatch at position: 1 required type for x: var MatrixProperties but expression 'varC1' is of type: Alias expression: init_C1(varC1, C0) .\concepts.nim(28, 10) Concept2: type mismatch: got <Alias, typedesc[C1]> but expected one of: proc init_C2(x: var Element; y: MatrixProperties) first type mismatch at position: 2 required type for y: MatrixProperties but expression 'C1' is of type: typedesc[C1] .\concepts.nim(3, 5) C1: varC0.copy_C0() is TC0 varC0.init_C0() too nested for type matching .\concepts.nim(16, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc init_C1(x: var MatrixProperties; y: CatalogueProperties) first type mismatch at position: 2 required type for y: CatalogueProperties but expression 'C0' is of type: typedesc[C0] expression: init_C1(varC1, C0) .\concepts.nim(15, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc copy_C1(x: var MatrixProperties; y: CatalogueProperties): MatrixProperties first type mismatch at position: 1 required type for x: var MatrixProperties but expression 'varC1' is of type: Alias expression: copy_C1(varC1, C0) .\concepts.nim(15, 18) C1: expression '' has no type (or is ambiguous) .\concepts.nim(14, 5) C1: concept predicate failed .\concepts.nim(16, 10) C1: type mismatch: got <Alias, typedesc[C0]> but expected one of: proc init_C1(x: var MatrixProperties; y: CatalogueProperties) first type mismatch at position: 1 required type for x: var MatrixProperties but expression 'varC1' is of type: Alias expression: init_C1(varC1, C0) expression: init_C2(varC2, C1) expression: isCncpt2(object_2)
Obviously there is something too nested for type matching - but, well, I thought that the maximum level for nesting was 7.
What am I doing wrong? - Fixes as well as workarounds welcome (generic solutions would be nice ...).
leaving aside the question of whether its a good idea, here's how to get it to compile:
with devel and new-style concepts:
type
Concept0* = concept
proc copy_C0(x:Self):Self
proc init_C0(x:var Self)
Concept1* = concept
proc copy_C1(s:Self,x:Concept0):Self
proc init_C1(s:var Self,x:Concept0)
Concept2* = concept
proc copy_C2(s:Self,x:Concept1):Self
proc init_C2(s:var Self,x:Concept1)
with old-style concepts:
type
Concept0* = concept c,var v,type t
copy_C0(c) is t
init_C0(v)
Concept1*[T:Concept0] = concept c, var v, type t
copy_C1(c,T) is t
init_C1(v,T)
Concept2*[T:Concept1] = concept c,var v, type t
copy_C2(c,T) is t
init_C2(v,T)
Thank you, shirleyquirk for your comments.
Took me a while to get trough them.
Ad new-style concepts:
I have not yet looked into this. Is there any documentation (or alike) available yet? (Tried to find some ...) - Would really like to start using these rather sooner than later.
Ad something like:
Seems that my explanations weren't that bad. Just the main focus is on defined 'features' of each level I intended to pass downwards instead of the whole thing - but I changed my mind: such limitation is likely end up in a mess some day. Thanks for the input.
Ad old-style concepts:
Well, that my code did not compile was obvious. Tried to implement it your way but again had some issues getting it compiled.
What finally compiled is the following:
# concepts.nim:
type
Concept0* = concept C0, var varC0, type TC0
varC0.copy_C0() is TC0
varC0.init_C0()
proc c0Init*[T: Concept0](x: var T) =
x.init_C0()
proc c0Copy*[T: Concept0](x: var T): T =
result = x.copy_C0()
type
Concept1*[C0:Concept0] = concept C1, var varC1, type TC1
copy_C1(varC1) is TC1
init_C1(varC1, C0)
proc c1Init*[C0: Concept0, C1: Concept1[Concept0]](x: var C1, y: C0) =
x.init_C1(y)
proc c1Copy*[C1: Concept1[Concept0]](x: var C1): C1 =
result = x.copy_C1()
type
Concept2*[C1:Concept1[Concept0]] = concept C2, var varC2, type TC2
copy_C2(varC2) is TC2
init_C2(varC2, C1)
proc c2Init*[C1: Concept1[Concept0], C2: Concept2[C1]](x: var C2, y: C1) =
x.init_C2(y)
proc c2Copy*[C2: Concept2[Concept1[Concept0]]](x: var C2): C2 =
result = x.copy_C2()
# objects.nim
type
CatalogueProperties* = ref object
name*: string
proc init_C0*(
x: var CatalogueProperties) =
new(x)
proc copy_C0*(
x: var CatalogueProperties
): CatalogueProperties {.noSideEffect.} =
result = new CatalogueProperties
type
MatrixProperties*[CP: ref object] = ref object
name*: string
catalogue_properties*: CP
proc init_C1*[CP: ref object](
x: var MatrixProperties[CP],
y: CP) =
new(x)
x.catalogue_properties = y
proc copy_C1*[CP: ref object](
x: var MatrixProperties[CP]
): MatrixProperties[CP] =
result = new MatrixProperties[CP]
result.name = x.name
result.catalogue_properties = x.catalogue_properties
type
Element*[MP] = ref object
name*: string
matrix_properties*: MP
proc init_C2*[EMP: ref object,
EL: Element[EMP]](
x: var EL,
y: EMP) =
new(x)
x.matrix_properties = y
proc copy_C2*[EMP: ref object,
EL: Element[EMP]](
x: var EL
): EL =
result = new EL
result.name = x.name
result.matrix_properties = x.matrix_properties
# main.nim
import
./concepts as m_concepts,
./objects as m_objects
proc isCncpt0(c0: m_concepts.Concept0) = discard
proc isCncpt1(c1: m_concepts.Concept1[m_concepts.Concept0]) = discard
proc isCncpt2(c2: m_concepts.Concept2[m_concepts.Concept1]) = discard
var object_0: m_objects.CatalogueProperties
object_0.isCncpt0()
m_concepts.c0Init(object_0)
discard m_concepts.c0Copy(x = object_0)
object_0.name = "hello"
var object_1: m_objects.MatrixProperties[m_objects.CatalogueProperties]
object_1.isCncpt1()
m_concepts.c1Init(object_1, object_0)
discard m_concepts.c1Copy(x = object_1)
object_1.name = "world"
var object_2: m_objects.Element[m_objects.MatrixProperties[m_objects.CatalogueProperties]]
object_2.isCncpt2()
m_concepts.c2init(object_2, object_1)
discard m_concepts.c2Copy(object_2)
object_2.name = "!"
echo object_2.matrix_properties.catalogue_properties.name
echo object_2.matrix_properties.name
echo object_2.name
Gives a beautiful:
hello world !
The Concept2*[C1:Concept1[Concept0]] seems to do the trick.
Thank you again. Learned a bit :-)
glad you got it working!
it's possible to do so with less genericity, should you desire:
concepts.nim
type
Concept0* = concept C0, var varC0, type TC0
varC0.copy_C0() is TC0
varC0.init_C0()
Concept1*[C0:Concept0] = concept C1, var varC1, type TC1
copy_C1(varC1) is TC1
init_C1(varC1, C0)
Concept2*[C1:Concept1] = concept C2, var varC2, type TC2
copy_C2(varC2) is TC2
init_C2(varC2, C1)
proc c0Init*(x: var Concept0) = x.init_C0()
proc c0Copy*(x: var Concept0): Concept0 = x.copy_C0()
proc c1Init*(x: var Concept1, y: Concept0) = x.init_C1(y)
proc c1Copy*(x:var Concept1): Concept1 = x.copy_C1()
proc c2Init*(x: var Concept2, y: Concept1) = x.init_C2(y)
proc c2Copy*(x:var Concept2): Concept2 = x.copy_C2()
works with objects.nim/main.nim unchanged
minor point: c2Copy doesn't need to take a var parameter, sorry, i changed that in my answer i hope that didn't mess you up.
I have not yet looked into this. Is there any documentation (or alike) available yet? (Tried to find some ...) - Would really like to start using these rather sooner than later.
For now there's just the RFC on new-style concepts: https://github.com/nim-lang/RFCs/issues/168
And some tests here: https://github.com/nim-lang/Nim/blob/devel/tests/concepts/tspec.nim
OK, will try.
var is superfluous - there to allow the wrapperless deepcopy as a default.
Thank you!
the var parameter in deepcopy is for the destination, not the source, yes?
proc c0copy(x: Concept0):Concept0 = deepcopy(result,x);