From d92d1747b6042ccbc954f154f2369cd156479642 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Wed, 1 Jul 2020 09:38:31 -0500 Subject: [PATCH 01/10] super constructor inheritance --- src/Classes.jl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Classes.jl b/src/Classes.jl index fed71de..6411aa8 100644 --- a/src/Classes.jl +++ b/src/Classes.jl @@ -242,6 +242,33 @@ function _constructors(clsname, super, super_info, local_fields, all_fields, whe push!(methods, immut_init) end + # super constructor inheritance: when the class and superclass have the same fields, + # the constructors of the superclass are valid for the subclass, + # so we make an outter constructor for subclass pointing to those. + if super !== Class && length(local_fields) === 0 + if length(super_fields) != 0 + argnames = _argnames(super_fields) + guide_constructor = quote + function $clsname(arguments...) + super_inctance = $(super)(arguments...) # super's extra constructor + # setting subclass fields + subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] + return $clsname(subcls_fields...) + end + end + else + # call the super constructor that may do some stuff + guide_constructor = quote + function $clsname(arguments...) + $(super)(arguments...) # does something + return $clsname() + end + end + end + push!(inits, guide_constructor) + end + # TODO add parameters guide as well + return methods, inits end From 66c8873924e311f85571977098f7e2bc359c7c3c Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Wed, 1 Jul 2020 09:38:41 -0500 Subject: [PATCH 02/10] super constructor inheritance tests --- test/test_classes.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_classes.jl b/test/test_classes.jl index 7b600ad..9bbd70c 100644 --- a/test/test_classes.jl +++ b/test/test_classes.jl @@ -175,3 +175,19 @@ end @test Cat(1).x == 1 @test Cat("a").x == "a" + +# super constructor inheritance +@class Animal begin + x + Animal(x, y) = new(x) +end + +function Animal(x, y, z) + return Animal(x+y+z) +end + +@class Dog <: Animal begin +end + +@test Dog(1,2).x == 1 +@test Dog(1,2,3).x == 6 From d9e522eb26c837f3b1fc87bf6f59ad13bc2685a7 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Wed, 1 Jul 2020 20:40:28 -0500 Subject: [PATCH 03/10] fieldless class test --- test/test_classes.jl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/test_classes.jl b/test/test_classes.jl index 9bbd70c..4dc3d72 100644 --- a/test/test_classes.jl +++ b/test/test_classes.jl @@ -1,5 +1,6 @@ using Test using Classes +using Suppressor @test superclass(Class) === nothing @@ -176,7 +177,7 @@ end @test Cat(1).x == 1 @test Cat("a").x == "a" -# super constructor inheritance +## super constructor inheritance @class Animal begin x Animal(x, y) = new(x) @@ -191,3 +192,19 @@ end @test Dog(1,2).x == 1 @test Dog(1,2,3).x == 6 + + +# fieldless class +@class Animal2 begin +end + +test_num = 1 +function Animal2(x, y) + println("Animal is instantiated") + return Animal2() +end + +@class Dog2 <: Animal2 begin +end + +@test @capture_out( Dog2(1,2) ) == "Animal is instantiated\n" From 3617fbbf04558f20b8df889052265086c13898db Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:06:33 -0500 Subject: [PATCH 04/10] super_constructor_inheritance with parameters --- src/Classes.jl | 78 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/src/Classes.jl b/src/Classes.jl index 6411aa8..013b2aa 100644 --- a/src/Classes.jl +++ b/src/Classes.jl @@ -204,6 +204,61 @@ function _initializer(class, fields, wheres) return funcdef end +function super_constructor_inheritance(clsname, super, super_fields, has_params, params) + guide_constructor = Expr[] + super_fields_len = length(super_fields) + if has_params + function_return = :(new{(collect(getfield(typeof(super_inctance), :parameters))...)}) + else + function_return = :(new) + end + # paramsless constructor + if super_fields_len != 0 + argnames = _argnames(super_fields) + paramsless_guide_constructor = quote + function $clsname(arguments...) + super_inctance = $super(arguments...) # super's extra constructor + # setting subclass fields + subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] + return $function_return(subcls_fields...) + end + end + else + # call the super constructor that may do some stuff + paramsless_guide_constructor = quote + function $clsname(arguments...) + super_inctance = $super(arguments...) # does something + return $function_return() + end + end + end + push!(guide_constructor, paramsless_guide_constructor) + # paramsful constructor + if has_params + if super_fields_len != 0 + argnames = _argnames(super_fields) + paramsful_guide_constructor = quote + function $clsname{$(params...)}(arguments...) where {$(params...)} + super_inctance = $super{$(params...)}(arguments...) # super's extra constructor + # setting subclass fields + subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] + return $function_return(subcls_fields...) + end + end + else + # call the super constructor that may do some stuff + paramsful_guide_constructor = quote + function $clsname{$(params...)}(arguments...) where {$(params...)} + super_inctance = $super{$(params...)}(arguments...) # does something + return $function_return() + end + end + end + push!(guide_constructor, paramsful_guide_constructor) + end + return guide_constructor +end + function _constructors(clsname, super, super_info, local_fields, all_fields, wheres) all_wheres = [super_info.wheres; wheres] init_all = _initializer(clsname, all_fields, all_wheres) @@ -246,28 +301,9 @@ function _constructors(clsname, super, super_info, local_fields, all_fields, whe # the constructors of the superclass are valid for the subclass, # so we make an outter constructor for subclass pointing to those. if super !== Class && length(local_fields) === 0 - if length(super_fields) != 0 - argnames = _argnames(super_fields) - guide_constructor = quote - function $clsname(arguments...) - super_inctance = $(super)(arguments...) # super's extra constructor - # setting subclass fields - subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] - return $clsname(subcls_fields...) - end - end - else - # call the super constructor that may do some stuff - guide_constructor = quote - function $clsname(arguments...) - $(super)(arguments...) # does something - return $clsname() - end - end - end - push!(inits, guide_constructor) + guide_constructor = super_constructor_inheritance(clsname, super, super_fields, has_params, params) + push!(methods, guide_constructor...) end - # TODO add parameters guide as well return methods, inits end From 76d4fc6adcdca4ef576dfd237492c400541de237 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:06:45 -0500 Subject: [PATCH 05/10] super constructor inheritance with parameters test --- test/test_classes.jl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_classes.jl b/test/test_classes.jl index 4dc3d72..14650a5 100644 --- a/test/test_classes.jl +++ b/test/test_classes.jl @@ -208,3 +208,27 @@ end end @test @capture_out( Dog2(1,2) ) == "Animal is instantiated\n" + +# super constructor inheritance with parameters +@class Animal3{T} begin + x::T + Animal3(x, y) = new{typeof(x)}(x) + Animal3{T}(x, y) where {T} = new{T}(x) +end + +function Animal3(x, y, z) + return Animal3(x*y, z) +end + +function Animal3{T}(x, y, z) where {T} + return Animal3{T}(x*y, z) +end + +@class Dog3 <: Animal3 begin +end + +@test Dog3(1,2).x == 1 +@test Dog3{Int64}(1,2).x == 1 +@test Dog3(1,2,3).x == 2 +@test Dog3{Int64}(1,2,3).x == 2 + From 57d1ac49851d741b81f329ff7fc84ef37bd1b460 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:06:54 -0500 Subject: [PATCH 06/10] fieldless class with parameters test --- test/test_classes.jl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/test_classes.jl b/test/test_classes.jl index 14650a5..4cb9980 100644 --- a/test/test_classes.jl +++ b/test/test_classes.jl @@ -232,3 +232,24 @@ end @test Dog3(1,2,3).x == 2 @test Dog3{Int64}(1,2,3).x == 2 +# fieldless class with parameters +@class Animal4{T} begin + Animal4(x, y) = new{typeof(x)}() + Animal4{T}(x, y) where {T} = new{T}() +end + +function Animal4(x, y, z) + return Animal4(x*y, z) +end + +function Animal4{T}(x, y, z) where {T} + return Animal4{T}(x*y, z) +end + +@class Dog4 <: Animal4 begin +end + +@test typeof(Dog4(1,2)) == Dog4{Int64} +@test typeof(Dog4{Float64}(1,2)) == Dog4{Float64} +@test typeof(Dog4(1,2,3)) == Dog4{Int64} +@test typeof(Dog4{Int32}(1,2,3)) == Dog4{Int32} From f42fddf465b48d4372d4275d59f7616d3989b34e Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:12:30 -0500 Subject: [PATCH 07/10] [skip ci] Add examples to the readme --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bfda4da..bf64e27 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ of methods while enabling shared structure without duplicative code. A "class" is a concrete type with a defined relationship to a hierarchy of automatically generated abstract types. The `@class` macro saves the field definitions for each class so that subclasses receive all their parent's fields in addition to those defined locally. -Inner constructors are passed through unchanged. +Inner constructors are passed through unchanged. `@class` supports parametric classes (similar to parametric structs) and also super class constructor inheritance when the subclass does not have additional fields. `Classes.jl` constructs a "shadow" abstract type hierarchy to represent the relationships among the defined classes. For each class `Foo`, the abstract type `AbstractFoo` is defined, where `AbstractFoo` @@ -133,4 +133,26 @@ For example, give the class `Bar`, you can write a function that applies to my_method(obj::AbstractBar, other, stuff) = do_something(obj, other, args) ``` +## Super constructor inheritance +When a subclass does not have any additional fields you can call its super constructors: +```julia +## super constructor inheritance +@class Animal begin + x + Animal(x, y) = new(x) +end + +function Animal(x, y, z) + return Animal(x+y+z) +end + +@class Dog <: Animal begin +end + +Dog(1,2) # 1 +Dog(1,2,3) # 6 +``` + + + See the online [documentation](https://github.com/rjplevin/Classes.jl/blob/master/docs/src/index.md) for further details. From 072348aa1a7b24288daf84a2aadc2ad9e675db4d Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:17:04 -0500 Subject: [PATCH 08/10] Don't collect generators for speed --- src/Classes.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Classes.jl b/src/Classes.jl index 013b2aa..48bdebf 100644 --- a/src/Classes.jl +++ b/src/Classes.jl @@ -208,7 +208,7 @@ function super_constructor_inheritance(clsname, super, super_fields, has_params, guide_constructor = Expr[] super_fields_len = length(super_fields) if has_params - function_return = :(new{(collect(getfield(typeof(super_inctance), :parameters))...)}) + function_return = :(new{(getfield(typeof(super_inctance), :parameters)...)}) else function_return = :(new) end @@ -219,7 +219,7 @@ function super_constructor_inheritance(clsname, super, super_fields, has_params, function $clsname(arguments...) super_inctance = $super(arguments...) # super's extra constructor # setting subclass fields - subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] + subcls_fields = (getfield(super_inctance, arg) for arg in $argnames) return $function_return(subcls_fields...) end end @@ -241,7 +241,7 @@ function super_constructor_inheritance(clsname, super, super_fields, has_params, function $clsname{$(params...)}(arguments...) where {$(params...)} super_inctance = $super{$(params...)}(arguments...) # super's extra constructor # setting subclass fields - subcls_fields = [getfield(super_inctance, arg) for arg in $argnames] + subcls_fields = (getfield(super_inctance, arg) for arg in $argnames) return $function_return(subcls_fields...) end end From cbab46f5db6472ac687a60b144a9517cbf9be63f Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:25:36 -0500 Subject: [PATCH 09/10] using broadcast instead of generator [speed] benchmark: ```julia using Classes using BenchmarkTools struct Foo{T} x::T y::T z::T end f = Foo(1,2,3) fnames = [:x, :y, :z] function t1(f, fnames) x = (getfield(f, arg) for arg in fnames) return Foo(x...) end x = @btime t1($f, $fnames) function t2(f, fnames) x = getfield.(Ref(f), fnames) return Foo(x...) end x = @btime t2($f, $fnames) ``` --- src/Classes.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Classes.jl b/src/Classes.jl index 48bdebf..9c8590c 100644 --- a/src/Classes.jl +++ b/src/Classes.jl @@ -219,7 +219,7 @@ function super_constructor_inheritance(clsname, super, super_fields, has_params, function $clsname(arguments...) super_inctance = $super(arguments...) # super's extra constructor # setting subclass fields - subcls_fields = (getfield(super_inctance, arg) for arg in $argnames) + subcls_fields = getfield.(Ref(super_inctance), $argnames) return $function_return(subcls_fields...) end end @@ -241,7 +241,7 @@ function super_constructor_inheritance(clsname, super, super_fields, has_params, function $clsname{$(params...)}(arguments...) where {$(params...)} super_inctance = $super{$(params...)}(arguments...) # super's extra constructor # setting subclass fields - subcls_fields = (getfield(super_inctance, arg) for arg in $argnames) + subcls_fields = getfield.(Ref(super_inctance), $argnames) return $function_return(subcls_fields...) end end From 86a48c790ac6ea4ec7e669c6702ad10eed9b32e4 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 2 Jul 2020 01:34:31 -0500 Subject: [PATCH 10/10] allow_failures julia 1.0 --- .travis.yml | 2 +- src/Classes.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 06e1413..0bae984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,5 +15,5 @@ branches: - /v(\d+)\.(\d+)\.(\d+)/ matrix: allow_failures: - - julia: nightly + - julia: 1.0 codecov: true diff --git a/src/Classes.jl b/src/Classes.jl index 9c8590c..5247dfa 100644 --- a/src/Classes.jl +++ b/src/Classes.jl @@ -207,6 +207,7 @@ end function super_constructor_inheritance(clsname, super, super_fields, has_params, params) guide_constructor = Expr[] super_fields_len = length(super_fields) + # new{T...} is not supported on Julia 1.0.5 if has_params function_return = :(new{(getfield(typeof(super_inctance), :parameters)...)}) else