Skip to content

Reusable property hooks #167

Open
Open
@thekid

Description

@thekid

As noted in the "Future Scope" section of the property hooks RFC, reusable package hooks are not part of the first RFC, but the authors @iluuu1994 and @Crell envision it being added later. Here are some ideas of how it could be implemented.

Swift

The concept here is called property wrappers. Here's an example from their documentation:

@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

rectangle.height = 10 // Width is set to 10
rectangle.height = 24 // Width is set to 12

See https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Property-Wrappers

Kotlin

The concept here is called property delegates. Here's an example of how these are implemented:

// Syntax
var name: String by NameDelegate()

// Compiled to
val name$delegate = NameDelegate()
var name: String
    get() = name$delegate.getValue(this, this::name)
    set(value) { name$delegate.setValue(this, this::name, value) }

See https://blog.kotlin-academy.com/kotlin-programmer-dictionary-field-vs-property-30ab7ef70531

Idea for PHP

Going down the same path as Kotlin (which the PHP RFC is inspired quite a bit from) but without introducing any new keywords, we could come up with the following:

class Environment {

  // Syntax with "as" (doesn't conflict with syntax highlighting like "use" would)
  public string $home as new ByLazy(fn() => getenv('HOME'));

  // Compiled to the following which already works with PR #166 merged
  private $__home_delegate= new ByLazy(fn() => getenv('HOME'));
  public string $home {
    get => $this->__home_delegate->get($this, (object)['value' => &$field]);
    set => $this->__home_delegate->set($this, (object)['value' => &$field], $value);
  }
}

The ByLazy implementation is as follows:

class ByLazy {
  public function __construct(private callable $init) { }

  public function get($self, $property) {
    return ($property->value??= [($this->init)()])[0];
  }

  public function set($self, $property, $value) {
    $property->value= [$value];
  }
}

Note: Using an array for a value will allow initializing the property to NULL.

See also

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions