Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static fields on types #496

Open
kengorab opened this issue Nov 14, 2024 · 0 comments
Open

Static fields on types #496

kengorab opened this issue Nov 14, 2024 · 0 comments

Comments

@kengorab
Copy link
Owner

kengorab commented Nov 14, 2024

Given a type/enum Foo, there should be some mechanism for denoting "static" fields on that type/enum. A "static" field in this context is essentially a global variable which is namespaced to that type/enum, for example, consider the prelude type Map<K, V>:

type Map<K, V> {
  size: Int
  _entries: MapEntry<K, V>?[] = []
  _capacity: Int = 16
  _loadFactor: Float = 0.75

  func new<K, V>(initialCapacity = 16): Map<K, V> {
    // Find a power of 2 >= initialCapacity, if non-default value provided
    val capacity = if initialCapacity != 16 {
      initialCapacity.nextPowerOf2()
    } else {
      initialCapacity
    }
    ...
  }
  ...
}

The default values for the _capacity and _loadFactor fields are supplied in those fields' _initializer_s, but the initialCapacity parameter's default value in the new "static" method is a magic number (which is also referenced once again within the function body itself).

This value could be extracted out into a variable outside of the type definition, such as

val MAP_DEFAULT_INITIAL_CAPACITY = 16

but that allows that value to leak out into a scope broader than the scope in which it's intended to be used (ie. just within the Map scope).

Other languages have a notion of "static" values, as in global values which are namespaced within the scope of the type itself. Accessing the default initial capacity if it were defined as a "static"/namespaced variable would look like Map.defaultInitialCapacity (if it were indeed exposed as a public value). These other languages typically use a modifier keyword like static to describe this. Using this, the above would look something like:

type Map<K, V> {
  size: Int
  _entries: MapEntry<K, V>?[] = []
  _capacity: Int = 16
  _loadFactor: Float = 0.75

  static defaultInitialCapacity: Int = 16

  func new<K, V>(initialCapacity = Self.defaultInitialCapacity): Map<K, V> {
    // Find a power of 2 >= initialCapacity, if non-default value provided
    val capacity = if initialCapacity != Self.defaultInitialCapacity {
      initialCapacity.nextPowerOf2()
    } else {
      initialCapacity
    }
    ...
  }
  ...
}

Note: This uses the Self.<identifier> syntax which is proposed in #497

Note though that this introduces the static keyword, which has previously been omitted - what other languages would call "static methods" are just implemented in Abra as ordinary methods on a type/enum just without the self parameter. So introducing static just for this purpose feels odd. Not to mention that the meaning of "static" differs across programming languages, so it might make more sense to use something that feels a bit more ergonomic.

Since a "static" value is just an ordinary variable, we could leverage the existing val/var variable declaration syntax:

type Map<K, V> {
  size: Int
  _entries: MapEntry<K, V>?[] = []
  _capacity: Int = 16
  _loadFactor: Float = 0.75

  val defaultInitialCapacity: Int = 16

  ...
}

This also had the added benefit of representing the reassignability of the static value (which can also be referred to as "type variables", a new category of variable alongside "top-level variables", "local variables", and "instance variables").

Within the type/enum declaration, it must come after field declarations but may be interspersed between method declarations within the scope. It also must have an initializer (much like other variable declarations) and must also have a type annotation (much like other field declarations). Like type methods (static methods), generics are not accessible within a type variable's type annotation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant