Associating information to a type


struct Foo

@is_friend(Foo, "Bob")
@is_friend(Foo, "Tim")


Is there a clean way of implementing @is_friend and friends_of to return ["Bob", "Tim"]?

One solution is with a global Dict. Then @is_friend becomes push!(that_dict[Foo], "Bob") and friends_of is trivial. But that will Revise very poorly, and it doesn’t work with precompilation (unless I use Requires.@init, but ehhh…)

Another solution is for @is_friend to become is_friend(::Type{Foo}, ::Val{"Bob"}) = true. Then I can read the friend list off of methods((Type{Foo}, Any,)). That is Revise- and precompilation-friendly. But it’s a terrible solution… I guess?

Am I missing anything here? (Other than “You should rewrite your program in a different way”)

How about creating a module-local constant? That is to say, @is_friend(Foo, "Bob") is lowered to

if @isdefined(__friends)
    const __friends = Dict()
push!(get!(__friends, Foo, []), "Bob")

? This way, __friends can be stored in the precompilation cache.

Then we can look it up via friends_of(Foo) = parentmodule(Foo).__friends[Foo]. It’s probably a good idea to gensym __friends.

I don’t know how to make it Revise-friendly, though.

1 Like

I am not sure I understand the spec fully, but I would go with

is_friend(T, person) = person ∈ friends_of(T)

friends_of(::Type{Foo}) = Set("Bob", "Tim") # maintain list here

which is flexible and should be Revise-friendly.

1 Like

It’s not open. Suppose that instead of friends, I’m registering methods that should be called by Foo at some point. I’m stuck doing a big ugly @register_all_methods Foo begin ... end that contains all the methods in one big block. I have that solution already. @big_macro_that_contains_complex_expansion_code_to_register_all_methods_from_dsl_code. It’s not great. It would be much nicer and modular with a smaller @declare_fruminating_method macro that macroexpands into @register_method Foo ...

I found a decent solution using methods, after all: ConferenceCall.jl

1 Like