nim-works / nimskull

An in development statically typed systems programming language; with sustainability at its core. We, the community of users, maintain it.
https://nim-works.github.io/nimskull/index.html
Other
272 stars 38 forks source link

Remove `bind` and `mixin` from generics #561

Open saem opened 1 year ago

saem commented 1 year ago

Core Issue

bind and mixin are template metaprogramming features, which generics are not. Generics, or parametric polymorphism, have to do with types.

To put a very fine point on things:

proc foo[T](a, b :T): T

Is a procedure type, with a type parameter, meaning the type changes (polymorphic) based on said type parameter. It can be read as, foo is a procedure type, where for all T (universal), T, T -> T.

It's not arbitrary syntactic "fill in the blanks", those are templates and those are entirely different. Doing this will drive a major step in substantially improving our type system and digging deep into the fun bits.

Solution/Approach

Steps:

  1. in a branch remove bind and mixin from generics
  2. chase down compiler and stdlib issues until things work again (or close)
  3. use the lessons learned in the above steps to determine the alternative approach
  4. implement the alternative approach, this and any intermediate improvements are what will get merged

Steps 1 - 3 are there to discover the rest of the detailed solution, because mixin is a poor facsimile for late static type safe binding, which is fundamentally very useful. So some sort of alternative approach is required for generics to fill the gap the removal of mixin, especially, will leave behind.

If you'd like to work this issue please ping, @saem.

Early Thinking about Solution

This is mostly illustrative and not any sort of final design. The assumption is that early binding remains as the default behaviour and so the focus is on some mixin-ish feature/behaviour. Taking the mixin example form the manual (reproduced below):

proc create*[T](): ref T =
  mixin init   # <-- this will go away
  new result
  init result

With whatever mixin-ish thing we end up with, it should be equivalent to the following -- ignore the syntax, it's merely approximating the approximate goal using existing constructs:

proc create*[T, init: proc (T) | proc (T): ref T = init](): ref T =
  new result
  init result
blackmius commented 6 months ago

doesnt it means that type T is not simple anything, but have to implement init proc? maybe go to finalising concept way

type A = concept a
  a.init() is ref A | void

proc create[T: A](): ref T =
  new result
  init result
saem commented 6 months ago

doesnt it means that type T is not simple anything, but have to implement init proc? maybe go to finalising concept way

IIUC, then yes, over time that's the conclusion I'm arriving at too. That the first real fix is to have working concepts that capture the contract that the routine requires.