3

What I'm trying to achieve in Mathematica is the creation of a binary operator whose operands are both pure functions over the natural numbers. The result of the operator should be another pure function over the natural numbers.

To demonstrate concretely what I want, suppose I have the following functions defined:

f[n_Natural]:=2*n;
g[n_Natural]:=n-1;

(There is no Head called "Natural" so the above pattern matching won't work. But I want f and g to accept only natural numbers. This is problem No. 1 [SOLVED])

I then want a binary operator defined like so:

Needs["Notation`"];
CombinedFunction[f_NaturalFunction,g_NaturalFunction]:={#}/.{{x_Natural}:>f[#]+g[#]}}&;
InfixNotation[ParsedBoxWrapper["\[CirclePlus]"], CombinedFunction];

Operating $f$ $\oplus$ $g$ yields a pure function $h$ that only takes a natural number as an argument. I have found a way of enforcing the domain of $h$ thanks to this thread, but I want to extend this to ensure that $\oplus$ itself is only defined for unary functions over the natural numbers. Seeing as there's no Head like 'NaturalFunction', I don't know how to do this. This is problem No. 2.

As an additional issue, the operator (which currently yields a function defined over the integers) currently gives an unsimplified output:

Needs["Notation`"];
CombinedFunction[f_, g_] := {#} /. {{x_Integer} :> f[x] + g[x]} &;
AddInputAlias["4" -> ParsedBoxWrapper["\[CirclePlus]"]];
InfixNotation[ParsedBoxWrapper["\[CirclePlus]"], CombinedFunction];

f=1&;
g=#&;
h=f\[CirclePlus]g
{#1} /. {{x$_Integer} :> (1 &)[x$] \[LeftRightArrow] (#1 &)[x$]} &

I would have expected the output to be:

(1+#)&

I'm unsure of the inner workings of what I've written so I don't know how to obtain a simplified result. I can now apply $h$ to an integer and it operates as expected. However:

h[3.5]
{3.5}

I want instead Mathematica to behave as if the function was simply undefined for anything but an integer, just as it would do if I defined $h$ like so:

Clear[h]; h[x_Integer]:=x+1;
h[3.5]
h[3.5]
Myridium
  • 1,089
  • 5
  • 15
  • 1
    f[n_Integer /; Positive[n]] := 2*n; g[n_Integer /; Positive[n]] := n - 1; (note Natural has a definition where 0 is included, if that's what you need, change condition... – ciao May 30 '14 at 08:14
  • Cheers, that solves the first problem! – Myridium May 30 '14 at 08:15
  • Have a look at DownValues - if the functions involved are just your definitions and you use a consistent format for the "naturals" constraint (and /or also wrap any "foreign" function with a definition of your making with the constraint) you can parse the pattern to see if constraint is present. – ciao May 30 '14 at 08:45

1 Answers1

5

Without giving this much thought you might proceed as follows:

naturalQ = IntegerQ[#] && Positive[#] &;

You can then define:

fn[n_?naturalQ] := 2*n;

fn /@ {-1, 0, 1}
{fn[-1], fn[0], 2}

For the second problem you might make use of SubValues syntax:

SetAttributes[nFun, HoldAll]

nFun[p_, body_][arg_?naturalQ] := With[{p = arg}, body]

Now:

nFun[x, x^2][8]
nFun[x, 2 x][8]
nFun[x, x+5][8]
64
16
13

You can define CirclePlus directly since it is an operator. You don't need the Notation package. (You can enter the \[CirclePlus] character with Escc+Esc.) Again using SubValues syntax:

CirclePlus[f_nFun, g_nFun][arg_?naturalQ] := f[arg] + g[arg]

Now:

f = nFun[x, x];
g = nFun[x, 1];
h = f \[CirclePlus] g
h[7]
8

This doesn't produce a Function object but perhaps it is sufficient. If it is not acceptable please explain how and why and I shall try again.


Anticipating a possible request, and also offering a variant, here is a method that makes use of Slot notation and yields partial evaluation:

ClearAll[nFun, CirclePlus]

SetAttributes[nFun, HoldFirst]
nFun[body_][arg__?naturalQ] := body &[arg]

CirclePlus[nFun[b1_], nFun[b2_]] := nFun[b1 + b2]

Now:

f = nFun[#];
g = nFun[1];

h = f\[CirclePlus]g
h[7]
nFun[#1 + 1]

8

For complete evaluation of the body you can use nFun @@ {b1 + b2} for the RHS of the second definition.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • This contains a lot of useful information for me, thank you. I'll define the CirclePlus directly and use the boolean _? from now on. However, I still desire that f [CirclePlus] g return a pure function. The idea is to produce an abelian group of functions of natural numbers that is closed under addition. So it is essential that adding f and g return another function which you wouldn't know had come from an operation. – Myridium May 30 '14 at 09:11
  • I look forward to your expansion of this (I know it's coming... ;-} ). Have not thought it out much, but my gut feeling is part of this (limiting functions sent to circle-plus to those with domain naturals) is an interesting problem if they include functions not defined by the user. Other than wrapping such things in an appropriate user-defined skeleton, is there a way to interrogate "foreign" functions (e.g., what if Prime were one of the functions - how might one tell its domain is the naturals?). +1, if only to prod... – ciao May 30 '14 at 09:11
  • @Myridium Please see my update made moments before your comment. – Mr.Wizard May 30 '14 at 09:12
  • @rasher I don't have the strength to dig into this to that level right now. I couldn't sleep so here I am, but I'm still tired. – Mr.Wizard May 30 '14 at 09:13
  • 1
    @Mr.Wizard: https://www.youtube.com/watch?v=metwE-QpgAY – ciao May 30 '14 at 09:20
  • @rasher LOL! :-D If only it were that easy -- but preferably without strangulation. – Mr.Wizard May 30 '14 at 09:28
  • Ah, this is good! A kind of 'nFun' class of functions which enforce that their argument be natural numbers. I suppose then that I could wrap any function at all with nFun[ ... ] to to make it conform to this restriction!

    One note: I've made a tweak and added an evaluate: nFun[Evaluate[b1+b2]] so that nFun[#]$\oplus$nFun[2#] = nFun[3#] for instance. This shouldn't cause problems, should it?

    – Myridium May 30 '14 at 09:32
  • 1
    @Myridium Thanks for the Accept. I'll try to review this tomorrow when I'm more awake and hopefully thinking better, if I have time. Your Evaluate expression should do the same this as nFun @@ {b1 + b2} which I mentioned at the bottom of my present answer. It shouldn't cause problems unless there are operations within the body expressions that behave incorrectly with symbolic input. There remains issues and cases to address; for example you may want f \[CirclePlus] g \[CirclePlus] h to work properly; I will need to change the definition for that. – Mr.Wizard May 30 '14 at 09:39
  • Extending it in this way would certainly be appreciated. And right you are about the @@; I had missed that. – Myridium May 30 '14 at 09:44
  • Adding attributes "Flat" and "Orderless" to CirclePlus has done the trick to make $\oplus$ automatically associative. Only thing left to do perhaps is have the operator $\oplus$ take precedence over argument to nFun[ ... ]. i.e. $f \oplus g \oplus h[5]=(f \oplus g \oplus h)[5]$ Not sure if that's possible though. – Myridium May 30 '14 at 09:51
  • @Myridium for what it's worth you could instead use a definition like CirclePlus[fns : nFun[_] ..] := Thread[+fns, nFun] or for complete evaluation := nFun @@ {(+fns)[[All, 1]]}. Regarding binding power I don't think you should change it as such things are standardized (see (30430)); I'm not saying it's impossible, at least from the Notebook interface, but I don't recommend messing with it. – Mr.Wizard May 30 '14 at 10:05
  • @Mr.Wizard i find gems when i read your answers ! +1 – Ali Hashmi Feb 26 '17 at 15:21