Package functions and symbolic calculations

8

0

I'm writing my package (.m) in Mathematica and when I call the function it returns 0 instead of a right output.

Here's my code:

Package.m

BeginPackage[ "MyP`"]

Begin[ "`Private`"]

 MyLagrange[f_]:= Module[{a},
      a = D[f,x1];
      a
 ]

End[]

EndPackage[]

If my input is f = Log[x1] it returns 0 instead of 1/x1, what's wrong?

Motosega

Posted 2016-05-10T10:10:42.853

Reputation: 101

Declare Global`x1 in between BeginPackage[] and Begin[], and try again. – J. M.'s ennui – 2016-05-10T10:13:48.260

1@SimonRochester while linked topic is older I'd make it a duplicate of this one. It is because the answer here is more general. Sorry but your solution requires to clear the kernel, before loading a package, to avoid shadowing problems with all exported dummy symbols (here x). Not to mention that if two packages export x the shadowing will be unavoidable. – Kuba – 2016-12-22T10:45:07.683

@Kuba I am inclined to agree, but I never like closing older questions as duplicates of newer ones. That doesn't mean I won't do or allow it, it's just how I feel about it. One alternative that comes to mind is to merge these questions which would move your more general answer to the older question; this would "cost" you an Accept but it would juxtapose your method and caveats with the seemingly simpler approach shown in Simon's answer. – Mr.Wizard – 2016-12-22T17:13:26.233

1@Mr.Wizard I'm fine with that, feel free to merge :) – Kuba – 2016-12-22T17:37:03.160

@Kuba I will wait 24 hours in case anyone has an objection or wishes to reopen this question as a Merge is impossible to reverse without SE developer intervention. (please remind me if I forget) – Mr.Wizard – 2016-12-22T17:38:54.583

@Mr.Wizard Sure, whichever approach is fine with me. One thing to note with the older question is that the code provided in the OP doesn't actually reproduce the problem -- I needed to guess that there was a Private` context being used, as discussed in the comments. – Simon Rochester – 2016-12-22T18:20:43.163

@Simon I see that now. Perhaps a close the other way really would be better unless that older one is rewritten. Since you are the only one who answered that one I guess you are the one who might feel slighted by it being closed. If you do not mind it seems like reversing the direction of the closure would be the simpler (less work) solution. OK? – Mr.Wizard – 2016-12-22T22:23:35.237

@Mr.Wizard Yup, that's ok. – Simon Rochester – 2016-12-22T22:29:06.683

Answers

11

Short explanation

It returns 0 because you are doing something like:

D[ Sin[Global`x1], MyP`Private`x1]
  (*Global` or current $Context really*)

Why? Full answer would be long so I'm sending you to topics linked below, short answer for the most frequent case is:

x1 in notebook will be created in Global` context while x1 from the package in MyP`Private`. And this will happen whether you will use x1 before <<MyP` or after. When MyLagrange[f_... is parsed, $ContextPath contains MyP` and System` only. Also, tricks with putting Global` inside the package won't work when the notebook you are working with has CellContext->Notebook.

You can read more in a related topic: Behavior of Remove inside a Package and in the general one I'm encouraging you to become familiar with: How symbol lookup actually works


Handy tip to consider

MyPackage`Private` context is used so that package implementation symbols won't interfere with outside world.

But the behavior you expect is exactly the opposite!

So if that package is only mean for you for daily research you can just delete Begin/BeginPackage/End/EndPackage and leave a script which will just load some definitions into current context. That is not a general way to go with code meant for distribution but may be just fine for you.

Otherwise read further.


Generic solution

There are some tricks available to detect a variable that we need to differentiate with respect to but I'd say the safest thing is to provide it explicitly:

ClearAll["MyP`*"];

BeginPackage["MyP`"];

   MyLagrange;

Begin["`Private`"];

   MyLagrange[f_, var_] := Module[{a}, 
      a = D[f, var];
      a
   ];

End[];
EndPackage[];

MyLagrange[Sin[x1], x1]
Cos[x1]

I don't think it is too much of a hassle since all D and Solve functions are asking you for related symbols anyway.


Hairy shortcuts

Alternatively you could go with

  • a = D[f, Symbol[$Context <> "x1"]]

    Probably the most stable from this section.

    The problem here is that you force the user to always use x1, does e.g. Solve force you to always use x?.

    Another one is that the x1 could be on $ContextPath but not in $Context. The best example is a package. After Begin["`Private`"] the $Context is MyPackage`Private` while x could have been introduced in MyPackage` earlier. That sounds artificial but still can happen and it is a straightforward way to break your code.

or

  • a = D[f, Global`x1]

    Same as above but even more restricted because the function will only be usable when $Context is Global`. Sounds useful enough? Notice that you can't change notebook's context to 'unique to this notebook' or you cant use that function inside another package.

or

  • D[f, FirstCase[Level[f, {-1}], _Symbol]]

    you never know what's inside f and maybe there are some constant symbols that should just be ignored.

Kuba

Posted 2016-05-10T10:10:42.853

Reputation: 129 207