3
$\begingroup$

How to achieve the following behavior on a symbol f?

Background: The main goal is to use the symbol to tag an abstract data structure where downvalues are used to store information. Only defined downvalues are to be used. The bare invocation of a symbol in an expression should result in an error message.

Note: I am not asking how to design the abstract data structure. What I am asking is the following:

The context in which this symbol is to be used is something like this (a snapshot of a stack):

f := Fail;
f[_] := Fail;
f[1] = value1;
f[2] = value2;

so that only invocation on existing downvalues or when a new downvalue is defined it works, but both invocation on a non-existing downvalue fails, and the bare invocation fails. For example, assume that the stack looks like this before we start using the symbol

f := Fail
f[_] := Fail

and then we start using the f symbol like this

f = 3 (* fails due to f := $Fail *);
    f[1] = value1 (* works, an assignment *);
    f[2] = value2 (* ibid *);
    x = f + 1 (* fails due to f := $Fail *);
x = f[3] + 1 (* fails due to f[_] := $Fail *);
x = f[1] + 1 (* works: x = value1 + 1 *);
x = f[2] + 1 (* works: x = value2 + 1 *);
f[3] = value3 (* works: an assignemnt *); 
x = f[3] + 1 (* should now work since f[3] has been assigned *);

Here is an example when one actually tries to implement this

ClearAll[f];
f := fail1;
f[___] := fail2

This results in

Information[f]
Global`f
f:=fail1

which is good. We want it to fail if invoked directly without argument. However, the default invocation on non-existing downvalues is already problematic:

Information[fail1]
Global`fail1
fail1[___]:=fail2

thus something that is not wanted. (I understand why this happens: The SetDelayed command evaluates its LHS). Everything goes down the drain right at the beginning, since one would not be able to define the SetDelayed part.

Background[EDIT]: A mathematica code is being passed to a function which changes it slighlty. the code on input looks like, e.g.,

this@f1 = value1
this@f2 = this@f1 + 1

and the processed code should read

o@f1 = value1
o@f2 = o@f1 + 1

if the user specifies

f1 = value1
this@f2 = this@f1 + 1

the system should complain. I know how to do this on the phraser level, no problem. I wonder, is it possible to arrange for the suitable definitions for f1 and f2 so that the behavior I desired occurs, i.e. later on when f1 = value1 is evaluated that an exception occurs?

Also the following is defined silently at the same time

o[id_]@f_ := f[id]

In this way one can define a super simple data strcuture that can be instantiated like a class and operated upon. it should be very natural to write method definitions for such a class.

$\endgroup$
15
  • 1
    $\begingroup$ Check out Stack $\endgroup$
    – Rojo
    Feb 10, 2014 at 10:52
  • $\begingroup$ Why do you think that there is some kind of magic that will allow you to override Mathematica's semantics at such a basic level? $\endgroup$
    – m_goldberg
    Feb 10, 2014 at 13:47
  • 1
    $\begingroup$ @m_goldberg, there is Stack. But most of us tend to avoid it $\endgroup$
    – Rojo
    Feb 10, 2014 at 13:56
  • 3
    $\begingroup$ SetAttributes[dvQ, HoldFirst]; dvQ[sym_] := Stack[_]~MatchQ~{___, HoldForm[_sym], _}; ClearAll[f] f /; StackInhibit[! dvQ[f]] := fail; f[2] = 3; f[_] := fail; $\endgroup$
    – Rojo
    Feb 10, 2014 at 14:01
  • $\begingroup$ @Rojo. I don't think Stack was implemented to provide a way to subvert basic semantics and certainly would not recommend it for that purpose. $\endgroup$
    – m_goldberg
    Feb 10, 2014 at 14:02

2 Answers 2

4
$\begingroup$

General considerations

I think your goal here is misguided. If you state what you are actually trying to accomplish we can probably recommend alternative approaches.

Due to the standard evaluation sequence the heads of expressions are evaluated first, if f has a direct (OwnValues) assignment it will evaluate first in the expression f[1].

Edit: As Rojo also notes Fail is a System symbol with a specific function relating to Condition expressions. I shall use $Failed instead below.

Observe that given in the right order these definitions can be made, though they will not work as you desire:

f[_] := $Failed;
f[1] = value1;
f[2] = value2;
f := $Failed;

OwnValues[f]
{HoldPattern[f] :> $Failed}
DownValues[f]
{HoldPattern[f[1]] :> value1,
 HoldPattern[f[2]] :> value2,
 HoldPattern[f[_]] :> $Failed}

But now:

f[1]
$Failed[1]

As Rojo commented you may be able to use Stack to circumvent this, as Leonid did for How do you set attributes on SubValues? but first I think you should consider alternative approaches. Again, why do you want to do this?

I think work noting: even if f evaluates to $Failed it can still have its value reassigned with Set:

f = foo;

f[2]
foo[2]

I take from your example that you wish to prevent this. Is that the primary motivation of your question?


A possible specific solution

Guessing a bit as to what you need I think this may be moving in the right direction.
Here are two sample inputs, changed from your example to what I think you might have meant:

