vtereshkov / umka-lang

Umka: a statically typed embeddable scripting language
BSD 2-Clause "Simplified" License
1k stars 53 forks source link

question about interfaces #404

Closed thacuber2a03 closed 1 month ago

thacuber2a03 commented 1 month ago

ok, so I have this interface:

// value.um
type Value* = interface {
    valueType(): Type
    print()
    isFalsey(): bool
    equals(v: ^Value): bool
}

and at some point in the program, I have this call:

// vm.um, inside a switch
case .Equal:
    b, a := pop(), pop()
    push(value::Bool(a.equals(6b)))

(yes, it's another Lox interpreter, but this one is public)

pop() returns Values, so a and b implement Value, and equality checking in that case is fine, but now I want to add functions to the Lox interpreter, so I make a new interface that extends Value:

// object.um
type Object* = interface {
    value::Value
    objectType(): Type
}

and I make functions implement this interface.

now here's the thing: I want to use the equals method from the Value interface for Objects, i.e. I want to somehow downcast a Value or a ^Value, to an Object or an ^Object respectively. am I able to do this? if so, how? and if I can't, is there some other way I can achieve what I'm trying to do?

vtereshkov commented 1 month ago

No, this is not possible. The feature would have a high runtime cost. It has been considered in #179 and rejected.

However, I cannot see why you need this hierarchy of interfaces at all. Your Value is just a VM stack slot, not even an AST node. It's quite "passive", i.e., it shouldn't know how to evaluate() itself, as this is done by the appropriate VM instructions. Everything on the stack, including functions and objects, can be Values.

My radical suggestion is:

type Value* = any

Thus you also get the type information out of the box. Why write

if a.valueType() == .String && b.valueType() == .String {
    push(value::String(a) + value::String(b))
}

if you can write

    if lhs, rhs := ^str(a), ^str(b); lhs != null && rhs != null {
        push(lhs^ + rhs^)
    }

?

thacuber2a03 commented 1 month ago

Thus you also get the type information out of the box. Why write

if a.valueType() == .String && b.valueType() == .String {
    push(value::String(a) + value::String(b))
}

if you can write

if lhs, rhs := ^str(a), ^str(b); lhs != null && rhs != null {
    push(lhs^ + rhs^)
}

?

what about the values that aren't base types, like value::Nil:

type Nil* = struct {}

or, say, this Function type:

type Function* = struct {
    arity: uint
    chunk: chunk::Chunk
    name: value::String
}
thacuber2a03 commented 1 month ago

off-topic but I got issue number 404 XD

thacuber2a03 commented 1 month ago

Thus you also get the type information out of the box. Why write

if a.valueType() == .String && b.valueType() == .String {
    push(value::String(a) + value::String(b))
}

if you can write

if lhs, rhs := ^str(a), ^str(b); lhs != null && rhs != null {
    push(lhs^ + rhs^)
}

?

what about the values that aren't base types, like value::Nil:

type Nil* = struct {}

or, say, this Function type:

type Function* = struct {
  arity: uint
  chunk: chunk::Chunk
  name: value::String
}

nevermind, did not read this:

Everything on the stack, including functions and objects, can be Values.

let me try that and see if it works out I, however, want to keep the print and isFalsey methods

vtereshkov commented 1 month ago

what about the values that aren't base types, like value::Nil ... or, say, this Function type:

You can convert any type (not necessarily a basic type) to any. So you can put your Function into any and then check if it's indeed your Function:

val := any(Function{/*...*/})
//...
if f := ^Function(val); f != null {
    call(f^)
}

As for Nil, I don't know what is better: to use your own Nil as you defined it, or just set any to null.

Anyway, I think you needn't reinvent RTTI.

off-topic but I got issue number 404 XD

Also noticed this :)

thacuber2a03 commented 1 month ago

As for Nil, I don't know what is better: to use your own Nil as you defined it, or just set any to null.

wait, I could make Nil a ^null instead, can't I?

vtereshkov commented 1 month ago

null is a value, not a type. There is no name for the base type of the null constant.

thacuber2a03 commented 1 month ago

@vtereshkov thanks :D edit: the commit notice is there already lol

vtereshkov commented 1 month ago

Great! By the way, you can join our community chat if you wish: https://discord.gg/PcT7cn59h9