在Julia中封装类型别名

huangapple go评论61阅读模式
英文:

Wrapping a type alias in julia

问题

I am trying to see if validate_convert is defined for the type specified. Problem is I defined my type as an alias instead of wrapping it. Now I have a lot of code that uses this type and it seems to me like wrapping it would require a lot of changes to use something like value(U::UserID) = U.value.

Do you see anyway of making this work but not match when type=String only when type=UserID or is there another solution?

UserID = String

function validate_convert(::Type{UserID}, input)::UserID
parse(UserID, input)
end

type = UserID
hasmethod(validate_convert, Tuple{typeof(type),typeof(value)})

英文:

I am trying to see if validate_convert is defined for the type specified. Problem is I defined my type as an alias instead of wrapping it. Now I have a lot of code that uses this type and it seems to me like wrapping it would require a lot of changes to use something like value(U::UserID) = U.value.

Do you see anyway of making this work but not match when type=String only when type=UserID or is there another solution?

UserID = String

function validate_convert(::Type{UserID}, input)::UserID
    parse(UserID, input)
end

type = UserID
hasmethod(validate_convert, Tuple{typeof(type),typeof(value)})

答案1

得分: 1

Sure, here's the translated text:

"似乎对我来说,要包装它需要很多更改,以使用类似 value(U::UserID) = U.value 的东西。

TypedDelegation.jl:

轻松地将函数应用于字段。
将结构体的字段用作该类型的值上的操作的操作数。

您可以将您的类型设置为包装器类型,然后使用 @delegate_onefield@delegate_onefield_astype 来将对您的 UserID 类型的调用转发到包装的 String 上。@delegate_onefield 将返回与 String 上的调用一样的内容,@delegate_onefield_astype 对于返回 String 的方法很有用,但您希望它们返回 UserID。"

英文:

> it seems to me like wrapping it would require a lot of changes to use something like value(U::UserID) = U.value.

TypedDelegation.jl:

> Easily apply functions onto fields.
> Use a struct's fields as operands for operations on values of that type.

You can make your type a wrapper type, and then use @delegate_onefield and @delegate_onefield_astype to forward calls on your UserID type to be calls on the wrapped String. @delegate_onefield will return what the call on the String returns as is, @delegate_onefield_astype is useful for methods that return a String but you want them to return a UserID.

答案2

得分: 0

你的类型别名 UserIDString 完全等效:

julia> UserID = String;

julia> UserID === String
true

所以,不,无法编写一个只匹配 UserID 而不匹配 String 的方法。你的选择是将 UserID 设为包装字符串(或整数或其他所需类型)的结构体,或者只需使用普通的 String 作为用户 ID。如果不需要根据用户 ID 进行分派,那么使用字符串作为 ID 可能是有意义的。

英文:

Your type alias UserID is completely equivalent to String:

julia> UserID = String;

julia> UserID === String
true

So, no, there is no way to write a method that matches UserID but not String. Your options are to make UserID a struct that wraps a string (or an integer or whatever you need), or to just use plain old String for your user IDs. If you don't need to dispatch on the user ID, then it might make sense to just use strings for the IDs.

答案3

得分: 0

Wrapper types are a bit of a problem. If you want Wrapper{T} to behave just like T, then you need to reimplement all the methods defined for T, which is unfortunate.

This may not be applicable for you, but if you're mainly interested in using these wrapper types inside structs you can hide some logic to wrap/unwrap the data transparently when it's accessed. As an example:

struct ValidatedField{Tag, T}
    val::T

    function ValidatedField{Tag, T}(val) where {Tag, T}
        vf = new{Tag, T}(val::T)
        (validate(vf) || throw(ArgumentError("Invalid: $val"))) && vf
    end
end

value(vf::ValidatedField) = vf.val
validate(vf::ValidatedField) = false

abstract type ValidatedRecord end

function Base.getproperty(vr::V, field::Symbol) where V <: ValidatedRecord
    f = getfield(vr, field)
    return f isa ValidatedField ? value(f) : f
end

function Base.setproperty!(vr::V, field::Symbol, val) where V <: ValidatedRecord
    T = fieldtype(V, field) 
    setfield!(vr, field, T <: ValidatedField ? T(val) : val)
end

UserID = ValidatedField{:UserId, String}
validate(u::UserID) = all(islowercase, value(u))

mutable struct User <: ValidatedRecord
    id::UserID # appears as a String but must be a valid UserID
    name::String # just a normal String

    function User(id, name)
        u = new()
        u.id, u.name = id, name
        return u
    end
end

user = User("foo", "Bob")
user.id # => "foo"
user.id = "Bar" # invalid
user.id = "bar" # valid
英文:

Wrapper types are a bit of a problem. If you want Wrapper{T} to behave just like T, then you need to reimplement all the methods defined for T, which is unfortunate.

This may not be applicable for you, but if you're mainly interested in using these wrapper types inside structs you can hide some logic to wrap/unwrap the data transparently when it's accessed. As an example:

struct ValidatedField{Tag, T}
    val::T

    function ValidatedField{Tag, T}(val) where {Tag, T}
        vf = new{Tag, T}(val::T)
        (validate(vf) || throw(ArgumentError(&quot;Invalid: $val&quot;))) &amp;&amp; vf
    end
end

value(vf::ValidatedField) = vf.val
validate(vf::ValidatedField) = false

abstract type ValidatedRecord end

function Base.getproperty(vr::V, field::Symbol) where V &lt;: ValidatedRecord
    f = getfield(vr, field)
    return f isa ValidatedField ? value(f) : f
end

function Base.setproperty!(vr::V, field::Symbol, val) where V &lt;: ValidatedRecord
    T = fieldtype(V, field) 
    setfield!(vr, field, T &lt;: ValidatedField ? T(val) : val)
end

UserID = ValidatedField{:UserId, String}
validate(u::UserID) = all(islowercase, value(u))

mutable struct User &lt;: ValidatedRecord
    id::UserID # appears as a String but must be a valid UserID
    name::String # just a normal String

    function User(id, name)
        u = new()
        u.id, u.name = id, name
        return u
    end
end

user = User(&quot;foo&quot;, &quot;Bob&quot;)
user.id # =&gt; &quot;foo&quot;
user.id = &quot;Bar&quot; # invalid
user.id = &quot;bar&quot; # valid

huangapple
  • 本文由 发表于 2023年6月9日 09:54:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76436716.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定