7
$\begingroup$

(NB Related question asks about such things without using the notation package... I would be quite happy with any answer, including the use of Notation)

With long, complex expressions I find the multiplicity of punctuation characters, especially commas, on a line makes the code very hard to parse visually.

I would like to be able to write more verbose but more readable code such as

If[True Then Print["True!"] Else Print["Not True!"]]

Using the Notation package and palette...

<< Notation`
Notation[if[ c_ then t_ else e_]  \[DoubleLongRightArrow] If[c_, t_,e_]]

where as required by the documentation on Entering Notation ("When defining your own notations it is critically important to use the notation templates") the items on either side of the arrow are indeed created with the notation templates.

This does indeed produce an "if then else" construct, but I would like to retain capitalisation as If Then Else, and this doesn't work:

Notation[If[ c_ Then t_ Else e_]  \[DoubleLongRightArrow] If[c_, t_,e_]]

because - even though it is functionally OK - on creating input I see

If[True Then Print["True!"] Else Print["Not True!"], Null, Null, Null]

where the Nulls are greyed out prompts to supply the parts of If[]

Question How can one define such alternate notations, retaining capitalisation and how can they be made compatible with e.g. Which[], where there may be an arbitrary number of conditions and conditional statements, without defining a new Which with notation patterns for each specific number of conditions?

$\endgroup$
  • 1
    $\begingroup$ That's in deed an issue with nested If statements. I typically use a line break for each comma and write comments like (*Then*) and (*Else*) in addition to the commas. Not exactly what asked for, but it works for me. $\endgroup$ – Henrik Schumacher Jun 1 '19 at 12:10
  • $\begingroup$ @HenrikSchumacher I hadn't got as far as nesting. Damn. I also use (*Then*) etc. and I'm fed up with it, If someone with your rep hasn't come up with a neat fix I probably shouldn't hold my breath. It is just SO annoying to have to do that... $\endgroup$ – Julian Moore Jun 1 '19 at 12:35
  • $\begingroup$ Well, I focus on numerical computing and there, using If is a bad in general and in particular with top level code. So I try to avoid it. This is why I do not feel such an urge to fix that. Let's see; maybe somebody comes up with a neat solution... $\endgroup$ – Henrik Schumacher Jun 1 '19 at 13:05
  • 1
    $\begingroup$ If it is only a matter of visualizing, maybe replace code with a "comment"-augmented form? Example: In[50]:= Format[comment[str_String]] := str If[a, b, c] /. Verbatim[If][cond_, then_, else_] :> If[cond, (comment["(Thence)"]; then), (comment["(Elsewise)"]; else)] Out[51]= If[a, "(Thence)"; b, "(Elsewise)"; c]. (I realize this is a bit simple-minded, and leaves a residue of semicolons along with those commas. But it is easier than mucking deep in notation, defining several new functions, etc.) $\endgroup$ – Daniel Lichtblau Jun 2 '19 at 15:05
  • $\begingroup$ @DanielLichtblau or you could do that with boxes and be able to insert comments directly. Also then you could set up a nice input alias that’d add the comment boxes. $\endgroup$ – b3m2a1 Jun 2 '19 at 19:27
5
$\begingroup$

I think the Notation package should be able to do this, but I don't think you need to use it. Instead, create the following InputAliases:

CurrentValue[EvaluationNotebook[],{InputAliases,"then"}] = TemplateBox[
    {},
    "Then",
    DisplayFunction->(StyleBox[" Then",ShowAutoStyles->False, FontColor->GrayLevel[.5]]&),
    InterpretationFunction->(","&),
    SyntaxForm->","
];
CurrentValue[EvaluationNotebook[],{InputAliases,"else"}] = TemplateBox[
    {},
    "Else",
    DisplayFunction->(StyleBox[" Else",ShowAutoStyles->False, FontColor->GrayLevel[.5]]&),
    InterpretationFunction->(","&),
    SyntaxForm->","
];

Then, just use the aliases instead of commas. For example:

If[x>3 Then y = 1; z = 2 Else y = 10; z = 20]

If[x > 3, y = 1; z = 2, y = 10; z = 20]

If you really want to evaluate an If construct, and have it displayed using the "Then" and "Else" tokens, then you would need to use the Notation package. But, I think you want to be able to visually parse code that you've written better, so the package shouldn't be needed.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Perhaps you could add a gif showing how to correctly create the call to Notation? I can't seem to get it to work properly. $\endgroup$ – Chip Hurst Jun 2 '19 at 15:28
  • $\begingroup$ @ChipHurst The InterpretationFunction seems to break the notation. On the other hand, the aliases I created avoid the need for the Notation package. $\endgroup$ – Carl Woll Jun 2 '19 at 16:00
  • $\begingroup$ The primary objective is an aid to code comprehension; this is nice for initial entry purposes but how could help with readability? Didn't understand the last sentence "But... needed" $\endgroup$ – Julian Moore Jun 2 '19 at 18:30
  • $\begingroup$ I think both input alia and extra Format rules would give the best result since the boxes would persist past evaluation. $\endgroup$ – b3m2a1 Jun 2 '19 at 19:29
  • $\begingroup$ @JulianMoore I assumed you were referring to readability of code that you entered using the aliases, especially since you used ==> and not <==> when defining your notation. If you are referring to readability of old code, or other peoples code, then you could add a MakeBoxes rule or use the Notation package to format If as desired. $\endgroup$ – Carl Woll Jun 2 '19 at 20:22
8
$\begingroup$

This still requires some commata and also additional brackets, but it might be somewhat more legible.

Then /: If[cond_, Then[x_], Else[y_]] := If[cond, x, y]
Then /: If[cond_, Then[x_]] := If[cond, x]

To be used like this:

If[
 True,
 Then[
  Print["True!"]
  ],
 Else[
  Print["Not True!"]
  ]
 ]

The reason why I use TagSetDelayed instead of SetDelayed here is that If, as a built-in symbol, is projected. In general, it is good practice not to unprotect and modify built-in symbols (but as all good advice, this has certainly also exceptions). Using Then /: If[...] := ... assigns the definition (more precisely, the rule) to Then which is not protected. That's it.

I am not sure whether this is necessary, but things might become more robust by giving Then and Else the attribute HoldAll.

SetAttributes[Then, HoldAll];
SetAttributes[Else, HoldAll];
| improve this answer | |
$\endgroup$
  • $\begingroup$ Why add the need for extra brackets? Wouldn't Then /: If[cond_, Then, x_, Else, y_] := If[cond, x, y] work too? $\endgroup$ – Chip Hurst Jun 1 '19 at 18:12
  • $\begingroup$ @ChipHurst Good point. However, it just came to my mind that bracketing has the nice effect that code highlighting is able to to mark the beginning and end of the then- and else-clause. Usually, this is where I get pretty lost when having to deal with several nested If-statements. Oh, and we would have to deal with the error message General::argb when using you five-variable version of If. I am not sure what the best way would be. $\endgroup$ – Henrik Schumacher Jun 1 '19 at 18:22
  • $\begingroup$ I've just read the documentation on TagSet (/:) and have to admit that given the examples there I don't understand how this works; could you explain it? NB this has no more typing that @Szabolcs ex. 3 and, for me, has the benefit that the extra typing is functional ;) $\endgroup$ – Julian Moore Jun 2 '19 at 7:32
4
$\begingroup$

Trying to introduce new notation could very easily lead to errors. I think it's not a good idea.

You can make the code more readable using line breaks, indentation and comments.

Example 1

If[condition,
  foo;
  bar;
  baz
  ,
  boo;
  hoo
]

Example 2

If[
    condition
    ,
    foo;
    bar;
    baz
    ,
    boo;
    hoo
]

Example 3

If[
    condition
    , (* then *)
    foo;
    bar;
    baz
    , (* else *)
    boo;
    hoo
]

(Without claiming that mine is a good style, I'd like to show it. While I prefer concise formatting, I still use plenty of indentation and line breaks.)

| improve this answer | |
$\endgroup$
  • 2
    $\begingroup$ Ex. 3 illustrates what my code looks like now and we obviously agree this is quite clear, I'm just a) lazy and b) resentful I can't be as lazy as I want to be ;) $\endgroup$ – Julian Moore Jun 2 '19 at 7:33
3
$\begingroup$

Borrowing from Carl's answer, we can use InputAutoReplacements to automatically insert the aliases instead of explicitly typing them.

SetOptions[EvaluationNotebook[], InputAutoReplacements -> {
  "Then" -> 
    TemplateBox[{}, "Then", 
      DisplayFunction -> (StyleBox[" Then", ShowAutoStyles -> False, 
      FontColor -> GrayLevel[.5]] &), 
      InterpretationFunction -> ("," &), SyntaxForm -> ","],
  "Else" -> 
    TemplateBox[{}, "Else", 
      DisplayFunction -> (StyleBox[" Else", ShowAutoStyles -> False, 
      FontColor -> GrayLevel[.5]] &), 
      InterpretationFunction -> ("," &), SyntaxForm -> ","]
 }];

enter image description here

| improve this answer | |
$\endgroup$

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