SAS (9.4 / Viya)

This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].

1 Assessment

This page assesses SAS in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.

Core coupling 51/100 · Ecosystem coupling 54/100 · Engineering readiness 34/100

1.1 Core coupling

1.1.1 Type

  • ❌ Dynamic typing with no static check (T1): A variable takes its type when the DATA step first builds the row, so a mismatch appears at run time
  • ⚠️ Two types exist and a character length is fixed at first use: A variable is numeric or character, and a later longer value is truncated to the length already set
  • ⚠️ The dataset carries the schema, not a declaration: A variable list comes from the dataset descriptor, so a variable’s type is read from data rather than from source
  • ✅ LENGTH and ATTRIB declare a variable explicitly: A length, a type, and a format can be fixed before the data arrives, which decouples the variable from the value that first creates it
  • ✅ FORMAT and INFORMAT separate presentation from storage: A date is a number with a DATE9. format, so a reader sees a date and the value stays numeric
  • ❌ There is no type beyond a column in a dataset: SAS has no user-defined type, generic, or interface, so a richer value is encoded in columns

1.1.2 Construction

  • ✅ The DATA step separates data handling from analysis: A DATA step reads, merges, filters, and derives; a PROC step analyses and reports, so the two are distinct statement kinds
  • ✅ A PROC step names its analysis and takes a dataset: PROC MEANS data=work.sales; performs one analysis on one input, so the analysis is not tangled with retrieval
  • ✅ SET, MERGE, and BY compose datasets declaratively: A join is declared with a BY key, so the key is the join condition rather than a hand-coded loop
  • ❌ No immutability by default (C2): A DATA step can overwrite the dataset it reads (data work.x; set work.x;), and every variable is reassignable
  • ❌ A macro is the only module mechanism: %MACRO is text substitution, so an expansion depends on the macro symbol table and on the place of the invocation
  • ⚠️ The DATA step’s implicit loop carries state across rows: RETAIN and the automatic _N_ follow the row stream, so row order becomes part of the result

1.1.3 Memory

  • ✅ No manual memory management: The SAS supervisor allocates and frees the buffers of a step, and the program never names an address
  • ✅ The program-data vector bounds per-row state: A step holds one row’s variables in a fixed vector, so the program does not size buffers
  • ✅ The resource envelope is a configuration choice: SORTSIZE, MEMSIZE (on the hosts that provide it), and the Viya CAS memory settings are set outside the program
  • ⚠️ A large sort spills to the work library: A sort or a hash join that exceeds its memory writes to the utility library, so the temporary footprint follows the data volume
  • ⚠️ A macro variable persists for the whole session: A %LET value stays until it is reassigned, so it is shared state that outlives the step that set it

1.1.4 Error

  • ✅ OPTIONS ERRORABEND stops the session on an error: An error ends the run instead of continuing, which turns a silent failure into a visible one
  • ✅ A macro-level guard can abort the run: %IF &SYSERR %THEN %ABORT reads the error status and stops the batch
  • ❌ No structured error path (Er1): SAS has no exception construct, so a DATA step records an error, writes a note to the log, and by default continues to the next observation
  • ⚠️ An error is reported in the log, not to the caller: A step sets &SYSERR and the automatic _ERROR_ flag, so detection is by log inspection or a macro check
  • ⚠️ Error checking is opt-in per step: The default continues on many data errors, so a batch run can finish with a wrong dataset

1.1.5 Concurrency

  • ✅ Procedures can use several threads: OPTIONS THREADS lets PROC SORT, PROC MEANS, and PROC SQL use more than one processor
  • ✅ SAS/CONNECT and MP Connect distribute work across sessions: A job can be split over several SAS sessions on one host or across hosts
  • ✅ A dataset is locked per member: Two writers do not share a row buffer, so member-level locking prevents a concurrent overwrite in one library
  • ⚠️ The language offers no lock or atomic primitive: Coordination is the platform’s library locking and the scheduler’s, not a construct in the DATA step
  • ⚠️ The macro symbol table is shared session state: One step’s %LET or CALL SYMPUT is visible to every later step in the session

1.1.6 Platform

  • ❌ Cannot function outside one vendor (P1): The language needs a SAS installation, and a program’s unit of deployment is a SAS library on a SAS host
  • ❌ SAS datasets are the interchange unit: The persistence format is a proprietary member of a SAS library
  • ⚠️ Viya is a second runtime under the same vendor: Viya runs the language on the CAS engine with cloud deployment, but it is the same vendor’s platform, not an independent implementation
  • ✅ A dataset can be written to the documented XPORT transport form: The XPORT engine with PROC COPY produces the openly documented transport format that the FDA requires for electronic submissions, and other tools can read it
  • ⚠️ Open-source interop exists from inside the platform: PROC PYTHON and PROC R call R and Python, so part of a workflow can use an open tool

