关于在julia中使用传统class的问题

有人在问这个问题。我们一步步来解决。
下面先给出一个不能继承的class。
其用法如下:

@class S begin
    a :: Int
    f(self, arg::Int) = self.a + arg 
end

s = S(1)
s.f(2) 
# => 3

实现如下:
class.jl


function div_set(method ::Expr)
    if method.head == :(::)
        :field
    else
        :method
    end
end

function div_set(method::Symbol)
    :field
end

function div_set(method::LineNumberNode)
    nothing
end
function group_by(f, seq)
    groups = Dict{Any, Any}()
    foreach(seq) do elem
        mapped = f(elem)
        get(groups, mapped) do
            groups[mapped] = []
        end |>
        function (group)
            push!(group, elem)
        end
    end
    groups
end
macro class(class_name, methods)
    groups = group_by(div_set, methods.args)
    get(groups, :field) do
        []
    end |>
    function(group)
        expr =
            quote
                struct $class_name
                $(group...)
                end
            end
        @eval __module__ $expr
    end
    cls = @eval __module__ $class_name
    get(groups, :method) do
        []
    end |>
    function(group)
        map(group) do method
            method_name = method.args[1].args[1]
            this        = method.args[1].args[2]
            deleteat!(method.args[1].args, [1, 2])
            method.args[1].head = :tuple
            if method.head === :(=)
                method.head = :(->)
            end
            bound_fn =
                 quote
                    ($this :: $cls) -> begin
                        $method
                    end
                 end
            method_name => @eval __module__ $bound_fn
        end |>
        function (seq)
            dispatcher = Dict(seq)
            m_this = Symbol(".", "this")
            m_attr = Symbol(".", "attr")
            fields = fieldnames(cls)
            property = quote
                function Base.getproperty($m_this :: $cls, $m_attr :: $Symbol)
                    if $m_attr in $fields
                        getfield($m_this, $m_attr)
                    else
                        $dispatcher[$m_attr]($m_this)
                    end
                end
            end
            property |> esc
        end
    end
end

以上实现有一些不足,例如method和field的覆盖问题(以上实现默认了field优先),例如self不能标注类型,没有实现静态方法等。当然要处理它们也是简单的, 如果你需要频繁使用class,那就解决这些问题即可。

关于继承的实现有多种方法,例如python的mro。
在车上写码打字都很痛苦,后面再更。

2 个赞

X-ref: 用闭包模拟

In any language with closures you can define your own “object systems” like this, for example see the many object systems that have been developed within Scheme. --Jeff

这个就是用闭包模拟。。

2 个赞