是否可以在不使用`eval()`的情况下在Julia中实现类型工厂?

时间:2018-02-07 23:32:10

标签: class julia factory

例如,我有一个基类型的抽象类型,我想实现一个类型工厂,它在这个抽象类型下动态创建具体类型,用户输入参数给出名称(和其他类型特征)。

abstract type AbstractFoo end

function TypeFactory(typename::String, supertype::DataType)
    ...
    return ConcreteSubtype
end

函数TypeFactory接受typenamesupertype个参数,并创建一个属于supertype且与typename同名的具体类型。< / p>

我知道这种类工厂在Python中实现起来并不太困难(例如,How can I dynamically create derived classes from a base class)。在Julia中,可以使用eval(parse())来评估字符串表达式(Is it possible to create types in Julia at runtime?)来模仿它。但在我看来,这种解决方案并不是面向对象意义上的真正的类型工厂。是否有可能在Julia中拥有一个类似于OOP语言(Python,C#等)的类型工厂?

编辑[2018年2月8日]:

我不清楚表达的事情。我是朱莉娅的新手,刚刚开始编写我的项目。我知道继承不受支持,并不意味着在Julia中解决这个问题。

来自Python背景,我有一种感觉eval()通常用于原型设计,但不适用于生产代码。当然Julia是不同的,并且比纯Python更有效率,但是eval()的代码仍然必须在运行时编译(如果我错了,请纠正我)。从性能(Julia, speeding up eval)的角度来看,它的使用也是不鼓励的。

通过'用户输入',我并不仅仅意味着命令行输入。它们可以由用户的配置文件提供。 (话虽如此,@ SalchiPapa的宏观解决方案既优雅又优雅!)

1 个答案:

答案 0 :(得分:3)

  

是否可以在不使用eval()的情况下在Julia中实施类型工厂?

您可以使用宏:

  

宏提供了一种方法,可以将生成的代码包含在程序的最后一个主体中。宏将一个参数元组映射到返回的表达式,而生成的表达式直接编译而不需要运行时eval()调用

julia> VERSION
v"0.7.0-DEV.2098"

julia> module Factory
       export @factory
       macro factory(type_name::Symbol, super_type::Symbol)
           # ...
           return quote
               struct $type_name <: $(esc(super_type))
                   # ...
                   bar
               end
               return $(esc(type_name))
           end
       end
       end
Main.Factory

julia> using Main.Factory: @factory

julia> abstract type AbstractFoo end

julia> @factory ConcreteFoo AbstractFoo
ConcreteFoo

julia> foo = ConcreteFoo(42)
ConcreteFoo(42)

julia> foo.bar
42

julia> ConcreteFoo <: AbstractFoo
true

julia> supertype(ConcreteFoo)
AbstractFoo

根据@Gnimuc在评论中的理解,使用input

进行编辑
julia> module Factory
       export @factory

       function input(prompt::String="")::String
           print(prompt)
           return chomp(readline())
       end

       macro factory(type_name = input("Type name: "))
           AbstractT = Symbol(:Abstract, type_name)
           ConcreteT = Symbol(:Concrete, type_name)
           return quote
               abstract type $(esc(AbstractT)) end
               struct $ConcreteT <: $(esc(AbstractT))
                   bar
               end
               return $(esc(AbstractT)), $(esc(ConcreteT))
           end
       end
       end
Main.Factory

julia> using Main.Factory: @factory

julia> @factory
Type name: Foo
(AbstractFoo, ConcreteFoo)

julia> @factory
Type name: Bar
(AbstractBar, ConcreteBar)

julia> @factory Baz
(AbstractBaz, ConcreteBaz)

julia> foo = ConcreteFoo(42)
ConcreteFoo(42)

julia> foo.bar
42

julia> ConcreteFoo <: AbstractFoo
true

julia> supertype(ConcreteFoo)
AbstractFoo

julia> @macroexpand @factory
Type name: Qux
quote
    #= REPL[1]:13 =#
    abstract type AbstractQux end
    #= REPL[1]:14 =#
    struct ConcreteQux <: AbstractQux
        #= REPL[1]:15 =#
        bar
    end
    #= REPL[1]:17 =#
    return (AbstractQux, ConcreteQux)
end

julia> eval(ans)
(AbstractQux, ConcreteQux)