Skip to content
Back to skills

Abl V12 Learning

ASecurity

'"Reference guide for Progress OpenEdge ABL 12.7 (2023) — v10→v12 migration"

  • 4 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added June 12, 2026
devopsgojavac#expressdockergitdatabaseci/cdsecurityperformance

Works with

  • cli

Security analysis

A100/100

Scanned June 12, 2026

npx -y skills add paulpas/agent-skill-router --skill abl-v12-learning --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Abl V12 Learning?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Abl V12 Learning
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/paulpas-abl-v12-learning/badge)](https://www.skillsdirectory.com/skills/paulpas-abl-v12-learning)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---




name: abl-v12-learning
compatibility: opencode
completeness: 95
content-types:
- code
- guidance
- examples
- diagrams
description: '"Reference guide for Progress OpenEdge ABL 12.7 (2023) — v10→v12 migration"
  INT64, ENUM, VAR shorthand, CATCH/THROW/FINALLY, JSON support, generic collections,
  safe navigation operator, server-side joins'
license: MIT
maturity: stable
metadata:
  domain: programming
  output-format: code
  related-skills: abl-v10-learning, algorithms
  role: reference
  scope: implementation
  triggers: abl v12, openedge 12, abl 12.7, v10 to v12 migration, abl migration, catch
    throw, jsonobject, read-json
  archetypes:
  - educational
  anti_triggers:
  - brainstorming
  - vague ideation
  response_profile:
    verbosity: medium
    directive_strength: medium
    abstraction_level: tactical
version: "1.0.0"




---




# Progress OpenEdge ABL 12 — v10 to v12 Migration Reference

> **Version:** Progress OpenEdge 12.7 (May 2023)  
> **Language:** Advanced Business Language (ABL), also known as Progress 4GL  
> **Scope:** v10→v12 migration patterns and new v12 features

---

## TL;DR Checklist

- [ ] **VAR statement** (12.3): `VAR INT x = 0.` replaces verbose `DEFINE VARIABLE` 
- [ ] **Compound operators** (12.3): Use `+=`, `-=`, `*=`, `/=` for cleaner code
- [ ] **Safe navigation** (12.5): `obj?:Property?:Method()` avoids null checks
- [ ] **CATCH/THROW** (12.x): Replace `ON ERROR` with structured exception handling
- [ ] **JSON support** (11.x+): Use `READ-JSON`/`WRITE-JSON` instead of manual parsing
- [ ] **Generic collections** (12.5+): `List<T>`, `SortedSet<T>`, `HashMap<K,V>` replace linked lists
- [ ] **ENUM type** (12.x): Strongly typed named constants for safety
- [ ] **PACKAGE-PRIVATE** (12.2): Package-level encapsulation like Java
- [ ] **Server-side joins** (12.0): `FOR EACH...JOIN` reduces client round-trips
- [ ] **INT64** (10.1B): 64-bit integers for large numeric values

---

## When to Use This Skill

This reference is designed for:

- **Migration engineers** moving legacy ABL v10 codebases to v12
- **ABL developers** learning new v12 language features
- **Code reviewers** evaluating v12 patterns vs. v10 anti-patterns
- **Architects** designing systems with v12 capabilities

---

## When NOT to Use This Skill

- For basic ABL procedural programming — use `programming-abl-v10-learning`
- For UI/widget programming — use `programming-abl-v12-ui`
- For OpenEdge server configuration — consult server documentation

---

## Version History: 12.0 – 12.7

| Version | Release | Key Theme |
|---------|---------|-----------|
| **12.0** | 2019 | Server-side joins, Docker, 64-bit sequences default, STOP catchable by default |
| **12.1** | 2020 | High availability, performance, security enhancements |
| **12.2** | 2020 | `PACKAGE-PRIVATE`/`PACKAGE-PROTECTED` access, AES-GCM encryption, Docker DB |
| **12.3** | 2021 | `VAR` statement, compound operators (`+=`, `-=`), `CHAR`/`INT` full synonyms |
| **12.4** | 2022 | `VAR` with inline `NEW`, expression initialization, `AGGREGATE` statement |
| **12.5** | 2022 | `List<T>`, safe navigation `?:`, override non-abstract properties, `FINAL` on properties |
| **12.6** | 2023 | `SortedSet<T>`, Java 17 support, TLS 1.3, `LeakCheck` diagnostics |
| **12.7** | May 2023 | `HashMap<K,V>`, `HASH-CODE()`, `.NET 6`, signed archive libraries |

---

## Six Major Pillars of Change (v10 → v12)

### 1. Language Ergonomics
The `VAR` shorthand, compound operators (`+=`, `-=`), and `CHAR`/`INT` keyword synonyms reduce boilerplate significantly.

```abl
/* v10 — Verbose */
DEFINE VARIABLE x AS INTEGER INITIAL 0.
DEFINE VARIABLE name AS CHARACTER INITIAL "".
ASSIGN x = x + 1.
ASSIGN name = name + " world".

/* v12 — Concise */
VAR INT x = 0.
VAR CHAR name = "".
x += 1.
name += " world".
```

### 2. OOP Maturity
Package-level access modifiers, property overriding, generics/collections, and the safe navigation operator (`?:`) bring ABL's OOP model closer to Java/C#.

### 3. Error Handling
Structured `CATCH`/`THROW`/`FINALLY` blocks (first available in 10.2B, fully developed by 12.x) replace the legacy `ON ERROR` + `NO-ERROR` model.

### 4. JSON Support
Complete JSON object model (`JsonObject`, `JsonArray`, `ObjectModelParser`) plus `READ-JSON`/`WRITE-JSON` on temp-tables and ProDataSets. JSON was completely absent in v10.

### 5. Generic Collections
Built-in generic collections (`List<T>`, `SortedSet<T>`, `HashMap<TKey,TValue>`) eliminate the need for custom linked-list patterns.

### 6. Infrastructure
64-bit sequences by default, multi-threaded database server, Docker support, OAuth2/SAML, TLS 1.3.

---

## Quick Reference: What's New in v12

### Complete Feature Comparison Table (v10 vs v12)

| Category | v10 | v12 | Status |
|----------|-----|-----|--------|
| **INT64 data type** | ❌ Not available | ✅ `DEFINE VARIABLE x AS INT64` | Added in v10.1B |
| **ENUM type** | ❌ Not available | ✅ `ENUM` statement | New in v12+ |
| **VAR shorthand** | ❌ Must use `DEFINE VARIABLE` | ✅ `VAR INT x = 0.` | New in 12.3 |
| **Compound operators** | ❌ `x = x + 1` | ✅ `x += 1` | New in 12.3 |
| **PACKAGE-PRIVATE access** | ❌ Not available | ✅ `PACKAGE-PRIVATE` / `PACKAGE-PROTECTED` | New in 12.2 |
| **Override non-abstract properties** | ❌ Only abstract | ✅ Any non-final property | New in 12.5 |
| **Safe navigation `?:`** | ❌ Must use VALID-OBJECT checks | ✅ `obj?:Property?:Method()` | New in 12.5 |
| **Generic collections** | ❌ Not available | ✅ `List<T>`, `SortedSet<T>`, `HashMap<K,V>` | 12.5–12.7 |
| **CATCH / THROW / FINALLY** | ❌ Not in v10 (added 10.2B) | ✅ Full structured exception handling | Fully mature in v12 |
| **BLOCK-LEVEL ON ERROR UNDO, THROW** | ❌ Not available | ✅ Single directive covers all blocks | New post-v10 |
| **JSON support** | ❌ None | ✅ `JsonObject`, `JsonArray`, `READ-JSON`, `WRITE-JSON` | New in 11.x |
| **SAX callbacks via OO class** | ❌ Procedure-only | ✅ `EVENT-HANDLER-OBJECT` | Improved in v12 |
| **DYNAMIC-INVOKE()** | ⚠️ Limited | ✅ Full class-method dynamic invocation | Improved in v12 |
| **SERIALIZE-ROW() for JSON** | ❌ XML only | ✅ JSON + XML targets | New in v12 |
| **STOP as catchable object** | ⚠️ Required `-catchStop` flag | ✅ Default behavior — CATCH/FINALLY fire | Changed in 12.0 |
| **64-bit sequences** | ⚠️ Optional | ✅ Default for new databases | Changed in 12.0 |
| **Methods as async callbacks** | ❌ Procedures only | ✅ Class methods as `EVENT-HANDLER` | New in 12.3 |
| **Server-side joins** | ❌ Not available | ✅ FOR+NO-LOCK, up to 10 tables | New in 12.0 |
| **`HASH-CODE` function** | ❌ Not available | ✅ `HASH-CODE(arg1, arg2)` | New in 12.7 |
| **`FINAL` on properties** | ❌ Not available | ✅ Prevent subclass override | New in 12.5 |
| **`AGGREGATE` statement** | ❌ Not available | ✅ Server-side aggregation | New in 12.4 |

---

## Version-by-Version Feature Table (v10.0B through v12.7)

| Feature | Introduced | Category |
|---------|-----------|----------|
| `DATETIME` data type | v10.0B | Data Types |
| `DATETIME-TZ` data type | v10.0B | Data Types |
| `BLOB` / `CLOB` field types | v10.0B | Data Types |
| `LONGCHAR` data type | v10.0B | Data Types |
| `INT64` data type | v10.1B | Data Types |
| `CLASS` / `INTERFACE` OOP | v10 | OOP |
| `CATCH` / `THROW` / `FINALLY` | 10.2B | Error Handling |
| JSON (`JsonObject`, `JsonArray`, `READ-JSON`, `WRITE-JSON`) | 11.x | JSON |
| `Progress.IO.MemoryOutputStream` / `MemoryInputStream` | **12.0** | I/O |
| `STOP` catchable by default (no `-catchStop` needed) | **12.0** | Error Handling |
| Indeterminate array resize without data loss | **12.0** | Language |
| 64-bit sequences enabled by default (new DBs) | **12.0** | Database |
| Server-side joins (FOR + NO-LOCK, up to 10 tables) | **12.0** | Database |
| `PACKAGE-PRIVATE` access modifier | **12.2** | OOP |
| `PACKAGE-PROTECTED` access modifier | **12.2** | OOP |
| Methods as socket / SAX callbacks | **12.2** | OOP |
| `require-return-values` compiler option | **12.2** | Compiler |
| AES-GCM encryption in ABL | **12.2** | Security |
| OpenEdge Application Archive (`.oear`) | **12.2** | CI/CD |
| `VAR` statement (shorthand `DEFINE VARIABLE`) | **12.3** | Language |
| `CHAR` and `INT` as full type keywords | **12.3** | Language |
| Compound operators (`+=`, `-=`, `*=`, `/=`) | **12.3** | Language |
| Runtime resize of initialized indeterminate arrays | **12.3** | Language |
| Methods as `RUN ... ASYNC` callbacks | **12.3** | OOP |
| Methods as SAX-READER callbacks | **12.3** | XML |
| `VAR` with expressions and function calls | **12.4** | Language |
| `VAR` with inline `NEW` | **12.4** | Language |
| `AGGREGATE` statement (server-side aggregation) | **12.4** | Database |
| Override non-abstract properties | **12.5** | OOP |
| `FINAL` on properties | **12.5** | OOP |
| Safe navigation operator `?:` | **12.5** | Language |
| `Progress.Collections.List<T>` | **12.5** | Collections |
| Generic type syntax `<T>` | **12.5** | Language |
| `Progress.Collections.SortedSet<T>` | **12.6** | Collections |
| `OpenEdge.Core.Util.LeakCheck` class | **12.6** | Diagnostics |
| Java 17 support | **12.6** | Platform |
| `Progress.Collections.HashMap<TKey, TValue>` | **12.7** | Collections |
| `HASH-CODE` function | **12.7** | Language |
| Signed Archive Libraries (`.apl`) | **12.7** | Deployment |
| `.NET 6` support on Windows | **12.7** | Platform |
| `ABL Type Hierarchy View` in Developer Studio | **12.7** | Tooling |

---

## Data Types

### Full Data Type Table (v10 vs v12)

| Data Type | v10 | v12 | Notes |
|-----------|:---:|:---:|-------|
| `CHARACTER` | ✅ | ✅ | Max 32,767 bytes. Default `""`. Keyword alias `CHAR` (v12.3+). |
| `LONGCHAR` | ✅ | ✅ | Up to 2 GB − 1 byte. Code-page aware. |
| `INTEGER` | ✅ | ✅ | 32-bit signed: −2,147,483,648 to +2,147,483,647. Default `0`. Keyword alias `INT` (v12.3+). |
| `INT64` | ❌ | ✅ | 64-bit signed: −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807. Default `0`. **Added v10.1B.** |
| `DECIMAL` | ✅ | ✅ | Up to 50 significant digits, up to 10 decimal places. Default `0`. |
| `LOGICAL` | ✅ | ✅ | TRUE/FALSE/YES/NO/Unknown(?). Default `NO`. |
| `DATE` | ✅ | ✅ | 1/1/32768 BC to 12/31/32767 AD. Default `?`. |
| `DATETIME` | ✅ | ✅ | DATE + milliseconds from midnight. Local time. Default `?`. **Added v10.0B.** |
| `DATETIME-TZ` | ✅ | ✅ | DATETIME + UTC offset. Stored in UTC. Default `?`. **Added v10.0B.** |
| `RAW` | ✅ | ✅ | Binary, ≤ 32K. |
| `MEMPTR` | ✅ | ✅ | In-memory binary, up to 2 GB − 1 byte. |
| `ROWID` | ✅ | ✅ | Unique database record identifier. Preferred over RECID. |
| `RECID` | ✅ | ✅ | Legacy 32-bit record ID. Use ROWID instead. |
| `HANDLE` | ✅ | ✅ | Pointer to an ABL object/widget. Interchangeable with WIDGET-HANDLE. |
| `WIDGET-HANDLE` | ✅ | ✅ | Alias for HANDLE. |
| `COM-HANDLE` | ✅ | ✅ | ActiveX/COM object reference. |
| `BLOB` | ✅ | ✅ | Binary large object. DB/temp-table fields only. Up to 1 GB. **Added v10.0B.** |
| `CLOB` | ✅ | ✅ | Character large object. DB/temp-table fields only. Up to 1 GB. **Added v10.0B.** |
| `CLASS` | ✅ | ✅ | User-defined class object reference. Default `?`. |
| `ENUM` | ❌ | ✅ | Strongly typed named constants. Implicitly class-based. **New in v12.** |

### INT64 — 64-Bit Integer (Added v10.1B)

```abl
DEFINE VARIABLE id AS INT64 INITIAL 0.

/* Large values that overflow INTEGER */
id = 9000000000000000000.

/* Use in database fields for large sequences, snowflake IDs, etc. */
TABLE-SCHEMA myTable:
    DEFINE COLUMN myId AS INT64.
END TABLE-SCHEMA.
```

### ENUM Type (New in v12)

The ENUM type provides strongly typed named constants with implicit class backing.

```abl
/* Define an enum type */
ENUM OrderStatus:
    PENDING.
    PROCESSING.
    SHIPPED.
    DELIVERED.
    CANCELLED.
END ENUM.

/* Use the enum */
VAR OrderStatus status = OrderStatus:PENDING.

/* Switch is type-safe */
SWITCH status:
    CASE OrderStatus:PENDING:
        DISPLAY "Waiting for processing".
    CASE OrderStatus:SHIPPED:
        DISPLAY "Out for delivery".
    DEFAULT:
        DISPLAY "Unknown status".
END SWITCH.

/* Iteration support */
FOREACH s IN OrderStatus:
    DISPLAY s / ENUM-NAME(s).
END.
```

---

## Variable Declaration

### The VAR Statement (New in 12.3)

The `VAR` statement is a concise alternative to `DEFINE VARIABLE`.

**Basic syntax:**
```abl
VAR TYPE name [AS subtype] [= expression].
```

**Examples:**
```abl
/* Basic declarations */
VAR INT count = 0.
VAR CHAR name = "".
VAR DECIMAL price = 99.99.
VAR LOGICAL isActive = TRUE.

/* With expressions */
VAR INT sum = 1 + 2 + 3.
VAR DATE today = TODAY.
VAR INTEGER length = LENGTH("hello").

/* With function calls */
VAR CHAR json = GET-TEMP-JSON().
VAR INTEGER count = GET-RECORD-COUNT().

/* With inline NEW (12.4+) */
VAR Customer customer = NEW Customer().
VAR JsonObject payload = NEW JsonObject().
VAR Progress.Collections.List<STRING> names = NEW Progress.Collections.List<STRING>().

/* Arrays */
VAR INT scores[5].
VAR CHAR items[?].

/* Multiple declarations */
VAR INT x = 0, y = 0, z = 0.
```

**MUST NOT:**
- Use `VAR` for database fields (only variables)
- Use `VAR` for temp-table definitions (use `DEFINE TEMP-TABLE`)
- Use `VAR` to shadow class properties or method parameters

### CHAR and INT as Type Keywords (12.3+)

`CHAR` is now a full synonym for `CHARACTER`. `INT` is now a full synonym for `INTEGER`.

```abl
/* v10 */
DEFINE VARIABLE name AS CHARACTER.
DEFINE VARIABLE count AS INTEGER.

/* v12 */
VAR CHARACTER name.
VAR INT count.

/* Also valid (12.3+) */
VAR CHAR name.
VAR INT count.
```

### Compound Operators (12.3+)

Compound assignment operators reduce boilerplate for common operations.

```abl
VAR INT x = 0.
VAR CHAR text = "Hello".
VAR DECIMAL total = 100.00.

x += 1.      /* Same as: x = x + 1 */
x -= 5.      /* Same as: x = x - 5 */
x *= 2.      /* Same as: x = x * 2 */
x /= 3.      /* Same as: x = x / 3 */

text += " World".    /* Same as: text = text + " World" */

total += 50.00.      /* Same as: total = total + 50.00 */
```

---

## Error Handling (v12 Model)

### CATCH / THROW / FINALLY

**v10 legacy pattern (anti-pattern):**
```abl
/* v10 — Fragile, hard to trace */
ON ERROR UNDO, ERROR-HANDLER.

PROCESS-ORDER(orderNum IN):
    /* ... logic ... */
    IF NOT AVAILABLE order THEN
        THROW ERROR "Order not found".
    /* ... more logic ... */
END.

ERROR-HANDLER:
    /* Single handler for everything */
    DISPLAY ERROR-TEXT.
    RETURN.
```

**v12 structured pattern:**
```abl
/* v12 — Structured, explicit, testable */
PROCESS-ORDER(orderNum IN):
    ON ERROR UNDO, THROW.
    
    TRY
        VAR Order order = GET-ORDER(orderNum).
        
        IF order = ? THEN
            THROW NEW OrderNotFoundException("Order not found", orderNum).
            
        UPDATE-ORDER(order).
        COMMIT.
        
    CATCH OrderNotFoundException AS e:
        LOGGER:WARN("Order {1} not found", orderNum).
        RETURN FALSE.
        
    CATCH Progress.Lang.Exception AS e:
        LOGGER:ERROR("Error processing order {1}: {2}", orderNum, e:Message).
        RETURN FALSE.
        
    FINALLY:
        /* Always runs — cleanup here */
        RELEASE-LOCK(orderNum).
    END.
    
    RETURN TRUE.
END.
```

**Key principles:**
1. Use `ON ERROR UNDO, THROW` at procedure/block level
2. Use `TRY/CATCH/FINALLY` for structured exception handling
3. Define custom exception classes for domain errors
4. Catch specific exceptions before general ones
5. Use `FINALLY` for cleanup code that must always run

### Custom Exception Classes

```abl
/* Define a custom exception hierarchy */
CLASS acme.OrderException INHERITS Progress.Lang.Exception:
    CONSTRUCTOR PUBLIC OrderException(message AS STRING):
        SUPER(message).
    END CONSTRUCTOR.
END CLASS.

CLASS acme.OrderNotFoundException INHERITS acme.OrderException:
    DEFINE PRIVATE PROPERTY _orderId AS INTEGER.
    
    CONSTRUCTOR PUBLIC OrderNotFoundException(message AS STRING, orderId AS INTEGER):
        SUPER(message).
        _orderId = orderId.
    END CONSTRUCTOR.
    
    DEFINE PUBLIC PROPERTY OrderId AS INTEGER GET ONLY:
        GET():
            RETURN _orderId.
        END GET.
    END PROPERTY.
END CLASS.

/* Usage */
THROW NEW acme.OrderNotFoundException("Order not found", orderId).
```

---

## JSON Support (New in 11.x, Enhanced in 12.x)

### Reading JSON

```abl
/* Parse JSON string to JsonObject */
VAR CHAR jsonStr = ""{\"name\":\"John\",\"age\":30}\"".
VAR JsonObject obj = NEW JsonObject():LOAD-JSON(jsonStr).

/* Access properties */
DISPLAY obj:GetValue("name") AS CHARACTER.
DISPLAY obj:GetValue("age") AS INTEGER.

/* Read nested object */
VAR JsonObject address = obj:GetObject("address").
DISPLAY address:GetValue("city") AS CHARACTER.
```

### Writing JSON

```abl
/* Build JsonObject programmatically */
VAR JsonObject user = NEW JsonObject().
user:Put("name", "John").
user:Put("age", 30).
user:Put("active", TRUE).

/* Convert back to string */
VAR CHAR jsonStr = user:GET-JSON().
DISPLAY jsonStr.
/* Output: {"age":30,"name":"John","active":true} */
```

### READ-JSON / WRITE-JSON (11.x+)

```abl
DEFINE TEMP-TABLE ttUser
    FIELD id AS INTEGER
    FIELD name AS CHARACTER
    FIELD email AS CHARACTER
    FIELD active AS LOGICAL.

/* Populate temp-table */
CREATE ttUser.
ASSIGN ttUser/id = 1
       ttUser/name = "John"
       ttUser/email = "john@example.com"
       ttUser/active = TRUE.

/* Write temp-table to JSON */
VAR CHAR userJson = "".
WRITE-JSON(ttUser, userJson).

/* Read JSON back into temp-table */
DEFINE TEMP-TABLE ttUser2
    FIELD id AS INTEGER
    FIELD name AS CHARACTER
    FIELD email AS CHARACTER  
    FIELD active AS LOGICAL.

READ-JSON(ttUser2, userJson).
DISPLAY ttUser2/name.
```

### JsonArray

```abl
VAR JsonArray arr = NEW JsonArray().
arr:Add("Apple").
arr:Add("Banana").
arr:Add("Cherry").

/* Access by index */
DISPLAY arr:GetValue(0) AS CHARACTER.  /* Apple */

/* Iterate */
FOREACH i FROM 1 TO arr:Count:
    DISPLAY arr:GetValue(i - 1) AS CHARACTER.
END.

/* Convert to JSON string */
VAR CHAR jsonArr = arr:GET-JSON().
```

---

## Generic Collections (12.5+)

### List<T>

```abl
VAR Progress.Collections.List<STRING> names = NEW Progress.Collections.List<STRING>().

names:Add("Alice").
names:Add("Bob").
names:Add("Charlie").

/* Access by index (0-based) */
DISPLAY names:Get(0) AS CHARACTER.  /* Alice */

/* Modify */
names:Set(1, "Robert").

/* Remove */
names:Remove("Charlie").

/* Count */
DISPLAY names:Count.  /* 2 */

/* Contains check */
IF names:Contains("Alice") THEN
    DISPLAY "Found Alice".
    
/* Clear */
names:Clear().
```

### SortedSet<T>

```abl
VAR Progress.Collections.SortedSet<STRING> uniqueNames = NEW Progress.Collections.SortedSet<STRING>().

uniqueNames:Add("Charlie").
uniqueNames:Add("Alice").
uniqueNames:Add("Bob").
/* Adding duplicate returns FALSE */
uniqueNames:Add("Alice").  /* Returns FALSE, not added */

/* Automatically sorted */
DISPLAY uniqueNames:First() AS CHARACTER.  /* Alice */
DISPLAY uniqueNames:Last() AS CHARACTER.   /* Charlie */
```

### HashMap<TKey, TValue>

```abl
VAR Progress.Collections.HashMap<STRING, INTEGER> ages = NEW Progress.Collections.HashMap<STRING, INTEGER>().

ages:Put("Alice", 30).
ages:Put("Bob", 25).
ages:Put("Charlie", 35).

/* Get value */
VAR INT aliceAge = ages:Get("Alice").
DISPLAY aliceAge.  /* 30 */

/* Check if key exists */
IF ages:ContainsKey("Bob") THEN
    DISPLAY "Bob is in the map".

/* Remove entry */
ages:Remove("Charlie").

/* Count */
DISPLAY ages:Count.  /* 2 */
```

### HASH-CODE Function (12.7+)

```abl
/* Generate hash code for objects or values */
VAR INT hash1 = HASH-CODE("hello").
VAR INT hash2 = HASH-CODE("hello").
DISPLAY hash1 = hash2.  /* TRUE - same input produces same hash */

/* Multi-argument hash */
VAR INT combined = HASH-CODE("firstName", "lastName", 12345).

/* Useful for custom Equals implementations and hash-based collections */
```

---

## OOP Features

### PACKAGE-PRIVATE / PACKAGE-PROTECTED Access (12.2+)

Package-level access modifiers allow sharing between classes in the same namespace.

```abl
/* File: acme/Base.cls */
CLASS acme.Base:
    DEFINE PRIVATE PROPERTY _private AS STRING.
    DEFINE PACKAGE-PRIVATE PROPERTY _packageLevel AS STRING.
    DEFINE PACKAGE-PROTECTED PROPERTY _protected AS STRING.
    DEFINE PUBLIC PROPERTY Public AS STRING.
END CLASS.

/* File: acme/Sibling.cls - Same package */
CLASS acme.Sibling:
    METHOD PUBLIC VOID AccessBase(base AS acme.Base):
        /* base:_private      - ERROR: cannot access */
        base:_packageLevel = "shared".    /* OK: same package */
        base:_protected = "inherited".    /* OK: package-protected */
    END METHOD.
END CLASS.

/* File: other/Outside.cls - Different package */
CLASS other.Outside:
    METHOD PUBLIC VOID AccessBase(base AS acme.Base):
        /* base:_private      - ERROR: cannot access */
        /* base:_packageLevel - ERROR: different package */
        /* base:_protected    - ERROR: different package */
        base:Public = "public".           /* OK: public */
    END METHOD.
END CLASS.
```

### Safe Navigation Operator `?:` (12.5+)

Eliminates verbose null checks when accessing nested properties.

```abl
/* v10/v11 — Verbose null checking */
VAR Customer cust = GET-CUSTOMER(id).
IF cust <> ? THEN
    IF cust:Address <> ? THEN
        IF cust:Address:City <> ? THEN
            DISPLAY cust:Address:City:Name.
        END.
    END.
END.

/* v12.5+ — Concise safe navigation */
VAR CHAR cityName = GET-CUSTOMER(id)?:Address?:City?:Name ?? "Unknown".
DISPLAY cityName.
```

### Override Non-Abstract Properties (12.5+)

Prior to 12.5, only abstract properties could be overridden. Now any non-final property can be overridden.

```abl
/* Base class with concrete property */
CLASS acme.Base:
    DEFINE PUBLIC PROPERTY Value AS STRING GET. SET.
END CLASS.

/* Override with coded getter/setter (12.5+) */
CLASS acme.Derived INHERITS acme.Base:
    DEFINE PRIVATE PROPERTY _value AS STRING.
    
    DEFINE PUBLIC PROPERTY OVERRIDE Value AS STRING GET. SET.
    END PROPERTY.
    
    GET():
        RETURN UPPER(_value).
    END GET.
    
    SET(newValue AS STRING):
        _value = LOWER(newValue).
    END SET.
    
END PROPERTY.
END CLASS.
```

### FINAL Properties (12.5+)

Prevent property overriding in subclasses.

```abl
CLASS acme.FinalBase:
    DEFINE FINAL PUBLIC PROPERTY Immutable AS STRING GET. SET.
END CLASS.

CLASS acme.FinalDerived INHERITS acme.FinalBase:
    /* This would cause a compile error:
    DEFINE PUBLIC PROPERTY OVERRIDE Immutable AS STRING... 
    */
END CLASS.
```

---

## Server-Side Joins (12.0+)

### Basic Server-Side Join

```abl
/* v10 — Client-side join (slow) */
FOR EACH Customer NO-LOCK:
    FOR EACH Order WHERE Order.CustId = Customer.Id NO-LOCK:
        /* Process each combination */
    END.
END.

/* v12 — Server-side join (fast) */
FOR EACH Customer NO-LOCK
    JOIN Order WHERE Order.CustId = Customer.Id NO-LOCK:
        /* Database performs the join */
    END.
END.
```

### Multiple Table Joins

```abl
/* Server-side join up to 10 tables */
FOR EACH Customer NO-LOCK
    JOIN Order WHERE Order.CustId = Customer.Id NO-LOCK
    JOIN OrderItem WHERE OrderItem.OrderId = Order.Id NO-LOCK
    JOIN Product WHERE Product.Id = OrderItem.ProductId NO-LOCK:
        
    DISPLAY Customer.Name,
            Order.OrderDate,
            OrderItem.Quantity,
            Product.Name,
            OrderItem.UnitPrice * OrderItem.Quantity AS Total.
END.
```

---

## Migration Checklist (v10 → v12)

### Language Modernization

- [ ] Replace `DEFINE VARIABLE x AS INTEGER` with `VAR INT x`
- [ ] Replace `DEFINE VARIABLE x AS CHARACTER` with `VAR CHAR x`
- [ ] Use compound operators: `x += 1` instead of `x = x + 1`
- [ ] Use `CHAR`/`INT` keywords instead of `CHARACTER`/`INTEGER` where appropriate

### Error Handling Migration

- [ ] Replace `ON ERROR` blocks with `TRY/CATCH/FINALLY`
- [ ] Create custom exception classes for domain errors
- [ ] Remove `NO-ERROR` flags; use structured error handling
- [ ] Add `FINALLY` blocks for cleanup code

### JSON Migration

- [ ] Replace manual JSON string parsing with `JsonObject:LOAD-JSON()`
- [ ] Use `READ-JSON`/`WRITE-JSON` for temp-table serialization
- [ ] Replace `SERIALIZE-ROW(..., "XML")` with `SERIALIZE-ROW(..., "JSON")`

### Collections Migration

- [ ] Replace linked list patterns with `List<T>`
- [ ] Replace sorted array patterns with `SortedSet<T>`
- [ ] Replace custom hash maps with `HashMap<TKey, TValue>`
- [ ] Use `HASH-CODE()` for custom object hashing

### OOP Improvements

- [ ] Replace `VALID-OBJECT()` chains with safe navigation `?:`
- [ ] Use `PACKAGE-PRIVATE` for package-level encapsulation
- [ ] Consider `FINAL` on properties that should not be overridden
- [ ] Override non-abstract properties where needed (12.5+)

### Database Improvements

- [ ] Convert client-side nested FOR EACH loops to server-side JOINs
- [ ] Review sequence fields for INT64 compatibility
- [ ] Consider `AGGREGATE` statement for server-side aggregations

---

## Related Skills

| Skill | Purpose |
|-------|--------|
| `programming-abl-v10-learning` | Original ABL v10 procedural programming reference |
| `programming-abl-v12-ui` | UI/widget programming in ABL 12 |

---

## References

- [Progress OpenEdge 12.7 Documentation](https://documentation.progress.com/output/oberon/enusoe1270index/)
- [ABL Language Reference](https://documentation.progress.com/output/oberon/enusoe1270lang/)
- [OpenEdge Developer Studio](https://documentation.progress.com/output/oberon/enusoe1270dds/)

---

*Compiled from official Progress OpenEdge 12.7 documentation. Last updated: May 2023*
---

## When to Use

Use this skill when:

- **Implementing or analyzing algorithms** — You need to understand, implement, or optimize algorithms for a specific problem domain
- **Comparing algorithmic approaches** — You are evaluating different algorithms for correctness, performance, or trade-offs
- **Studying computational patterns** — You need reference implementations and complexity analysis for standard algorithmic patterns
---

## Core Workflow

1. **Understand the Problem** — Identify inputs, outputs, constraints, and edge cases. **Checkpoint:** Clearly define the problem statement and expected behavior.

2. **Select Algorithm** — Choose the most appropriate algorithm based on constraints (time/space complexity, data size, ordering requirements). **Checkpoint:** Justify the algorithm choice with complexity analysis.

3. **Implement & Test** — Write clean, readable code with type hints, docstrings, and edge case handling. **Checkpoint:** Verify correctness with multiple test cases including edge cases.

4. **Analyze & Optimize** — Review time/space complexity, identify optimization opportunities, and validate against benchmarks. **Checkpoint:** Confirm complexity bounds match analysis and all test cases pass.

---

## Constraints

### MUST DO
- Include time and space complexity analysis for all algorithms
- Show at least one working example with input/output
- Include diagrams for non-trivial algorithm flows

### MUST NOT DO
- Present an algorithm without its complexity bounds
- Use recursive solutions without discussing tail-call or memoization alternatives
- Omit edge case handling in examples

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…