1.1.7 Trade-offs (not coupling)

  • The DATA-step/PROC-step split is a deliberate separation of concerns: Retrieval and analysis are distinct statement kinds by design
  • The dataset is the only data abstraction: One table shape for all data keeps the language uniform, at the cost of encoding a richer value in columns
  • A macro is a text-substitution preprocessor: It keeps a repeated block in one place, at the cost of visibility at expansion time
  • Point-and-click Enterprise Guide serves a non-programmer task: Hiding the language suits a code-free workflow, so the GUI’s coupling to the engine is the product’s purpose
  • A proprietary platform is traded for a regulated-industry toolchain: One vendor with documented releases and validation packages suits a regulated deliverable
  • Viya chose coexistence over replacement: Modernising the architecture while keeping SAS 9.4 running is a compatibility decision

1.2 Ecosystem coupling

1.2.1 Performance

  • ✅ The DATA step and the procedures run compiled code: A step is compiled to a program-data-vector loop executed by the supervisor
  • ✅ Multithreading and the CAS engine parallelise heavy work: Threaded procedures and distributed in-memory execution in Viya carry the large jobs
  • ✅ An index or the SPD engine avoids a full scan: A lookup by an indexed key or by the multi-threaded SPD engine reads fewer pages than a sequential pass
  • ⚠️ A sort can dominate a job’s time and space: A sort that exceeds memory spills to the work library, so a job of a given size needs a matching utility allocation
  • ⚠️ A BY-group analysis requires sorted input: Many procedures need the data sorted by the BY key, which makes a sort a precondition of the analysis

1.2.2 Ecosystem

  • ✅ The library is large and mature inside its industry: SAS ships statistical, econometric, clinical, and operations-research modules with long support windows
  • ✅ Regulated statistical practice has decades of SAS convention: Clinical and regulated reporting follows CDISC standards and platform validation practice
  • ✅ Viya adds open-source tooling: Jupyter, Python, R, and REST interfaces connect to a Viya deployment
  • ❌ Proprietary (Ec4): The product is licensed by subscription, and its modules are licensed separately
  • ❌ Small, niche, and increasingly legacy (Ec3): The language’s own market has shrunk against R and Python, so new analysts and new packages arrive more slowly
  • ⚠️ The ecosystem is closed: A shared contribution is an in-house macro or user-defined format, not a package drawn from an open index

1.2.3 Evolution

  • ✅ Long-term stability is a stated commitment: A program written for an earlier release of the same generation still runs on a later one
  • ✅ The vendor carries validated code forward: Existing 9.4 language support continues in Viya, so a submission program is not stranded
  • ⚠️ The language has evolved slowly: SAS 9.4 has been the reference release since 2013, and the DATA-step and PROC syntax is largely unchanged
  • ⚠️ The modern architecture is a second platform: Viya changes deployment, engines, and administration, so a move to it is a platform migration rather than a language upgrade
  • ⚠️ Evolution follows the vendor’s roadmap: A new feature appears at the vendor’s next release, not at a date the community fixes

1.2.4 Domain

  • ✅ Excellent fit for regulated statistical reporting: A DATA step plus a PROC produce a documented, reproducible table
  • ✅ Clinical and financial workflows have a fixed vocabulary: PROC LIFETEST, PROC PHREG, PROC FREQ, and PROC LOGISTIC name the standard analyses directly
  • ❌ Domain and vendor lock-in (D1): The language is built for statistical analysis on a SAS platform, so both the domain and the vendor are fixed
  • ⚠️ The regulatory transport requirement has narrowed: The FDA still requires the open SAS V5 XPORT transport format for datasets, but CDISC standards define their content and other tools can write the format, so the language’s old regulatory moat has shrunk
  • ⚠️ General-purpose programming sits outside the design centre: Text processing, user interfaces, and systems work are not the intended use
  • ⚠️ Analysts increasingly use R and Python beside it: An open-source language is cheaper per seat and easier to hire for

1.2.5 Learning

  • ✅ The DATA-step/PROC-step division is teachable: A beginner learns retrieval in a DATA step and analysis in a PROC, and the two do not mix
  • ✅ Documentation and training are complete and vendor-provided: SAS documentation, courses, and certification cover every module
  • ⚠️ Steep (L1): The DATA step’s implicit loop, the program-data vector, and the two-match step of MERGE are unlike any other language’s execution model
  • ⚠️ DATA-step syntax is idiosyncratic: Statements are KEYWORD variable=value; lines whose effect depends on their order in the step
  • ⚠️ Macro programming is a second language on top: %MACRO, &var, %eval, and the quoting functions (%str, %bquote) have their own rules

