Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/Concepts/dt_collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ If you assign an element's index that surpasses the last existing element of the
//myCol[4]=null
```



## Instantiation

Collections must have been instantiated, otherwise trying to read or modify their elements will generate a syntax error.
Expand Down Expand Up @@ -112,6 +114,46 @@ You can create two types of collections:

For more information, refer to the [Shared objects and collections](shared.md) section.


## Assignment

Collection and [object](./dt_object.md) data types are handled in the 4D language through **references** (i.e., internal pointers), unlike scalar data types (integer, date, etc.). As a result, when assigning a collection to a variable (e.g. `$myVar:=[1;2;3]`), it is the **reference** that is assigned, not the value itself. Any subsequent modification of the *$myVar* variable will therefore be reflected everywhere the original collection is referenced. This follows the same principle as [pointers](./dt_collection.md), except that the *$myVar* variable does not need to be dereferenced.

For example:

```4d
var $col1; $col2 : Collection
var $o : Object

$col1:=[1;2;3] //a reference to the collection is created
$col2:=$col1 //both variables share the same collection reference
$o:={ list:$col1 } //the object stores a reference to the same collection

$col1.push(4)
//$col2 = [1,2,3,4]
//$o = {"list":[1,2,3,4]}

$col2[0]:=10
//$col1 = [10,2,3,4]
//$o = {"list":[10,2,3,4]}

$o.list.push(5)
//$col1 = [10,2,3,4,5]
//$col2 = [10,2,3,4,5]

ASSERT($col1=$col2) //True
```

This principle applies wherever objects or collections are assigned, including in [parameters](./parameters.md) or [formula](../commands/formula) expressions.

:::note

If you want to create a **deep copy** of a collection, use the [`collection.copy()`](../API/CollectionClass.md#copy) function.

:::



## Collection functions

4D collection references benefit from special class functions (sometimes named *member functions*). Collection functions are listed in the [Class API Reference](../API/CollectionClass.md) section.
Expand Down
38 changes: 37 additions & 1 deletion docs/Concepts/dt_object.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@ Keep in mind that property names differentiate between upper and lower case.
:::


You manage Object type variables, fields or expressions using the standard [object notation](#properties) or the commands available in the **Objects (Language)** theme. Note that specific commands of the **Queries** theme such as `QUERY BY ATTRIBUTE`, `QUERY SELECTION BY ATTRIBUTE`, or `ORDER BY ATTRIBUTE` can be used to carry out processing on object fields.
You manage Object type variables, fields or expressions using the standard [object notation](#properties) or the commands available in the **Objects (Language)** theme.

Each property value accessed through the object notation is considered an expression. You can use such values wherever 4D expressions are expected:

- in 4D code, either written in the methods (Code Editor) or externalized (formulas, 4D tags files processed by `PROCESS 4D TAGS` or the Web Server, export files, 4D Write Pro documents...),
- in the Expression areas of the Debugger and the Runtime explorer,
- in the Property list of the Form editor for form objects: Variable or Expression field as well as various selection list box and columns expressions (Data Source, background color, style, or font color).






## Instantiation

Objects must have been instantiated, otherwise trying to read or modify their properties will generate a syntax error.
Expand Down Expand Up @@ -122,6 +127,37 @@ You can create two types of objects:
For more information, refer to the [Shared objects and collections](shared.md) section.


## Assignment

Object and [collection](./dt_collection.md) data types are handled in the 4D language through **references** (i.e., internal pointers), unlike scalar data types (integer, date, etc.). As a result, when assigning an object or a collection to a variable (e.g. `$myVar:={ a:2 }`), it is the **reference** that is assigned, not the value itself. Any subsequent modification of the *$myVar* variable will therefore be reflected everywhere the original object is referenced. This follows the same principle as [pointers](./dt_collection.md), except that the *$myVar* variable does not need to be dereferenced.

For example:

```4d
var $o1; $o2 : Object
var $col : Collection

$col:=[1;2;3] //a reference to the collection is created
$o1:={ a:2 ; b:$col } //a reference to the object is created
$o2:=$o1 //both variables $o1 and $o2 share the reference to the same object

$o1.a:=10 //$o2 = {"a":10,"b":[1,2,3]}
$o2.a:=20 //$o1 = {"a":20,"b":[1,2,3]}
$col.push(4)
//$o1 = {"a":20,"b":[1,2,3,4]}
//$o2 = {"a":20,"b":[1,2,3,4]}
ASSERT($o1=$o2) //True
```

This principle applies wherever objects or collections are used, including in [parameters](./parameters.md) or [formula](../commands/formula) expressions.


:::note

If you want to create a **deep copy** of an object, use the [`OB COPY`](../commands/ob-copy) command.

:::


## Properties {#properties}

Expand Down
22 changes: 22 additions & 0 deletions docs/Concepts/flow-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -799,3 +799,25 @@ logConsole($message)

```

## defer (expression)

<details><summary>History</summary>

|Release|Changes|
|---|---|
|21 R4|Added
</details>


The [`defer`](../commands/defer) command allows you to stack one or more expression(s) that will automatically execute when the current method or function **finishes running**.

Whether you are managing document closings, resetting interprocess flags, or freeing up resources, ensuring that your housekeeping tasks execute flawlessly no matter how or where your function terminates can be handled by `defer` keywords.


```4d
//make sure some code is executed at exit
defer(myCleaningMethod)
//Do something...
```

