2
$\begingroup$

For instance, the user would define a for each loop in their code as having a structure something like: foreach([variable1] : [variable2]){ [statements] } (when [ ] are placeholders) and then the pieces would be used like this: for(i = 0; i < variable2.length; i++){ variable1 = variable2[i]; statements } and then in the rest of the program, the user could use a for each loop with that format: total = 0; foreach(num : numbers){ total += num; }

I hope that example makes sense. To be clear, I am saying that in this hypothetical language a for each loop does not exist, but the user can define one which will, for then on, work exactly the same as any other structure. Are there any languages which allow this?

$\endgroup$
3
  • $\begingroup$ I do have a programming language that does it, but it is a little of an esoteric programming language $\endgroup$
    – MilkyWay90
    Commented Jan 4, 2019 at 18:52
  • 2
    $\begingroup$ Lisp supports that. $\endgroup$ Commented Jan 4, 2019 at 20:03
  • $\begingroup$ Smalltalk supports that as well. $\endgroup$
    – ShyPerson
    Commented Oct 24, 2023 at 0:31

5 Answers 5

2
$\begingroup$

There are two aspects to your question:

  1. Can we arrange a convenient syntax to express one control structure in terms of another?
  2. Can we actually define new control flow mechanisms.

For example, suppose we have a language which only has the if statements without else:

if A then B

Can we express if A then B else C somehow? Yes, like this:

a = A ;
if a then B ;
if not a then C

It would be even better if we could package up the above piece of code into convenient syntax so that we could write just if A then B else C.

Sometimes we can define new control structures using existing ones. For instance, a while loop

while A do B

can be replaced with a call to a recursive function (under some technical assumptions):

function whileLoop():
   if A then (B ; whileLoop ())

whileLoop();

This sort of transformation is a bit more involved because it requires that for each while loop we define a new recursive function. If we were to automate the process, we would have to worry about many details (how to invent unique names of generated functions, do the functions have access to the local variables, etc.) than when we're performing just a straightforward syntactic translation of one variant of loop to another.

On the other hand, there are control mechanism which simply are not expressible in a language, unless already supported. One such example would be continuations. You can't get these in C, for example, but they exist in Scheme.

Your question is about syntax: can we make a syntactic transformation that replaces one piece of syntax with another piece of syntax. Languages usually deal with this sort of thing through a macro mechanism or syntax extensions. Here are some examples:

  • C has macros which will get you some of the way
  • Scheme has a more elaborate macro system which gets you a lot further
  • Racket, a descendant of Scheme, has an elaborate multi-stage computation system which allows essentially arbitrary syntax extensions
  • OCaml has the camplp5 syntax extension tool which allows pretty much arbitrary syntax extensions
  • Template Haskell is an extension of Haskell which allows meta-level transformations of syntax.

I am sure there are many other languages with similar facilities that I am not familiar with.

$\endgroup$
3
  • $\begingroup$ Converge was specifically designed as a language with an expressive macro system despite having complex Python-like syntax. $\endgroup$ Commented Jan 6, 2019 at 12:56
  • $\begingroup$ @JörgWMittag: a link to the language would be nice. $\endgroup$ Commented Jan 6, 2019 at 16:21
  • $\begingroup$ Here you go: convergepl.org $\endgroup$ Commented Jan 6, 2019 at 17:49
1
$\begingroup$

Swift has a syntax that allows you to write something that looks like user defined control structures.

Swift has "closures". Closures are roughly bits of code that can be assigned to a variable or used as a function parameter. That means you can write a function that takes a closure as a parameter, and calls it when it wants.

And there is a syntactic rule: If a closure is the last parameter in a function call, then instead of passing it as the last parameter, you can remove it from the parameter list and add it after the call in { parentheses }. For example, I have a function that executes code on a background thread. I call it like

run_in_background () { write the code here to run in the background }

or a function that does something with all array elements

for_each_array_element (somearray) { code to apply to each array element }

In reality, this is equivalent to a simple function call, but it looks like a control structure.

BTW. || and && are operator functions that don't evaluate their second argument until the code in the operator function asks for it. So you can write

if p != nil && p.x == 0 { }

and the operator&& function gets called - but p.x == 0 isn't evaluated yet at that point.

$\endgroup$
0
$\begingroup$

In FORTH control structures are defined in code. I think Smalltalk had some related features.

$\endgroup$
1
  • $\begingroup$ Indeed. Conditionals / Booleans in Smalltalk are essentially isomorphic to the Church Encoding of Booleans in λ-calculus. Loops are methods on Closures that take Closures as arguments. Classic for loops are methods on Integers that take Closures as arguments. Collections know how to iterate themselves, map themselves, fold themselves, etc. Really, the only control structure built into the language is runtime-polymorphic dynamic message dispatch, aka virtual method calls. The fact that the Stack is just another Smalltalk object that can manipulated allows implementing arbitrary control. $\endgroup$ Commented Jan 6, 2019 at 13:01
0
$\begingroup$

On one level, all computer hardware has primitives capable of expressing various control structures.

The process of "user-defining" a particular control structure is then the process of writing a compiled high-level language.

That process of devising a language and compiler is usually quite difficult, because it involves a careful study, long experience, and a delicate settlement of human factors. Human factors is ultimately the only reason for high-level languages - otherwise, everything can be done with assembly language and integers.

When an already high-level language attempts to furnish a limited set of primitives by which all other concepts are supposed to be implemented, the usual casualty is human factors.

The goto statement (in languages which have it) is the all-purpose solution to control structures which don't easily fit within the standard set of control structures. Often, there is only a very small amount of extra syntactic fuss to using goto instead of the standard control structures.

But the reason languages do not retain only the goto statement, is because it is convenient to have and to use a set of standard and widely understood control structures, which are sufficient on their own to express all possible computation.

And the reason goto is highly discouraged, is precisely because "user-defined" structures outside those standard ones already furnished, are usually misconceived. But it's there for some legitimate residual cases.

Finally, interpreting your question as strictly as possible as being about the definition of custom blocks, with an identifying keyword, and further elements which control details of how the block behaves, there are languages like C with macro preprocessors, in which it is often possible to cobble together what appear visually as custom block structures, and which when compiled are replaced with a more elaborate set of primitive statements.

$\endgroup$
1
  • $\begingroup$ In C++ you cannot goto across variable definitions which makes using it a pain. In situations where goto would have been useful you create a loop “do { xxx } while (false);” and use break instead of goto. Also slightly more structured. $\endgroup$
    – gnasher729
    Commented Oct 23, 2023 at 18:10
-1
$\begingroup$

My programming language, MICGBF, solves your problem.

Any time you make a program, it turns that program into a single command, using a self-changing interpreter.

For example, if you give it the commands ������ and run it, then my programming language would make a new command called <insert tab here> which would execute ������. This could be helpful for making long programs short.

The proof for this is in the Python 3 code

$\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.