in1 = "this@f1 = value1
  this@f2 = this@f1 + 1";

in2 = "this = value1
  this@f2 = this@f1 + 1";

Now the process function:

process[in_String] :=
 If[FreeQ[#, HoldPattern[this]], #, $Failed] &[
  ToHeldExpression[in] /. HoldPattern[this[x_]] :> o[x]
 ]

And example applications:

process @ in1
process @ in2
Hold[o[f1] = value1, o[f2] = o[f1] + 1]

$Failed
$\endgroup$
18
  • $\begingroup$ Mhm, was just about to write that definitions can be made in another order, but when I tried it out fail1:=) $\endgroup$
    – Ajasja
    Feb 10, 2014 at 14:08
  • 1
    $\begingroup$ Given that you are giving high level recommendations, I would add to avoid Fail unless you know what to expect $\endgroup$
    – Rojo
    Feb 10, 2014 at 14:13
  • 1
    $\begingroup$ Check ga[] := Block[{}, Fail /; True]; go[] := Block[{}, $Failed /; True]; {ga[], go[]} $\endgroup$
    – Rojo
    Feb 10, 2014 at 14:25
  • 1
    $\begingroup$ It's what RuleCondition returns when its 2nd (and other) arguments aren't all True. I don't think any of this is worth mentioning in this answer, but a warning to avoid Fail seems appropriate $\endgroup$
    – Rojo
    Feb 10, 2014 at 14:28
  • 1
    $\begingroup$ @zorank Well now you are beginning to see the problem with working against Mathematica semantics. Frequently there is a way to force what you want but it comes at a cost of fragility, overhead, or makes your code unportable. However, depending on your usage you may find use in UpSet and TagSet; please look at those, and ask for help as needed. $\endgroup$
    – Mr.Wizard
    Feb 10, 2014 at 15:25
0
$\begingroup$

The solution to this dilema seems to be that these sort of checks have to be done in the phraser that processes the code. Only there is the information sitting how the symbol is to be used.

However, as it concerns the original question, a half way solution is like this:

ClearAll[f]
f /: Set[f, rhs___] := Print["fail1: ", ToString@rhs]
f /: SetDelayed[f, rhs___] := Print["fail2: ", ToString@rhs]
f[arg___] := Print["fail3: ", ToString@arg]

?f
Global`f
(f=rhs___)^:=Print[fail1: ,ToString[rhs]]
(f:=rhs___)^:=Print[fail2: ,ToString[rhs]]
f[arg___]:=Print[fail3: ,ToString[arg]]

runCode := 
 (
  Print["In (should fail): f=3"]; f = 3 (*fails with fail1 *);
  Print["In: f[1]=value1"]; f[1] = value1 (*works,an assignment*);
  Print["In: f[2]=value2"]; f[2] = value2 (*ibid*);
  Print["In (should fail but does not): x=f+1"]; x = f + 1 (*?fails with?*);
  Print["In (should fail): x=f[3]+1"]; x = f[3] + 1 (*fails with fail3 *);
  Print["In: x=f[1]+1"]; x = f[1] + 1 (*works:x=value1+1*);
  Print["In: x=f[2]+1"]; x = f[2] + 1 (*works:x=value2+1*);
  Print["In: f[3]=value3"]; f[3] = value3 (*works:an assignemnt*);
  Print["In: x=f[3]+1"]; x = f[3] + 1 (*should now work since f[
  3] has been assigned*);
  )

now we test it

runCode    
In (should fail): f=3
fail1: 3

In: f[1]=value1

In: f[2]=value2

In (should fail but does not): x=f+1

In (should fail): x=f[3]+1   
fail3: 3

In: x=f[1]+1

In: x=f[2]+1

In: f[3]=value3

In: x=f[3]+1

Almost, almost... The only problem is the "In (should fail but does not): x=f+1 " piece, when the symbol is used without an argument. For example, the following does not work ("TagSetDelayed::tagnf: Tag f not found in lhs_:=rhs_")

f /: SetDelayed[lhs_, rhs_] := (lhs := rhs) /; 
  failIfRhsIsNotOK[rhs, f]

nor ("TagSetDelayed::tagpos: Tag f in lhs_:=rhs_/;failIfRhsIsNotOo[rhs,f] is too deep for an assigned rule to be found. ")

f /: SetDelayed[lhs_, 
  rhs_ /; failIfRhsIsNotOK[rhs, f]] := (lhs := rhs) 

nor ("ibid")

f /: SetDelayed[lhs_, rhs_?failIfRhsIsNotOK[rhs, f]] := (lhs := rhs) 

In fact, even a simple construct like this

f /: Set[lhs_, rhs : Plus[_, f]] := (lhs = rhs)

does not work ("TagSetDelayed::tagpos: Tag f in lhs_=rhs:f+_ is too deep for an assigned rule to be found.")

Anyway, seems this is the best one can do without reworking how Mathematica deals with expressions on the basic level.

Regards Zoran

$\endgroup$

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.