Skip to content

Vim9: support using a protected new() method#16604

Closed
yegappan wants to merge 1 commit intovim:masterfrom
yegappan:obj-method-call
Closed

Vim9: support using a protected new() method#16604
yegappan wants to merge 1 commit intovim:masterfrom
yegappan:obj-method-call

Conversation

@yegappan
Copy link
Member

@yegappan yegappan commented Feb 9, 2025

No description provided.

@zzzyxwvut
Copy link
Contributor

Can protected constructors be also supported for enums
(E1325) and abstract classes (E1359) (constructors are not
inherited, so extending an abstract class with _new* from
a class with new* should not matter for E1377)? It would
further ensure their uselessness via inaccessibility.

@chrisbra
Copy link
Member

Hm, there doesn't seem to be anything special about _new() vs new(). In other words, the example already works with .new() instead of ._new(). I mean if you think this is useful, that is fine, but the example made me wonder a bit.

@yegappan
Copy link
Member Author

yegappan commented Feb 11, 2025

Hi @chrisbra

Hm, there doesn't seem to be anything special about _new() vs new(). In other words, the example already works with .new() instead of ._new(). I mean if you think this is useful, that is fine, but the example made me wonder a bit.

The difference is that the protected new() constructor cannot be called from outside of the class. So an instance of the class cannot be created outside of the class. This can be used to create the singleton class (https://refactoring.guru/design-patterns/singleton). One of the test checks that an instance of the class cannot be created outside of the class.

@yegappan
Copy link
Member Author

Can protected constructors be also supported for enums (E1325) and abstract classes (E1359) (constructors are not inherited, so extending an abstract class with _new* from a class with new* should not matter for E1377)? It would further ensure their uselessness via inaccessibility.

What is the use case for supporting the protected constructor with enums and abstract classes? The enum constructor is only used for the enum values and cannot be used outside of the enum.

@chrisbra
Copy link
Member

alright thanks!

@zzzyxwvut
Copy link
Contributor

Exactly, enum constants cannot be explicitly created with
any accessibility constructors, yet public accessibility is
‘advertised’ when these constructors are defined. This is
a matter style of course, so my request can be summarily
dismissed as such. :)

The documentation entry for protected constructors talks
about singletons. An enum with a single constant is also
a singleton; yet this limitation with respect to _new* is
not mentioned.

As for abstract classes, multi-statement initialisation is
currently rather roundabout (and visible) to pull off.

(A)

File tuple.vim:

vim9script

abstract class RandPair
    const a: number = RandPair.Rand()
    const b: number = RandPair.Rand()

    static def Rand(): number
        const seed: list<number> = srand()
        writefile([string(seed)], $VIM_PAIR_LOG, 'a')
        return rand(seed)
    enddef


#### CAN WE DO BETTER?
#   const a: number
#   const b: number

#### E1359
#   def _new()
#       const seed: list<number> = srand()
#       writefile([string(seed)], $VIM_PAIR_LOG, 'a')
#       this.a = rand(seed)
#       this.b = rand(seed)
#   enddef
endclass

class NumTriplet extends RandPair
    const c: number

    def new(this.c)
    enddef

    def string(): string
        return printf('(%d, %d, %d)', this.a, this.b, this.c)
    enddef
endclass

echo NumTriplet.new(0)

Protected constructors are fit for this duty. As with enum
constants initialisation, explicit constructor calls would
still not be permitted.

Another use case for protected constructors of concrete
classes is to enable access to implementation for creating
immutable objects.

(B)
File list.vim:
vim9script

export interface Listable
    def Cons(_: any): Listable
    def Rest(): Listable
    def First(): any
    def empty(): bool
    def len(): number
    def string(): string
endinterface

enum EmptyList implements Listable
    INSTANCE

    def Cons(value: any): Listable
        return List.new(value)
    enddef

    def Rest(): Listable
        return this
    enddef

    def First(): any
        return null
    enddef

    def empty(): bool
        return true
    enddef

    def len(): number
        return 0
    enddef

    def string(): string
        return '[]'
    enddef
endenum

class List implements Listable
    const _value: any
    const _size: number
    var _next: Listable

    def new(value: any)
        this._value = value
        this._size = 1
        this._next = EmptyList.INSTANCE
    enddef

    def _newCons(value: any, size: number)
        this._value = value
        this._size = size
    enddef

    def Cons(value: any): Listable
        const list: List = List._newCons(value, (this._size + 1))
        list._next = this
        return list
    enddef

    def Rest(): Listable
        return this._next
    enddef

    def First(): any
        return this._value
    enddef

    def empty(): bool
        return (this._size == 0)
    enddef

    def len(): number
        return this._size
    enddef

    def string(): string
        if (empty(this))
            return '[]'
        endif

        var list: Listable = this
        var text: string = '[' .. string(list.First()) .. ', '
        list = list.Rest()

        while (!empty(list))
            text ..= string(list.First()) .. ', '
            list = list.Rest()
        endwhile

        return strpart(text, 0, (strlen(text) - 2)) .. ']'
    enddef
endclass

export def MakeEmptyList(): Listable
    return EmptyList.INSTANCE
enddef

export def MakeList(value: any): Listable
    return List.new(value)
enddef
File test.vim:
vim9script

import './list.vim' as _list

# E1008?
const list: _list.Listable = _list.MakeList(0).Cons(1).Cons(2).Cons(3)
echo list ==  _list.MakeEmptyList().Cons(0).Cons(1).Cons(2).Cons(3)
echo list.string()
echo list.Rest().Rest().Rest().Rest().empty()
echo 1 == list.Rest().Rest().First()
echo 4 == list.len()

All in all, protected constructors are generally useful, and
it would be nice if they were generally supported at some
point.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants