【问题标题】:Is it possible to implement a type factory in Julia without using `eval()`?是否可以在不使用 `eval()` 的情况下在 Julia 中实现类型工厂?
【发布时间】:2018-07-18 09:48:41
【问题描述】:

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

abstract type AbstractFoo end

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

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

我知道这种类工厂在 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 的新手,最近才开始在其中编写我的项目。我知道不支持继承,也不打算在 Julia 中解决这个问题。

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

我所说的“用户输入”不仅仅指命令行输入。它们可以由用户的配置文件提供。 (话虽这么说,@SalchiPapa 的宏解决方案既恰当又优雅!)

【问题讨论】:

  • “不是面向对象意义上的真正类型工厂”是什么意思? IIUC,您在 Python 中寻找类似 @​​987654333@ 的东西,对我来说,这正是链接答案中方法 2 中的 createTypeAST+eval。这是一个帮助函数,允许用户在运行时动态创建一个新类型,我想知道它是如何连接到 OOP 的。
  • @Gnimuc 我认为他指的是继承。
  • @Gnimuc 感谢您的提问。我不是很清楚。我想我希望它是函数式的,它返回具体类型,而不是用eval() 评估的表达式。一个原因是预编译代码比运行时eval-ed 的代码更快。另外,我认为在生产代码中编写函数(和宏)更优雅,尽管这可能只是个人喜好。

标签: class julia factory


【解决方案1】:

是否可以在不使用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

根据cmets中@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)

【讨论】:

  • 正如OP所说的“使用用户输入参数给出的名称”,也许我们需要在宏中添加type_name = readline(STDIN) |&gt; Symbol。但我认为这是一种运行时信息,将 readline 函数插入宏实现是否是最佳实践?
  • 我很好奇Python是如何实现它的type()函数的。
  • @Gnimuc 考虑到您的 cmets,我已经更新了我的答案。
猜你喜欢
  • 2012-04-21
  • 2010-11-05
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
相关资源
最近更新 更多