10
$\begingroup$

I have some Mathematica code that shows a behaviour I really cannot understand.

The code only contains a bunch of function definitions. If I paste it in the Notebook Front End and I evaluate it, I can then use those functions and they work as expected. Until here everything's fine.

The problems start when I try to create a Package with the same code. I exported the code from the Notebook to a Package and I saved to a file.m file. When I load the package with the command Get["path/to/file.m"], it is loaded correctly and the functions defined in the package become visible in the new Notebook where I loaded the package. The problem is that some functions (at the moment just one foo) do not return the same results on evaluation if they are loaded from the package as if they were simply copy-pasted into the notebook.

I have read around several threads here on stack exchange, on the Wolfram forum and in the documentation and I have already checked the following things in my code:

  • I know that when packages are loaded with Get[] only the initialization cell is evaluated. My Package is composed only by a single cell, which is also marked as Initialization Cell.

  • I know that Input Cells are discarded in Packages and only Code Cells are evaluated. The only cell in my Package is Code.

  • I know about $Context and $ContextPath and that some variables and functions defined in the Package can shadow globals. I am not sure I completely understood all the nuances of these concepts but I placed the function definitions in the private block of the package. UPDATE: I was able to reduce the example to avoid variables, so this should not represent a problem anymore.

  • I also double-checked that the kernel was fresh in both cases:

    • when I executed the code directly from the Notebook
    • when I loaded the newly created package with the same code

After some work I managed to shrink the original Notebook to a minimal subset that reproduces the odd behaviour.

Here is the updated and reduced Notebook:

NaN=Indeterminate;

floatQ[x_]:=If[NumericQ[x],
    If[Head[x]=!=Complex,
        True,
        False
    ],
    If[x===∞∨x===-∞∨x===NaN,
        True,
        False
    ]
]

bar[x_/;floatQ[x]∧x!=∞∧x=!=NaN]:=If[x==0,
    0,
    ⌊Log[2,Abs[x]]⌋
]

foo[x_/;floatQ[x]∧x!=∞∧x=!=NaN]:=If[x==0∨bar[x]==1,
    1,
    ⌈Log[2,1+Abs[bar[x]-1]]⌉+1
]

If I evaluate it and then run following commands I get the following result which is correct.

The code for the package instead is the following:

BeginPackage["minimal`"]

bar::usage = "bar[float_number]"
foo::usage = "foo[float_number]"

Begin["`Private`"]

NaN=Indeterminate;

floatQ[x_]:=If[NumericQ[x],
    If[Head[x]=!=Complex,
        True,
        False
    ],
    If[x===∞∨x===-∞∨x===NaN,
        True,
        False
    ]
]

bar[x_/;floatQ[x]∧x!=∞∧x=!=NaN]:=If[x==0,
    0,
    ⌊Log[2,Abs[x]]⌋
]

foo[x_/;floatQ[x]∧x!=∞∧x=!=NaN]:=If[x==0∨bar[x]==1,
    1,
    ⌈Log[2,1+Abs[bar[x]-1]]⌉+1
]

End[]

EndPackage[]

If I close Mathematica and reopen it (so that I am sure I have a fresh Kernel), and I try to Get[] the package and run the same commands as above I get the following wrong result.

What am I doing wrong?

UPDATE This is not true anymore. I have worked on the example and I have been able to reduce it further as the code above shows. Now there are no variable definitions, just some simple functions that I wrote myself.

Unfortunately the minimal example is not so minimal, but I didn't write the original code myself. It comes from a Notebook I got from the internet and I have not been able to reduce it further. I also tried with some code I wrote, but I have not been able to obtain the same misbehaviour.

As you can see the code in the Notebook and in the Package are really the same, except for the BeginPackage[] EndPackage[] portions.

$\endgroup$
  • $\begingroup$ "from a Notebook I got from the internet" - where exactly? Can you give us an address? $\endgroup$ – J. M. will be back soon May 12 '16 at 10:57
  • $\begingroup$ Of course. It's a Notebook freely distributed with this book: crcpress.com/The-End-of-Error-Unum-Computing/Gustafson/p/book/… You can find it in a .zip file in the Downloads/Updates tab, on the left, just below the picture of the book cover. In the original Notebook there is a ton of code, but the part I provided should be enough and self-contained. I hope this helps. $\endgroup$ – fez May 12 '16 at 11:02
  • $\begingroup$ I'm using Mathematica 10.4 on Linux x86_64 if this is of any help $\endgroup$ – fez May 12 '16 at 11:16
11
$\begingroup$

Update

This bug has been fixed in the just released Mathematica 11.0.


This appears to be a difference in parsing between the frontend and the kernel.

Compare (in a notebook)

HoldForm[⌈x⌉ + 1]

(* Ceiling[x] + 1 *)

with

Get[StringToStream["HoldForm[⌈x⌉ + 1]"]]

(* Ceiling[x] (+1) *)

where the latter has multiplication instead of addition.

One possible workaround is to just add parentheses in the .wl file, e.g.

(⌈Log[2, 1 + Abs[bar[x] - 1]]⌉) + 1
$\endgroup$
  • $\begingroup$ Ok great! This workaround works, both in the code snippet I posted here and in the big Notebook I took it from. But do you know why are the parser doing different things? I mean the correct form should be the first, right? Is this a bug in Mathematica? Or is there a reason why two different ways to parse the same thing should give two different results? I'm fairly new to both Mathematica and Stack Exchange, so maybe this question should be considered closed and I have to ask another question. I don't know...what do you think? $\endgroup$ – fez May 13 '16 at 15:59
  • 4
    $\begingroup$ I am leaning towards 'bug' and I've filed a report for the developers to take a look. $\endgroup$ – ilian May 13 '16 at 16:21
2
$\begingroup$

It seems to me that setenv is being used here to set the values of a series of helper variables that are then used by the other functions in the code. This (is awful but) works within a single notebook because all those variables are visible to all functions.

I suspect, however, that once you put the code in a package, you run into context problems. Those definitions made by setenv will apply to variables within the package, belonging to a different context. I am not sure where the problem lies exactly that generates the incorrect result (code is too long and I don't have time to chase it around), but I suspect that when you run one of the other packaged functions, they somehow end up dealing with either a wrong initialization value, or with an altogether initialized variable.

$\endgroup$
  • $\begingroup$ Yes, you are right, setenv sets helper variables that are used from the other functions. Bu I don't think that the problem is what you said. I call the same commands both in the Notebook and in the Package. If something was messing up the local variables (set with setenv) in the Package, the same operation would mess up the global variables (again set with setenv) in the Notebook version. Wouldn't it? $\endgroup$ – fez May 13 '16 at 7:31
  • $\begingroup$ Hi @MarcoB, I updated the example with a smaller code that stills shows the problem. Now the code does not define new variables. The setenv function is entirely gone, so it should not be a problem of context anymore. Now that the code is so small, is it easier for you to tell what's going on? $\endgroup$ – fez May 13 '16 at 9:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.