Idea for Restricted "Pseudo-Dynamic" Typing


(David Sweeris) #1

(Disclaimer: This might actually just be a slightly disguised subset of the “allow non-type generic parameters” idea, depending on whether that includes stuff like this:

var foo = 2
var bar = Vector<foo>()

or if it only addresses this:

var bar = Vector<2>()

)

Building on “pure" from: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/003684.html

Consider this function:

func @pure foo<T,U,V>(_: T.Type, _: U.Type) -> V.Type {…}

Since @pure prevents foo from accessing global/static variables, since it can only call other @pure functions, and since its only parameters are themselves other types, then the return type of V must solely depend on the *types* T and U (and what can be gotten from T & U using @pure functions). While this is not the same thing as “known at compile time”, it does (I think) guarantee that V can be deduced only using what is known about T and U *at compile time*. If the return value of such a function could used as a “proper” type (i.e., it could be used like “var bar: foo(Int.self, String.self)”, for instance), it wouldn’t so much remove the “types must be known at compile time” restriction, as it would give the programmer a powerful way to tell the compiler *how* to figure out what the type is.

The use case I’m thinking of is admittedly pretty narrow… I personally want this feature to help with a work-around I’m using for Swift not having non-type generic parameters:

protocol IVAT { //Integer Value As Type
  static var integerValue:Int {get}
}
struct _0 : IVAT { static let integerValue = 0 }
struct _1 : IVAT { static let integerValue = 1 }
struct _2 : IVAT { static let integerValue = 2 }
//…etc

struct Matrix<M:IVAT, N:IVAT> {
  private var backing = [[Double]](count: M.integerValue, repeatedValue: [Double](count: N.integerValue, repeatedValue: 0.0))
  subscript(row: Int, col: Int) -> Double {}
}

If we allowed pure functions to return “usable” types, then Matrices could be joined like this:

func @pure + <T: IVAT, U: IVAT, V: IVAT> (_: T.Type, _: U.Type) -> V.Type {
  switch (T, U) {
  case (_0, _0): return _0.self
  case (_0, _1): return _1.self
  …
  default: return _0.self
}
// joins two matrices… join([[1,2],[3,4]], [[5],[6]]) would return [[1,2,5],[3,4,6]]
func join <M:IVAT, N1:IVAT, N2:IVAT> (lhs: Matrix<M,N1>, rhs: Matrix<M, N2>) -> Matrix<M, N1.self + N2.self> {…}

Again, I realize the use case I’ve presented is pretty narrow (and one that’ll hopefully become a moot point later), but it seems like this would be a *very* expressive way to expand the power of generic types.

Anyway… Yay? Nay? Implementation-wise, my first thought is to embed the REPL in the generic specialization system then use the function in question as its input to extend the generic specialization system, but I don’t know if that’s the right place to handle it, let alone if that’s a feasible idea.

-Dave