7
$\begingroup$

I would like to utilize the MathLink option of having each Install of my package result in a linking object which I can use to control when and how certain elements are used in the underlying C code.

However, I have difficulties when implementing Mathematica functions on top of the functions which require the LinkObject. Of course I need the SetDelayed statement due to variable passing etc.

Take for instance the counter.tm example:

:Evaluate:      BeginPackage[ "counter`"]

:Begin:
:Function:      AddToCounter
:Pattern:       AddToCounter[$CurrentLink, n_Integer]
:Arguments:     {n}
:ArgumentTypes: {Integer}
:ReturnType:    Integer
:End:

:Evaluate:      EndPackage[ ]

This works great until I define additional functions, take for instance this additional function (it is placed within the BeginPackage/EndPackage).

:Evaluate:   MyAddToCounter[$CurrentLink,n_Integer] := 
                    Return[AddToCounter[$CurrentLink,n+1]]

However, when calling MyAddToCounter I only get the function call as a symbol:

In[2]:= MyAddToCounter[link,20]
Out[2]:= AddToCounter[$CurrentLink, 21]

Why is this?
And how can I preserve the separation of each LinkObject to the same package?

$\endgroup$
7
  • 3
    $\begingroup$ Try defining MyAddToCounter[link_,n_Integer] := AddToCounter[link,n+1], or simply MyAddToCounter[n_Integer] := AddToCounter[$CurrentLink,n+1]. The $CurrentLink variable is a global variable and you don't have to include it in the function's signature. It is likely different when you call the function that when you define it, and so the pattern does not match. $\endgroup$ Commented Sep 28, 2012 at 22:58
  • $\begingroup$ Brilliant! And I can now see why. The Install defines $CurrentLink and inserts it whereever specified. However, in cases of SetDelayed the function is not evaluated until an explicit call to the function. But at that point the $CurrentLink is not valid anymore. Hence we need ordinary variable passing! Thanks a lot! That helped! $\endgroup$
    – nickpapior
    Commented Sep 28, 2012 at 23:55
  • $\begingroup$ @LeonidShifrin I read your answer but did not check additional comments when I was writing up my answer.. Now your answer is gone and your comment contains everything important. $\endgroup$
    – halirutan
    Commented Sep 29, 2012 at 0:37
  • 1
    $\begingroup$ @halirutan My answer was wrong. I did not quite remember the mechanics of interation between Install and scoping. Usually I just look at the source code generated by mprep when I have questions, but had no time today. In any case, one should not answer in comments, and you give a full-fledged answer with explanations, so your answer is the answer here. I am actually very happy that you did it since I am quite busy at the moment. I think SE model is pushing us to think too often about the timing and to race to be the first at all costs. I dislike this, we should collaborate more. $\endgroup$ Commented Sep 29, 2012 at 0:49
  • $\begingroup$ @halirutan Just one thing (being a bit pedantic): since there is no Private` subsection in the package, usage message is not necessary to export the symbol (I also initially thought it is), as the symbol is already created in the package main context. Of course, having a usage message is a good style anyway. $\endgroup$ Commented Sep 29, 2012 at 0:52

1 Answer 1

6
$\begingroup$

In my opinion there are two things to consider. First, you want to evaluate the right hand side of your MyAddToCounter during installing your program to ensure that $CurrentLink has a value. Second, it is good practive to give your function a usage message. Although, this is not required here since you have no Private` context and the package context is prepended to the $ContextPath when EndPackage is called. This leaves you with

int AddToCounter P(( int));

:Evaluate:      BeginPackage[ "counter`"]

:Begin:
:Function:      AddToCounter
:Pattern:       AddToCounter[$CurrentLink, n_Integer]
:Arguments:     {n}
:ArgumentTypes: {Integer}
:ReturnType:    Integer
:End:

:Evaluate:      AddToCounter::usage = "AddToCounter[ ck, n] adds n to the counter ..."
:Evaluate:      MyAddToCounter::usage = "Pure energy."
:Evaluate:      MyAddToCounter[link_, n_Integer] := AddToCounter[link,n+1]
:Evaluate:      EndPackage[ ]

and everything should work fine. Let me make clear what really happens here:

