evincarofautumn / kitten

A statically typed concatenative systems programming language.
http://kittenlang.org/
Other
1.1k stars 42 forks source link

Field names in types #169

Open evincarofautumn opened 7 years ago

evincarofautumn commented 7 years ago

Large structures are unwieldy to use without named fields. I propose we reuse the as notation for specifying field names.

type Point3D:
  case point3D:
    x as Float64
    y as Float64
    z as Float64

I think it’s good to impose the restriction that fields are either all named, or all anonymous. Next, we need a syntax and semantics for field access, and the conventional .name notation is as good as anything.

1.0 2.0 3.0 point3D .x  // 1.0

1.0 2.0 3.0 point3D -> p;
p.x  // 1.0

Initially, field accesses will not be polymorphic—x.y means “look up the index of the field y in the inferred type of x and generate the appropriate match expression”. If the field isn’t present, or the inferred type is polymorphic, the compiler will produce an error.

1.0 2.0 3.0 point3D .x
// ==
1.0 2.0 3.0 point3D match case point3D -> x, _, _ { x }

This restriction can be relaxed later, for example if we add extensible records.

If the same field name is present in multiple constructors of a type, then the field must have the same type in each.

type Animal:
  case dog:
    age as Float64
  case cat:
    age as Float64

7.5 dog .age  // Float64

type Animal:
  case dog:
    age as Float64
  case cat:
    age as Int32  // Error: field 'age' has different types in different cases

If the field is present in some cases but not others, a field access produces an Optional.

type Job:
  case butcher
  case baker
  case candlestick_maker
  case programmer
  case beggar

type Animal:
  case human:
    name as Name
    job as Job
  case cat:
    name as Name

define get_a_job (Animal -> Job):
  .job  // match case human -> _, job { job some } else { none }
  beggar from_optional

We may also need syntax for initialising and replacing fields by name, like Haskell’s record notation.

data Animal = Human { name :: Name, job :: Job } | Cat { name :: Name }

sam, sammer :: Animal
sam = Human { name = "Sam", job = Programmer }
sammer = sam { name = "Sammer" }

But so far I haven’t been able to come up with anything satisfactory for Kitten.

evincarofautumn commented 7 years ago

It looks like we can represent an expression .label with a Get constructor for Term, then infer the type as α → α::label, that is, whatever the type of label is in α. Zonking does the actual lookup and error reporting, and then the Get term can be desugared to a match after inference. α can be any “concrete type”, which I guess is just a type constructor or constructor application.

For updating fields, I’m considering a notation like .(value -> name), or .(-> name) to take the value from the stack, but this doesn’t quite work for initialisation.