Copying one symbol into another

36

15

I would like to know how can I completely copy one Symbol to another. When I say copy, a refer to UpValues, DownValues, FormatValues.. and so on.

I created this function to do that, but I don't know if it's a good practice.

SetAttributes[copy,HoldFirst];
copy[new_Symbol,org_Symbol]:=Module[{},
    ClearAll@new;
    UpValues@new=UpValues@org/.org:> new;
    DownValues@new=DownValues@org/.org:> new;
    FormatValues@new=FormatValues@org/.org:> new;
    SetAttributes[new,Attributes[org]];
]

So I can copy some symbol b into a using a~copy~b.

There is a simpler way to do that? Or this approach is ok?


Update

Thanks for all comments. This is the evolution of the function above:

SetAttributes[copy,HoldFirst];
new_~copy~org_:=With[{prop={Attributes,UpValues, OwnValues, DownValues, SubValues, NValues, FormatValues, Messages,Options}},
    ClearAll@new;
    Set[#@new,#@org/.HoldPattern@org:>new]&~Scan~prop;
]

Murta

Posted 2013-02-21T02:48:24.207

Reputation: 23 859

Do you have a good use case for this? Or is it just theoretical? – Szabolcs – 2013-02-21T03:01:18.220

2There's some clone function of Leonid around, that does basically this. Don't forget Options, NValues, OwnValues, SubValues, Defaults. And make it HoldAll – Rojo – 2013-02-21T03:01:47.750

1Also, you can't trust this for built-ins, some of which seem to have "hidden" values, or for ReadProtected and Locked symbols, or for symbols that are recognized as special by other functions – Rojo – 2013-02-21T03:02:57.593

There's a subtlety: if org refers to org in its definition, will new keep referring to org or to new? – Szabolcs – 2013-02-21T03:03:26.610

In other words, I wouldn't trust this too much unless you know the function you are cloning. And I don't know a better way to copy a symbol – Rojo – 2013-02-21T03:04:15.207

Those replacements are risky too. For starters, wrap org in HoldPattern at least – Rojo – 2013-02-21T03:05:04.600

@Rojo, you are right. If I have the symbol org in the RHS of org I'll have a problem in the way I did. – Murta – 2013-02-21T03:09:07.800

@Rojo This might be the post you're looking for

– rm -rf – 2013-02-21T03:19:15.343

@rm-rf, exactly, but I stopped looking for it after seing the post you were too late to post – Rojo – 2013-02-21T03:20:36.023

@Szabolcs I think I have a good use. I'm working on a function to retrieve database tables in the form of objects. The properties of this object are in down and upvalues of the symbol, and a need to copy it between functions in my code. – Murta – 2013-02-21T03:36:35.080

Related StackOverflow question: http://stackoverflow.com/q/7912984/618728

– Mr.Wizard – 2013-02-21T09:12:31.920

Answers

41

Update It turns out that the correct way is to use ExtendedDefinition, not ExtendedFullDefinition. Please see the answer by @jkuczm for a detailed explanation.


This is a simplification of your solution:

Language`ExtendedFullDefinition[new] = 
 Language`ExtendedFullDefinition[old] /. HoldPattern[old] :> new

I believe Language`ExtendedFullDefinition is used in transferring definitions between the main kernel and subkernels. Also note the HoldPattern on the LHS of the rule which ensures that OwnValues will work.


One thing to think about is: what should happen if old refers back to itself? Should new keep referring to old, or to itself? This solution, like yours, replaces the old in the definition as well.

Szabolcs

Posted 2013-02-21T02:48:24.207

Reputation: 213 047

Something doesn't go well with symbols defined in packages. Even for a public symbol with full context specified, Language\ExtendedFullDefinition[symbolFromPackage]andLanguage`ExtendedFullDefinition[application`package`symbolFromPackage]both evaluate toLanguage`DefinitionList[]`. – akater – 2014-05-27T02:04:34.470

3@Akater Language`ExtendedFullDefinition has an ExcludedContexts option. Check if it's what's blocking the context of the package you want to use. – Szabolcs – 2014-05-27T13:07:28.827

@Szabolcs Yes, it was blocking all user contexts, and lots of built-in ones, as well. It works now, thank you. – akater – 2014-05-28T04:30:15.997

@Szabolcs Is there a particular reason to use Language`ExtendedFullDefinition instead of Language`ExtendedDefinition? Presented version with ExtendedFullDefinition can be dangerous. If old symbol depends on anotherSymbol, then definition of anotherSymbol will be present in DefinitionList given by ExtendedFullDefinition. If anotherSymbol has old symbol anywhere in its ...Values, then assignment from answer will permanently change definition of anotherSymbol. Using ExtendedDefinition instead of ExtendedFullDefinition will prevent it. – jkuczm – 2014-12-11T20:25:27.400

@jkuczm These are undocumented symbols, and much of what I wrote was based on guessing. It sounds like you are likely correct, but I do not have time to test this and update my post today. Perhaps you could write another answer? – Szabolcs – 2014-12-11T20:33:41.600

@Szabolcs Ok I'll add an answer explaining dangers associated with ExtendedFullDefinition. – jkuczm – 2014-12-11T20:41:27.870

Thanks @jkuczm, that will be useful to everyone. – Szabolcs – 2014-12-11T20:41:59.323

1It looks like somebody has added something this to the Function Repository as ResourceFunction["CopyDefinitions"]. – Tanner Legvold – 2020-11-24T02:45:54.833

Nice! I didn't know you could assign to that +1 – Rojo – 2013-02-21T03:09:28.627

@Rojo I learned about it from Oleksandr. I'm not sure if anything can go wrong with this ... check the Options. As I said, it's meant to be used for transferring definitions to subkernels, and for some contexts this should be prevented. – Szabolcs – 2013-02-21T03:12:43.247

Wow, much more simpler!! Tks! I have to think about the "old to itsef" question. I Don't know yet the best to my case. +1 – Murta – 2013-02-21T03:14:03.637

Damn... I just came here to write this! Of course, I learned this from you, so it's right that you got to post it :) – rm -rf – 2013-02-21T03:15:23.007

Does this method properly preserve Unevaluated, which is lost in a regular DownValues call? I am talking about issue described in Leonid's comment and the linked Q&A. (I cannot test it because this function is not in version 7.)

– Mr.Wizard – 2013-03-15T01:34:59.213

@Mr.Wizard Yes, it does seem to preserve it. – Szabolcs – 2013-03-15T16:36:51.267

16

Adding to Szabolcs's answer, it's better to use ExtendedDefinition instead of ExtendedFullDefinition.

In situation in which old symbol (the one that we want to copy), depends on anotherSymbol and anotherSymbol has old symbol somewhere in it's ...Values e.g.:

ClearAll[new, old, anotherSymbol]
old = anotherSymbol
anotherSymbol[] := 2 old

Full definition of old includes definition of anotherSymbol:

Language`ExtendedFullDefinition[old]
(* Language`DefinitionList[
    old -> {
        OwnValues -> HoldPattern[old] :> anotherSymbol, SubValues -> {},
        UpValues -> {}, DownValues -> {}, NValues -> {}, FormatValues -> {},
        DefaultValues -> {}, Messages -> {}, Attributes -> {}
    },
    anotherSymbol -> {
        OwnValues -> {}, SubValues -> {}, UpValues -> {},
        DownValues -> {HoldPattern[anotherSymbol[]] :> 2 old},
        NValues -> {}, FormatValues -> {}, DefaultValues -> {},
        Messages -> {}, Attributes -> {}
    }
] *)

Assignment using ExtendedFullDefinition:

Language`ExtendedFullDefinition[new] =
    Language`ExtendedFullDefinition[old] /. HoldPattern[old] :> new
(* Language`DefinitionList[
    new -> {
        OwnValues -> HoldPattern[new] :> anotherSymbol, SubValues -> {},
        UpValues -> {}, DownValues -> {}, NValues -> {}, FormatValues -> {},
        DefaultValues -> {}, Messages -> {}, Attributes -> {}
    },
    anotherSymbol -> {
        OwnValues -> {}, SubValues -> {}, UpValues -> {},
        DownValues -> {HoldPattern[anotherSymbol[]] :> 2 new},
        NValues -> {}, FormatValues -> {}, DefaultValues -> {},
        Messages -> {}, Attributes -> {}
    }
] *)

has a side effect. As we can see above, rule HoldPattern[old] :> new changes not only possible self references of old, discussed in Szabolcs's answer, but also references to old in definition of anotherSymbol.

By evaluating above assignment we have changed definition of anotherSymbol:

?? anotherSymbol
(* Global`anotherSymbol
anotherSymbol[]:=2 new *)

Language`ExtendedDefinition, in contrast to "Full" variant, returns and assigns only definition of symbol passed to it as argument.

ClearAll[new, old, anotherSymbol]
old = anotherSymbol
anotherSymbol[] := 2 old

Language`ExtendedDefinition[old]
(* Language`DefinitionList[
    old -> {
        OwnValues -> HoldPattern[old] :> anotherSymbol, SubValues -> {},
        UpValues -> {}, DownValues -> {}, NValues -> {}, FormatValues -> {},
        DefaultValues -> {}, Messages -> {}, Attributes -> {}
    }
] *)

Assignment to ExtendedDefinition:

Language`ExtendedDefinition[new] = 
    Language`ExtendedDefinition[old] /. HoldPattern[old] :> new
(* Language`DefinitionList[
    new -> {
        OwnValues -> HoldPattern[new] :> anotherSymbol, SubValues -> {},
        UpValues -> {}, DownValues -> {}, NValues -> {}, FormatValues -> {},
        DefaultValues -> {}, Messages -> {}, Attributes -> {}
    }
] *)

is free of side effect of ExtendedFullDefinition:

?? anotherSymbol
(* Global`anotherSymbol
anotherSymbol[]:=2 old *)

and of course correctly copies definition of old to new:

?? new
(* Global`new
new=anotherSymbol *)

jkuczm

Posted 2013-02-21T02:48:24.207

Reputation: 14 388

1Tks. Very interesting. +1 – Murta – 2014-12-11T22:25:02.240