See the [`defer`](../commands/defer) command description for more information.
2 changes: 1 addition & 1 deletion docs/Concepts/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ In the 4D Language, there are several categories of methods. The category depend
|**Form method**|Automatic, when an event involves the form to which the method is attached|No|Property of a form. You can use a form method to manage data and objects, but it is generally simpler and more efficient to use an object method for these purposes.|
|**Trigger** (aka *Table method*)|Automatic, each time that you manipulate the records of a table (Add, Delete and Modify)|No|Property of a table. Triggers are methods that can prevent "illegal" operations with the records of your database.|
|**Database method**|Automatic, when a working session event occurs|Yes (predefined)|There are 16 database methods in 4D. |
|**Class**|Automatically called when an object of the class is instantiated or when a function of the class is executed on an object instance in any other methods or in a [database field](../Develop/field-properties.md#class).|yes (class functions)|A **Class** is used to declare and configure the class [constructor](./classes.md#class-constructor), [properties](./classes.md#property), and [functions](./classes.md#function) of objects. See [**Classes**](classes.md) |
|**Class**|Automatically called when an object of the class is instantiated or when a function of the class is executed on an object instance in any other methods or in a [database field](../Develop/field-properties.md#class).|yes (class functions)|A **Class** is used to declare and configure the class [constructor](./classes.md#class-constructor), [properties](./classes.md#property), and [functions](./classes.md#function) of objects. See [**Classes**](classes.md) and [**Function** class](../API/FunctionClass.md). |


7 changes: 5 additions & 2 deletions docs/Concepts/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@ The values that operators affect are operands. In the expression `1 + 2`, the +
The **assignment operator** (`a:=b`) initializes or updates the value of `a` with the value of `b`:

```4d
$myNumber:=3 //assigns 3 to MyNumber variable
$myDate:=!2018/01/21! //assigns a date literal
$myNumber:=3 //assigns 3 to $myNumber variable
$myDate:=!2026/01/21! //assigns a date literal
$myLength:=Length("Acme") //assigns the result of the command (4) to $myLength
$col:=New collection //$col is initialized with an empty collection
$myObject:={ a:2 ; b:$col } //assigns an object reference to $myObject
$myObject.a:=3 //assigns a value to an object property
```

> Do NOT confuse the assignment operator `:=` with the equality comparison operator `=`. A different assignment operator (and not `=`) was deliberately chosen to avoid issues and confusion which often occur with == or === in other programming languages. Such errors are often difficult to recognize by the compiler and lead to time-consuming troubleshooting.



## Basic operators

Operator results depend on the **data types** they are applied to. 4D supports different operators on scalar data types. They are described with the data types, in the following sections:
Expand Down
10 changes: 10 additions & 0 deletions docs/Notes/updates.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ id: updates
title: Release Notes
---

## 4D 21 R4

Read [**What’s new in 4D 21 R4**](https://blog.4d.com/whats-new-in-4d-21-r4/), the blog post that lists all new features and enhancements in 4D 21 R4.

#### Highlights

- New [`defer`](../commands/defer) command to declare some code to be always executed at method or function exit.



## 4D 21 R3

Read [**What’s new in 4D 21 R3**](https://blog.4d.com/whats-new-in-4d-21-r3/), the blog post that lists all new features and enhancements in 4D 21 R3.
Expand Down
4 changes: 2 additions & 2 deletions docs/language-legacy/Formulas/execute-formula.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ displayed_sidebar: docs

The statement string must be one line. If *statement* is an empty string, **EXECUTE FORMULA** does nothing. The rule of thumb is that if the *statement* can be executed as a one-line method, then it will execute properly. Use **EXECUTE FORMULA** sparingly, as it can slow down execution speed. In a compiled database, the line of code is not compiled. This means that *statement* will be executed, but it will not have been checked by the compiler at compilation time.

**Note:** Executing formulas in compiled mode can be optimized using a cache (see *Cache for formulas in compiled mode* below).
**Note:** Executing formulas in compiled mode can be optimized using a cache (see [Cache for formulas in compiled mode](#cache-for-formulas-in-compiled-mode) below).

The *statement* can include the following elements:

Expand All @@ -46,7 +46,7 @@ The *statement* can include the following elements:
* If *statement* is a project method, it is recommended to use the [EXECUTE METHOD](../commands/execute-method) that allows you to pass parameters.
* It is not recommend to call any variable declaration in *statement* since it can generate conflicts in the code.

The formula can include process variables and interprocess variables. However, the statement cannot contain control of flow statements (If, While, etc.), because it must be in one line of code.
The formula can include process variables and interprocess variables. However, the statement cannot contain control of flow statements (If, While, Return, Break, etc.), because it must be in one line of code. These keywords will be ignored.

To ensure that the *statement* will be evaluated correctly regardless of the 4D language or version used, we recommend using the *token* syntax for elements whose name might vary between different versions (commands, tables, fields, constants). For example, to insert the [Current time](../commands/current-time) command, enter '**Current time:C178**'. For more information about this, refer to *Using tokens in formulas*.

Expand Down
2 changes: 1 addition & 1 deletion docs/language-legacy/Formulas/formula.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ displayed_sidebar: docs

## Description

The `Formula` command <!-- REF #_command_.Formula.Summary -->creates a `4D Function` object based upon the *formulaExp* expression<!-- END REF -->. *formulaExp* can be as simple as a single value or complex, such as a project method with parameters.
The `Formula` command <!-- REF #_command_.Formula.Summary -->creates a `4D Function` object based upon the *formulaExp* expression<!-- END REF -->. *formulaExp* can be as simple as a single value or complex, such as a project method with parameters. For more information on what *formulaExp* can contain, please refer to the [`EXECUTE FORMULA`](../commands/execute-formula) command description.

Having a formula as an object allows it to be passed as a parameter (calculated attribute) to commands or methods or to be executed from various components without needing to declare them as "shared by components and host database". When called, the formula object is evaluated within the context of the database or component that created it.

Expand Down
127 changes: 127 additions & 0 deletions docs/language-legacy/Interruptions/defer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
---
id: defer
title: defer
slug: /commands/defer
displayed_sidebar: docs
---

<!--REF #_command_.defer.Syntax-->**defer** ( *exitFormula* : Expression )<!-- END REF-->
<!--REF #_command_.defer.Params-->
<div class="no-index">

| Parameter | Type | | Description |
| --- | --- | --- | --- |
| exitFormula | Expression | &#8594; | Expression to be executed at exit|
</div>
<!-- END REF-->

<div class="no-index">
<details><summary>History</summary>

|Release|Changes|
|---|---|
|21 R4|Created|

</details>
</div>

## Description

<!--REF #_command_.defer.Summary-->The `defer` command declares an *exitFormula* expression that will always be executed when the method or function exits, even if an error has been thrown or a `return` has been executed<!-- END REF-->. Using a `defer` command allows you to ensure that a method or function ends correctly by executing completion code on exit. In addition, this command saves you from having to duplicate the same exit code for every return or catch block.

:::tip Related blog post

[Streamline Your Clean-Up Code with the “defer” Command](https://blog.4d.com/streamline-your-clean-up-code-with-the-defer-command)

:::


The `defer` command can be called anywhere in the method or function code, and you can insert as many `defer` expressions as you want in the code. During execution, all encountered *exitFormula* expressions are stacked. When the code execution stops, whatever the reason (normal flow, break, error, user abort, return...), all expressions in the "deferred stack" are popped and executed in LIFO (*Last In First Out)* order.

For example:

```4d
defer(ALERT("1"))
defer(ALERT("2"))
// At exit, alerts will display "2" and then "1"
```

In *exitFormula*, you pass the expression that you want to be evaluated upon method or function exit, whatever the way it exited. Behind the scenes, every time a `defer` is called, 4D converts *exitFormula* into a [formula](../../commands/formula) and adds it to a stack associated with the method or function. When the method or function ends, all formulas stored in the stack are evaluated in the order they appear in the collection.

As for all [formulas](../../commands/formula), if the *exitFormula* expression uses local variables, their current values are copied and stored in the formula object returned **when it is put in the *deferred stack***. When executed, the formula uses these copied values rather than the current values of the local variables.

:::note Notes

- Keep in mind that local variables store **references** for [object](../../Concepts/dt_object.md#assignment) and [collection](../../Concepts/dt_collection.md#assignment) values.
- If *exitFormula* contains another `defer` statement, an error is thrown.

:::

If the *exitFormula* expression throws an error, it is automatically intercepted and ignored and the execution flow continues without any interruption.



## Example 1

These examples illustrate the various supported *exitFormula* expressions:

```4d
// Method call
defer(aMethod)

// Object function call
defer(myObject.aFunction(something))

// Singleton function call
defer(cs.aClass.me.aFunction(something))
```

## Example 2

You want to make sure an XML reference will be always properly released, to avoid potential memory leaks:

```4d
var $xmlRef:=DOM Create XML ref("theRoot")
defer(DOM CLOSE XML($xmlRef))
...
```

## Example 3

You want to make sure the activity monitoring stops at the end of the method:

```4d
START MONITORING ACTIVITY(0.001;Activity all)
defer(STOP MONITORING ACTIVITY())
...
```

## Example 4

You want to control your log generation:

```4d
$logRecording:=Get database parameter(Diagnostic log recording)
SET DATABASE PARAMETER(Diagnostic log recording; 1)
defer(SET DATABASE PARAMETER(Diagnostic log recording; $logRecording))

$logLevel:=Get database parameter(Diagnostic log level)
SET DATABASE PARAMETER(Diagnostic log level; Log trace)
defer(SET DATABASE PARAMETER(Diagnostic log level; $logLevel))
```

## See also

[throw](../commands/throw)
[Last errors](../commands/last-errors)
[ON ERR CALL](../commands/on-err-call)

## Properties

| | |
| --- | --- |
| Command number | 1805 |
| Thread safe | no |



9 changes: 5 additions & 4 deletions docs/language-legacy/Interruptions/throw.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ Errors thrown using the **throw** command are managed by the 4D runtime as any n

The command supports three syntaxes:

### **throw(errorCode{; description})**
### throw(errorCode{; description})

It specifies the error code and an optional description text, the error is thrown immediately.
If no description is provided, it is filled with:

* Error code errorCode: (host) in the host application
* Error code errorCode: (C00x) in a component

### **throw(errorObj)**
### throw(errorObj)

*errorObj* object allows for more detailed error information and control over error handling. It can contain the following properties, as well as any custom property that you can refer to using placeholders within the **message** property.

Expand All @@ -61,7 +61,7 @@ When you use this syntax, the *errorObj* object is returned in [Last errors](../

**Note:** It is possible to call the command several times in the same project method to generate several errors. You can use the deferred option to send all errors at once.

### **throw**
### throw

It throws all current errors in **deferred mode**, meaning they will be added to a stack and handled when the calling method returns. This is typically done from within an [ON ERR CALL](../commands/on-err-call) callback.

Expand Down Expand Up @@ -111,7 +111,8 @@ throw({componentSignature: "xbox"; errCode: 600; name: "myFileName"; path: "myFi
## See also

[ASSERT](../commands/assert)
[Last errors](../commands/last-errors)
[defer](../commands/defer)
[Last errors](../commands/last-errors)
[ON ERR CALL](../commands/on-err-call)

## Properties
Expand Down
Loading
Loading