Module hasproperty, getproperty Inconsistency

When I use getproperty of :sin in Main I get some results:

julia> getproperty(Main, :sin)
sin (generic function with 13 methods)

But when I am trying to use hasproperty:

julia> hasproperty(Main, :sin)
false

My questions:

  • Is it expected behaviour? It feels a bit counterintuitive
  • Is there any other way to check wether function exists in a module before calling getproperty and getting exception?

The easier part first. Here an example for the different ways to query namespaces and structures

# For modules

@show isdefined(Main, :sin)
@show isdefined(Main, :tin)

@show getproperty(Main, :sin)

# For structs

struct Example
    d
    Example() = new()
    Example(d) = new(d)
end

e = Example()

@show hasproperty(e, :d)
@show isdefined(e, :d)
# @show getproperty(e, :d) #fails

e = Example(42)

@show hasproperty(e, :d)
@show isdefined(e, :d)
@show getproperty(e, :d) 

yielding

isdefined(Main, :sin) = true
isdefined(Main, :tin) = false
getproperty(Main, :sin) = sin
hasproperty(e, :d) = true
isdefined(e, :d) = false
hasproperty(e, :d) = true
isdefined(e, :d) = true  
getproperty(e, :d) = 42

For the second part:

I’ve got somewhat accustomed to it. Do you have a better idea?

1 Like

The documentation of hasproperty says:

  hasproperty(x, s::Symbol)

  Return a boolean indicating whether the object x has s as one of its own
  properties.

The important bit here is “as one of its own properties”.

julia> hasproperty(Base, :sin)
true

julia> a = 1
1

julia> hasproperty(Main, :a)
true
3 Likes

Yes, isdefined seems to be working option in this case, thanks a lot)

Maybe it would be more logical to name it getdefined instead of getproperty. But it is already too late to discuss it)

Yes, I see) But it is unfortunate that there is not much documentation on getproperty(value, name::Symbol)

help?> getproperty(value, name::Symbol)
  getproperty(value, name::Symbol)
  getproperty(value, name::Symbol, order::Symbol)

  The syntax a.b calls getproperty(a, :b). The syntax @atomic order a.b calls 
  getproperty(a, :b, :order) and the syntax @atomic a.b calls 
  getproperty(a, :b, :sequentially_consistent).

  Examples
  ≡≡≡≡≡≡≡≡≡≡

As the documentation says, getproperty is literally what is called when you do a.b. module do using Base automatically and therefore has all its exported symbols inside it (and Main is not exception to this), so they can be accessed with the dot notation and therefore getproperty. If anything, I think what is tripping you is the behavior of module not of getproperty.

I was mainly confused that you can get property of a module that doesn’t have this property)

It has? Every using (even the implicit using Base) make all exported names properties of the importer/user module. They are just not their own properties, and so hasproperty ignores them. Maybe hasproperty should be renamed hasownproperty you mean? Or maybe hasproperty should not restrict itself just to owned properties?

Yes, exactly, something like hasownproperty would be less confusing. Because intuitively getproperty(module, prop) should return property if and only if hasproperty(module, prop) is true

I think the fact that getproperty(module, prop) does not conflate
the module.prop or struct.prop with hasproperty(module, prop) is a good thing.

  1. It allows for overload of the . syntax
  2. You can add props lazily, on-demand, or even self-vivifying
  3. Keeping the functionality explicit and composable is a strength of Julia
julia> struct X end

julia> getproperty(X, :b)
ERROR: type DataType has no field b
Stacktrace:
 [1] getproperty(x::Type, f::Symbol)
   @ Base .\Base.jl:28
 [2] top-level scope
   @ REPL[3]:1

julia> hasproperty(X, :b)
false

You can see there is an error if getproperty(module, prop) is used with a non-existent prop. Having some alternate return value for this case seems an unnecessary extra step.

I do not see how any of your three points stem from the lack of conflation.

  1. How conflating would hinder overload? Asking for consistency does not hinder overload.
  2. Again, this is not hindered in any way, so?
  3. What does this has to do with the problem at hand? Why it would lack explicitness or composability to, for example, have a separate hasproperty and hasownproperty?

You example does not call getproperty(module, prop) it calls getproperty(object, prop) so I am not sure if I understand its purpose.