alshdavid / BorrowScript

TypeScript with a Borrow Checker. Multi-threaded, Tiny binaries. No GC. Easy to write.
1.45k stars 16 forks source link

Inclusion of structs or the ability to instantiate type literals #47

Closed alshdavid closed 1 year ago

alshdavid commented 2 years ago

This is a discussion for a longer term feature inclusion - just putting my thoughts here for discussion. The priority is building a compiler for a basic form of the project

Structs are useful types as they describe simple property collections. In JavaScript we have duck typed objects and in TypeScript we can describe those objects as type literals.

They are great for use cases like describing the values coming in from an external API or the properties of a constructors/method.

Classes are great for exposing functionality through methods within a container that has dependencies injected into it (via arguments - let's not talk about DI frameworks at this stage :laughing:). They are cumbersome to use to describe API responses.

To me, both are useful and I would like to include them.

class Foo {
  public bar: string

  constructor(bar: string) {
    this.bar = bar
  }
}

const f = new Foo()
struct Foo {
  bar: string
}

const f = Foo{}

Examples of usage

struct FooOptions {
  bar: string
}

class Foo {
  public bar: string

  constructor({ bar }: FooOptions) {
    this.bar = bar
  }
}

const f = new Foo(FooOptions{
  bar: "Hello"
})

To extend this further, what if we simply allowed the ability to instantiate type definitions?

type Foo = {
  bar: string
}
type Bar = number

const f = Foo{} // Foo { bar: '' }
const b = Bar{} // number = 0

That way we could use type logic to programatically describe structs

type FooOptions = PublicProperties<typeof Foo>

class Foo {
  public bar: string

  constructor({ bar }: FooOptions) {
    this.bar = bar
  }
}

const f = new Foo(FooOptions{
  bar: "Hello"
})
Isaac-Leonard commented 2 years ago

Checkout how alanlang or haskell does this.

argoopjmc commented 2 years ago

While we are discussing types, are there plans to support algebraic data types?

alshdavid commented 2 years ago

While we are discussing types, are there plans to support algebraic data types?

Absolutely, I believe that falls under the TypeScript area of inspiration

Isaac-Leonard commented 2 years ago

One thing to note with them is that apparently there's soundness in having them be open ended. Thats why rust has enums structured the way they do. I'm not exactly sure what the issues are though yet and ts style unions are easier to use then rusts in my opinion.

alshdavid commented 2 years ago

What's challenging here is that TypeScript offers an immensely flexible type system due to how practically everything is structurally evaluated.

This is something I'd like to preserve the essence of but it's an interesting conceptual challenge. I would perhaps be in favour structs replacing type literals and having classes and structs coexist.

struct Person {
  name: string
}

class PersonCollection {
  private _people: Person[]

  constructor() {
    this._people = People[]
  }

  public add(p Person): void { // move p to PersonCollection.add()
    this._people.push(p) // move p to PersonCollection._people
  }
}

interface IPersonCollection {
  add(Person): void
}

Where the compiler might be able to move values from one struct to another if the new struct contains at least as many values

struct Cat {
  name: string
  color: string
}

struct Monkey {
  name: string
  color: string
  height: int
}

const c: Cat = Cat{"Herbert", "Ginger"} // pointer to Cat{}
const m: Monkey = c //  a new Monkey is initialized and the properties of cat are copied to it, c is deallocated 
Isaac-Leonard commented 2 years ago

My biggest issue with this so far in my implementation is the ordering of fields in anonymous structs Are {x:4, y:1} and {y:1, x:4} the same? Currently I say yes and simply order everything alphabetically while compiling, but that wouldn't account for something like {x:4, y:1} and {w:3, x:4, y:1} both matching a type point = {x:Int, y:Int} Re-ordering fields on the fly would be slow, and require everything to be immutable or else you couldn't effectively pass things to different functions

alshdavid commented 2 years ago

Haha, perhaps the lexer will just complain if you don't order the struct properties alphabetically yourself. This will enforce property ordering as a styling convention, perhaps implemented later as part of the code formater

Isaac-Leonard commented 2 years ago

Not a bad idea, could give a warning or something but it doesn't fix the second example with extra properties before the expected ones