1.2.6 Syntax

  • ✅ Statements are keyword-led and read as English: PROC SORT data=a; by x; run; states the operation and the key
  • ✅ A PROC block is small per analysis: An analysis reads as PROC name data=...; plus a few statements
  • ❌ Verbose (S1): A typical step is a list of KEYWORD option; lines, and necessary statements such as LENGTH, FORMAT, and LABEL add lines before any data is touched
  • ⚠️ Every step must be closed by RUN or QUIT: A missing RUN; means the step does not execute, and a missing QUIT leaves a procedure open
  • ⚠️ The semicolon and the % sigil each carry meaning: A missing semicolon changes the statement, and a macro reference without % or & is treated as text
  • ⚠️ Formats and informats add a name to every value: date9., $char20., and best12. decorate a declaration, so a reader learns a second vocabulary

1.2.7 Testing

  • ✅ PROC COMPARE reports differences by variable and observation: A regression test is a dataset comparison with a named result
  • ✅ A check step can assert its own expectation: A step can read the result and %ABORT on a wrong value, and OPTIONS ERRORABEND turns a data error into a stop
  • ✅ The log is the primary diagnostic instrument: With MPRINT and MLOGIC it names the step and the macro line that failed
  • ⚠️ No test framework ships with the language: A test is a SAS program, so the harness is a script or a scheduler around it
  • ⚠️ Errors are runtime-only: A wrong type or a missing file appears when the step runs, not before
  • ⚠️ A passing run can still contain errors: The default continues after many errors, so a clean exit code does not imply a clean run

1.2.8 Security

  • ✅ No pointers and no manual memory access: The language offers no address arithmetic, so the memory-corruption class does not apply
  • ✅ Access control is a platform feature: Library and table permissions, metadata-server roles, and Viya identities control who reads a dataset
  • ✅ Viya records an audit trail: A deployment logs who ran which action against which resource
  • ⚠️ A macro variable can build a statement: &SYSFUNC and a resolved macro variable are expanded into syntax, so a value that carries user input can become code
  • ⚠️ Encryption and transport security are configuration choices: A library can be encrypted and a connection secured, but the default is the site’s decision
  • ⚠️ A dataset member is a binary file with no per-row rule: A leaked member is a full data dump, so row-level restriction must be built above the format

1.3 Engineering readiness

1.3.1 Reproducibility

  • ❌ Builds are not byte-reproducible by default.
  • ❌ Dependencies resolve without a lock by default.

1.3.2 Supply chain

  • ⚠️ Published packages carry no signed provenance by default.

1.3.3 Observability

  • ⚠️ No stable tracing and metrics standard.
  • ⚠️ The standard library has no structured logging.

1.3.4 Feedback

  • ⚠️ No fast in-editor analysis.

1.3.5 Interop

  • ❌ No general foreign-function interface.
  • ⚠️ No maintained portable target.

1.3.6 Governance

  • ⚠️ One vendor decides the language’s direction.
  • ⚠️ The specification is not public.

1.4 Profile

Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.

1.4.1 Energy and cost

What this axis asks: what does a running program cost in energy and time?

  • ⚠️ Runs inside a database engine: energy and time are the engine’s, not the program’s.

1.4.2 AI and agent readiness

What this axis asks: how well does a coding model and an agent work in this language?

  • ❌ Sparse training data: models write it unreliably [5].

1.4.3 Safety certification

What this axis asks: is there a certified or standards-qualified path to a safety-critical system?

  • ⚠️ No certified or standards-qualified path for safety-critical systems.

1.5 Scoring detail

1.5.1 Core coupling Dimensions

Dimension Mechanism Description Present Cost
Type T1 dynamic no static check ✅ −15
T2 weak static — 0
T3 dynamic/duck dispatch — 0
T4 no sum types — 0
T5 no generics — 0
T6 no namespaces — 0
T7 type erasure/reflection — 0
T8 operator overloading — 0
T9 inheritance required for polymorphism — 0
T10 no null-safety — 0
T11 no module boundary — 0
T12 no effect tracking ✅ −8
T13 async/sync colouring — 0
T14 unchecked foreign boundary — 0
Type subtotal −15 (−23)
Construction C1 global mutable state by default — 0
C2 no immutability by default ✅ −4
C3 constructors coupled to allocation — 0
C4 no constructor/validation — 0
Construction subtotal −4
Memory M1 manual deallocation — 0
M2 GC non-deterministic pauses — 0
M3 no RAII — 0
M4 no ownership model — 0
Memory subtotal 0
Error Er1 no structured path ✅ −15
Er2 unchecked exceptions only — 0
Er3 errors ignored/non-exhaustive — 0
Er4 no composable result type — 0
Error subtotal −15
Concurrency K1 GIL/no parallel — 0
K2 shared mutable state default — 0
K3 manual sync no race detection — 0
K4 no structured concurrency — 0
K5 no primitives — 0
Concurrency subtotal 0
Platform P1 cannot function outside one vendor/domain ✅ −15
P2 single-vendor but other uses — 0
P3 legacy/discontinued platform — 0
Platform subtotal −15