The pattern for AddToCounter contains the symbol $CurrentLink which gets a different value everytime you Install your program. Therefore, when you have n different installations of your MathLink program, you have n different rules in the DownValues of AddToCounter.

On the other hand MyAddToCounter does always have the same pattern and therefore no additional down value rule is added when you install a new instance. Depending on how clever Mathematica is the line MyAddToCounter[link_, n_Integer] := AddToCounter[link,n+1] (with an arbitrary complex rhs) is processed every time you install a new instance without any use.

For such occasions I would suggest that you don't pack the code inside the template file. It is completely sufficient to evaluate

BeginPackage["counter`"];
MyAddToCounter[link_, n_Integer] := AddToCounter[link, n];
EndPackage[];

one time inside your Mathematica session or inside a package which is loaded.

If you do have a function which requires to make use of the $CurrentLink you cannot simply make a definition like f[..]:=...$CurrentLink.... Remember that SetDelayed (:=) does not evaluate its right side. Therefore, $CurrentLink is not evaluate and stays in the definition as symbol. One possible solution is for instance a construct like this

With[{link = $CurrentLink},
 MyAddToCounter[link, n_Integer] := AddToCounter[link, n];
]

inside the template code.

The conclusion is: If you have an additional function which does not explicitly depend on $CurrentLink but only gives it through to a real template-function, you can simply define it inside Mathematica. If it depends explicitly on $CurrentLink you need to put in in the template code and you need to ensure that it is evaluated correctly during the installation process.

Compilation of the sample code

Anyone interested can try this fast by copying counter.tm from the examples directory and adapting the paths in the code below. If you are on Windows you may have to use mprep.exe in the Run call

tmfile = "/home/patrick/Documents/notebooks/SE/counter.tm";
{cfile, outfile} = StringReplace[tmfile, start__ ~~ ".tm" :> start ~~ #] & /@  {".c", ""};

Run[$InstallationDirectory <> 
 "/SystemFiles/Links/MathLink/DeveloperKit/"<> $SystemID <>
 "/CompilerAdditions/mprep", tmfile, "-o", cfile]

<< CCompilerDriver`
prog = CreateExecutable[{cfile}, "counter", "ShellOutputFunction" :> Print, 
 "ShellCommandFunction" :> Print]

lnk = Install[prog]
MyAddToCounter[lnk, 20]
$\endgroup$
6
  • $\begingroup$ yes and no. The answer you provide forces the assignment to be Set and not SetDelayed. I need the SetDelayed. Furthermore, the idea of having a global variable within the context of the package and passing the $CurrentLink is so that I can load it several times, and have different global variables for each LinkObject. This means that MyAddToCounter[n_Integer] = AddToCounter[$CurrentLink,n+1] will only work on the latest installed package, and will not let you decide which LinkObject that gets incremented. Hence MyAddToCounter[link_,n_Integer] := AddToCounter[link,n+1] is needed $\endgroup$
    – nickpapior
    Commented Sep 29, 2012 at 8:18
  • $\begingroup$ This simple example is not very insightful, but can you explain why you need SetDelayed? Have you really understood what does (not) happen with the DownValues of MyAddCounter when you use the pattern MyAddToCounter[link_,n_]? And finally, that you need the link name on the left hand side of your function def in order to distinguish several installs is already explained under the first code box. $\endgroup$
    – halirutan
    Commented Sep 29, 2012 at 12:03
  • $\begingroup$ probably I have no idea why, would you mind we take it to a chat? $\endgroup$
    – nickpapior
    Commented Sep 29, 2012 at 12:15
  • 1
    $\begingroup$ @zeroth, I'm chattable now. $\endgroup$
    – halirutan
    Commented Sep 29, 2012 at 12:22
  • $\begingroup$ I have sevaral calls within the actual function that I call, so I would like to have it executed each time instead of the usual Set. It involves a function: MyReadFile[link_,fname_String, OptionsPattern[{...}]] := Module[{tmp},Switch[tmp = OptionValue[...],...];Return[templateMyReadFile[fname]]; if that makes it more clear why I need SetDelayed? $\endgroup$
    – nickpapior
    Commented Sep 29, 2012 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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