PHP RFC: Single-Expression functions
- Version: 1.0
- Date: 2025-05-22
- Author: Dmitrii Derepko xepozzd@gmail.com
- Status: Draft
- Target Version: PHP 8.5
Introduction
This RFC proposes an alternative syntax for single-expression functions using the =
operator instead of curly braces and explicit return
statements.
This syntax provides a concise way to declare simple functions without the overhead of traditional function body syntax.
Proposal
This RFC introduces a shorthand syntax for functions that consist of a single return statement.
The proposed syntax uses the =
operator to denote that the function directly returns the result of the expression.
Reasoning
Modern codebases typically contain numerous entities, models, forms, data transfer objects (DTOs), and value objects (VOs) with many getter and setter methods. These methods are usually simple one-liners that create significant cognitive overhead with minimal functional value.
Concise syntax makes it easier to associate function names with their return values, reducing mental parsing overhead.
Current Syntax Problems
PSR-compliant syntax requires multi-line formatting:
function getName() { return "Name"; }
Even compact syntax contains redundant elements:
function getName() { return "Name"; }
Cognitive Processing Analysis
When reading traditional function syntax, developers mentally process:
function getName() { return "Name"; }
The brain performs this parsing:
function
- skip (boilerplate)getName
- identify function purpose()
- note no parameters{
- skip (boilerplate)return
- skip (expected keyword)"Name"
- identify return value;
- skip (boilerplate)}
- skip (boilerplate)
Effectively, the brain extracts:
getName() "Name"
This syntax directly represents the mental model: function maps to value, making the code more readable and reducing the cognitive load required to understand simple getter/setter methods.
Syntax
function functionName(parameters): returnType = expression;
This is functionally equivalent to:
function functionName(parameters): returnType { return expression; }
Examples
Basic function:
// Short syntax function getVersion(): int = 1; // Equivalent traditional syntax function getVersion(): int { return 1; }
Function with parameters:
// Short syntax function add(int $a, int $b): int = $a + $b; // Equivalent traditional syntax function add(int $a, int $b): int { return $a + $b; }
Class methods:
class Calculator { // Short syntax public function multiply(int $a, int $b): int = $a * $b; // Equivalent traditional syntax public function divide(int $a, int $b): float { return $a / $b; } }
Rationale
Distinction from Arrow Functions
This proposal deliberately uses =
instead of =>
to avoid confusion with existing arrow function syntax (fn() => expression
). The key differences are:
- Scope: Short functions with
=
are regular named functions with global scope - Capture: No variable capture from parent scope (unlike
fn
) - Syntax: Uses
function
keyword, notfn
- Purpose: Syntactic sugar for simple return statements
Benefits
- Reduced verbosity for simple functions
- Improved readability for single-expression functions
- Consistency with modern language trends toward concise syntax
- No performance impact - purely lexical transformation
- Visual clarity - removes 8 symbols, 1 line instead of 4
- Faster comprehension of function purpose and return value
- Consistent with mathematical notation where f(x) = expression
- Maintains clarity while eliminating redundancy
Use Cases
Getter methods:
class User { public function __construct( private string $name, private string $email ) {} public function getName(): string = $this->name; public function getEmail(): string = $this->email; public function getDisplayName(): string = ucfirst($this->name); }
Simple calculations:
function calculateTax(float $amount): float = $amount * 0.18; function isEven(int $number): bool = $number % 2 === 0; function formatCurrency(float $amount): string = '$' . number_format($amount, 2);
Delegation methods:
class Service { public function __construct(private Repository $repo) {} public function findById(int $id): ?Entity = $this->repo->find($id); public function count(): int = $this->repo->count(); }
Technical Implementation
The implementation is purely lexical—during parsing, the short syntax is transformed into the equivalent traditional syntax before further processing. This ensures:
- No runtime performance impact
- Full compatibility with existing reflection APIs
- Identical behavior to traditional function declarations
Backward Compatibility
This change introduces no breaking changes. The proposed syntax would currently result in a parse error, making it safe to implement.
Alternative Syntax Considered
Using => operator
The original Short Functions RFC proposed using =>
, but this creates confusion with arrow functions:
// Confusing - looks like an arrow function function add(int $a, int $b): int => $a + $b; // Clear distinction function add(int $a, int $b): int = $a + $b;
Pipe Operator usage
// multiline function handle(string $input): string = func1($input) |> func2(...) |> func3(...) |> func4(...) ; // oneline function handle(string $input): string = func1($input) |> func2(...) |> func3(...) |> func4(...);
Clone
class Point { public function __construct(private int $x, private int $y) {} public function withX($x): static => clone($this, ["x" => $x]); public function withY($y): static => clone($this, ["y" => $y]); }
class Point { public function __construct(private int $x, private int $y) {} public function withX($x): static { return clone($this, ["x" => $x]); } public function withY($y): static { return clone($this, ["y" => $y]); } }
Single-Expression functions in other languages
Kotlin
fun add(a: Int, b: Int): Int = a + b fun getVersion() = 1 fun isEven(n: Int) = n % 2 == 0
Scala
F#
let add a b = a + b let getVersion () = 1 let isPositive x = x > 0
Rust
fn add(a: i32, b: i32) -> i32 = a + b; fn get_version() -> i32 = 1; fn add(a: i32, b: i32) -> i32 { a + b }
Haskel
add :: Int -> Int -> Int add a b = a + b getVersion :: Int getVersion = 1
Swift
func add(_ a: Int, _ b: Int) -> Int = a + b func getVersion() -> Int = 1
C#
public int Add(int a, int b) => a + b; public string FullName => $"{FirstName} {LastName}"; public int GetVersion() => 1;
Dart
int add(int a, int b) => a + b; String get fullName => '$firstName $lastName';
Elixir
def add(a, b), do: a + b def get_version, do: 1
Javascript/Typescript (named closures)
const add = (a, b) => a + b; const getVersion = () => 1;
Limitations
- Single expression only - multi-statement functions require traditional syntax
- No variable assignments - cannot declare variables within the expression
- No control structures - if/else must use ternary operator
Future Scope
This feature could potentially be extended to:
- Interface method declarations with default implementations
- Omitting the
function
keyword to declare a function – class member