Copy Built-in function to new name

11

2

It is know that some built-in functions are system function and changing their properties may cause trouble.

Suppose for example I want to change features of Cases:

SetAttributes[Cases, HoldFirst]

This will create a problem in MMA.

Is there a method to copy the built-in functions (for example Cases) so that the new named function can be used as if it is the built-in functions and then I can do whatever changes I want to the new function?

This works but creates problem in MMA:

Unprotect[Cases];
SetAttributes[Cases, HoldFirst];
Cases[1 + 3, _, {-1}]
(*{1, 3}*)

I tried this but it does not work:

f = Cases;
SetAttributes[f, HoldFirst];
f[1 + 3, _, {-1}]
(*{4}*)

Thanks

Algohi

Posted 2015-02-07T08:34:46.260

Reputation: 19 067

Related: (7912984), (2793)

– Mr.Wizard – 2015-02-07T09:02:59.653

Answers

9

Basics

To get it out of the way, for the specific example given you could use Unevaluated:

Cases[Unevaluated[1 + 3], _, {-1}]
{1, 3}

To actually be able to modify a System function I recommend Internal`InheritedBlock:

SetAttributes[cases, HoldAll]

cases[args___] :=
  Internal`InheritedBlock[{Cases},
    SetAttributes[Cases, HoldFirst];
    Cases[args]
  ]

cases[1 + 3, _, {-1}]
{1, 3}

This does not make additional definitions convenient however. I am working on that.


Experimental construct

I think this may be useful. The idea is to have to associate cases with Cases only once, then be able to work with cases as desired. It is limited to DownValues and Attributes for now as I cannot think of a clean way to extend it to SubValues, UpValues, etc. That can probably be done with Stack but I am not as comfortable with that method as Leonid is so I shall leave it to a last resort.

SetAttributes[{clone, parent}, HoldAll]

clone[from_, to_] := Scan[(#[to] = #[from];) &, {DownValues, Attributes}]

parent[lhs_ -> rhs_] := (
  clone[lhs, rhs];
  Unprotect[rhs];
  x_rhs :=
    Internal`InheritedBlock[{rhs = lhs},
      Unprotect[lhs];
      clone[rhs, lhs];
      x
    ]
  )

Call it before definitions are made to cases:

parent[Cases -> cases];

Then add definitions to cases as desired:

SetAttributes[cases, HoldFirst]               (* test of Attributes *)

cases[a_, b_, "foo"] := cases[2^a, b, {-1}]   (* test of DownValues *)

Result:

cases[1 + 3, _, "foo"]
{2, 1, 3}

Mr.Wizard

Posted 2015-02-07T08:34:46.260

Reputation: 259 163

1

+1. I have to point out, that your experimental construct is very similar to the one I described here - same ideas about double-cloning, and Block-ing.

– Leonid Shifrin – 2015-02-07T14:15:55.187

@Leonid Fair enough. I did link that thread in a Related: comment. I starting working on this before looking at that, remembered you once wrote a clone function, looked at it and decided it was more complicated than I wanted here, and finished what you see above. I have no doubt you could do this more robustly and probably include some Stack magic to provide full emulation, and I encourage you to do so if you have time. – Mr.Wizard – 2015-02-07T14:35:21.453

Re: links - you are right, I haven't checked those. Although, given your answer, it would perhaps make more sense to mention them directly in it, but that's of course up to you. Besides, your construct is still different and used in a different context. Re: clone - there is actually a better way, exposed by Szabolcs here.

– Leonid Shifrin – 2015-02-07T14:42:54.533

@Leonid If I had based my code on yours I would have linked from within the answer as that's my standard practice. (As I'm sure you've seen.) ExtendedFullDefinition and friends don't work with core functions, correct? So specifically not applicable here? – Mr.Wizard – 2015-02-07T14:57:52.730

@Leonid Okay, I could at least use that to copy attributes and downvalues instead of my clone, but I'd need to change the "ExcludedContexts" option which ultimately makes it just as complex. – Mr.Wizard – 2015-02-07T15:06:31.633

Re: links - yes, sure, that's why I said that your code and use case is different. I only meant that it would help link together similar approaches, not in any other sense. Re: core functions - yes, but I thought you take care of internal definitions via Internal`InheritedBlock, while for top-level ones, I thought it should work. – Leonid Shifrin – 2015-02-07T15:28:58.163

Thanks for the answer and for the comments, although I admit I don't understand some of your discussions and some of Mr.Wizard's answer. Feeling ashamed (-.-). – Algohi – 2015-02-07T20:56:08.313