Stream: helpdesk (published)

Topic: Acceptable type piracy?


view this post on Zulip Timothy (Apr 27 2023 at 09:32):

I have a package where I want to create an extensible group of objects. I'm currently doing this via type parameters + dispatch, i.e. downstream packages defining methods for Thing{:specific_symbol}. This seems to be working rather well, but from what I can tell is technically type piracy.

I'd be interested to hear thoughts on this.

[ Cross-post from Slack: https://julialang.slack.com/archives/C6A044SQH/p1682264929271579 ]

view this post on Zulip jar (Apr 27 2023 at 18:21):

That looks like type piracy to me. If two users both define Thing{:foo} they'll collide. Instead if they define their own namespaced value Thing{ModuleA.Foo}/Thing{ModuleB.Foo} then they won't.

view this post on Zulip Mason Protter (Apr 27 2023 at 18:53):

Dispatch on type parameters is a bit of a grey area, but generally speaking it is considered type piracy

view this post on Zulip Mason Protter (Apr 27 2023 at 18:53):

Honestly though without context it's really hard to say if it's a bad thing or not. Sometimes type piracy is a really useful tool

view this post on Zulip Mason Protter (Apr 27 2023 at 18:54):

Sometimes it's a horrible idea

view this post on Zulip jar (Apr 27 2023 at 19:22):

What's a scenario where Base.maximum(Vector{MyType}) = causes a problem?

view this post on Zulip Mason Protter (Apr 27 2023 at 19:25):

If someone in Base wrote code that relied on Base.maximum(::Vector) behaving in a specific way, and you pirated a new Vector{MyType} method in there that changed it's behaviour, then that could cause problems

view this post on Zulip Mason Protter (Apr 27 2023 at 19:28):

E.g. imagine if you wrote a method Base.maximum(::Vector{MyType}) that calculated the maximum by comparing the absolute values of the MyTypes rather than the regular values, then maximum wouldn't be the same as doing a reduce(max, ::Vector{MyType}) which might be relied upon somewhere else

view this post on Zulip Mason Protter (Apr 27 2023 at 19:29):

You chose an example where one could easily say "well there's a clearly right or wrong way to implement maximum" but that's more or less besides the point.

view this post on Zulip Mason Protter (Apr 27 2023 at 19:30):

Instead of overloading maximum which operates on containers here, you should be overloading the scalar functions max, min, <, etc.

view this post on Zulip Michael Abbott (Apr 27 2023 at 22:05):

Thing{:foo} with a symbol seems a bit like environment variables, where ENV["package_var"] = "true" is OK so long as everyone sticks to some convention like having their unique package name there.

view this post on Zulip Timothy (Apr 28 2023 at 00:15):

In my case, I have a TOML file with entries of different types, and each entry specifies "I am an X". The way I'm handling this is by using that to construct Entry{:X} structs and then relying on dothing(::Entry{:X}) methods for a few crucial functions, and for more generic dogenericthing(::Entry) methods for general functionality. This way, to add support for a new entry type Y, a package just needs to implement a few dothing(::Entry{:Y}) entries, it is possible that one package could override another's methods. Singletons from the packages as parameters would be better in this respect, but I need to construct the Entry{:X} instances based on the string in the TOML, which seems a fair bit less straightforward in the singleton type parameter approach.

I have a feeling in practice this should be ok, but I appreciate the second opinions.

view this post on Zulip jar (Apr 28 2023 at 00:37):

If this is supposed to scale to many users, I think explicit namespacing should be used. eg using a tuple of (packagename, symbol) or similar.

view this post on Zulip Timothy (Apr 28 2023 at 07:19):

The entry types (which in this case are for data storage formats, e.g. csv, json, ...) are defined by packages, not users. I suppose the question here is whether it's worth worrying the creation or loading of two packages that say define a :json type. If one hopes that all potential packages that might build on my project will nicely cooperate and not fight over a certain name, then I suppose we'll be all right. If not, then I'll need to add a way of being able to specify the package and switch to singletons, or similar.

view this post on Zulip Alexander (Apr 28 2023 at 09:22):

Mason Protter said:

then maximum wouldn't be the same as doing a reduce(max, ::Vector{MyType}) which might be relied upon somewhere else

This can happen without any kind of near-piracy at all. Define your own AbstractVector type, define maximum for it in an arbitrary way - now you get this inconsistency.
The issue here isn't piracy, it's non-formalizeable "different methods of the same function should have the same general behavior".

view this post on Zulip Sukera (Apr 28 2023 at 09:24):

It's absolutely formalizeable, we just don't have any way to enforce these interfaces/connections at the moment

view this post on Zulip Mason Protter (Apr 28 2023 at 15:20):

@Alexander the thing though is that code typically is anle to rely much more heavily on implementation details when they’re operating on concrete types they own. When operating on an AbstractVector the writer needs to be careful to write highly generic code that hews to accepted norms and documented interfaces. That isnt really the case with bare Vector


Last updated: Oct 02 2023 at 04:34 UTC