Core Dimensions score: 51/100 — total cost = −49 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).

1.5.2 Ecosystem coupling Dimensions

Dimension Mechanism Description Present Cost
Performance R1 interpreted no AOT/JIT — 0
R2 GC pauses — 0
R3 JIT warmup — 0
Performance subtotal 0
Ecosystem Ec1 no package manager — 0
Ec2 fragmented tooling — 0
Ec3 small/niche/discontinued ✅ −8
Ec4 proprietary ✅ −8
Ec5 platform-locked tooling — 0
Ec6 monoculture — 0
Ec7 legacy burden — 0
Ecosystem subtotal −15 (−16)
Evolution V1 breaking changes — 0
V2 frozen — 0
V3 discontinued — 0
V4 glacial standards — 0
Evolution subtotal 0
Domain D1 domain/vendor lock-in ✅ −15
D2 poor domain fit — 0
D3 framework forces architecture — 0
Domain subtotal −15
Learning L1 steep ✅ −8
L2 large surface/multi-paradigm — 0
L3 sparse docs — 0
Learning subtotal −8
Syntax S1 verbose ✅ −8
S2 inconsistent — 0
S3 punctuation-heavy — 0
Syntax subtotal −8
Testing G1 weak tooling — 0
G2 poor debugging — 0
G3 runtime-only errors — 0
Testing subtotal 0
Security Q1 memory-unsafe — 0
Q2 dynamic/weak typing risk — 0
Q3 known vulnerability/injection — 0
Security subtotal 0

Meta Dimensions score: 54/100 — total cost = −46 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).

1.5.3 Engineering readiness Dimensions

Dimension Mechanism Description Present Cost
Reproducibility Y1 build not reproducible ✅ −8
Y2 dependencies not pinned ✅ −8
Reproducibility subtotal −15 (−16)
Supply chain Y3 no signed provenance ✅ −8
Y4 registry without namespacing — 0
Supply chain subtotal −8
Observability Y5 no stable tracing standard ✅ −8
Y6 no structured logging ✅ −4
Observability subtotal −12
Feedback Y7 slow build — 0
Y8 no fast in-editor analysis ✅ −4
Feedback subtotal −4
Interop Y9 no foreign-function interface ✅ −8
Y10 no portable target ✅ −4
Interop subtotal −12
Governance Y11 single-vendor governance ✅ −8
Y12 closed specification ✅ −8
Governance subtotal −15 (−16)

Readiness Dimensions score: 34/100 — total cost = −66 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).

Verdict: SAS’s design yields moderate core coupling and a weaker platform position. The DATA step and the PROC step are genuinely separate concerns, and the language is strong for regulated statistical reporting. Main strengths: the retrieval/analysis split, a mature module library, and long-term stability that carries validated code forward. Main weaknesses: dynamic typing with no static check, no structured error path, a proprietary platform with a closed ecosystem, a shrinking market against R and Python, and an idiosyncratic DATA-step syntax that makes the learning curve steep.


The formal development. The definitions, theorems, and proofs behind this page are published in:

All published papers

References

[1]
Y. Loth, “On the nature of coupling: All coupling as shared resource access.” Zenodo, Aug. 2026. doi: 10.5281/zenodo.21925122.
[2]
Y. Loth, “Coupling as a consequence of cohesion: Decomposition, the coupling floor, and domain-imposed coupling.” Zenodo, Aug. 2026. doi: 10.5281/zenodo.21925201.
[3]
Y. Loth, “On the nature of cohesion: Cohesion as correctness relative to a partitioning rule.” Zenodo, Jul. 2026. doi: 10.5281/zenodo.21703193.
[4]
SAS Institute, “SAS programming documentation.” SAS Institute, 2024. Available: https://documentation.sas.com/
[5]
S. Joel, J. J. Wu, and F. H. Fard, “A survey on LLM-based code generation for low-resource and domain-specific programming languages,” ACM Transactions on Software Engineering and Methodology, 2025, doi: 10.1145/3770084.