Advice for implementing "literal values as generic types"

Hi everyone! I'm trying to implement literal values as generic types. So far, I've made `LiteralExpr` inherit from both `Expr` and `GenericTypeDecl` (instead of just `Expr`), and did whatever other changes were necessary to get that compiling (mostly putting several "using Expr::setImplicit;" kind of lines within `LiteralExpr`'s declaration and prepending "(Expr*)" to a bunch of variables in what are now ambiguous function calls).

Anyway, I haven't done any testing yet to see how much I've broken (compiles != works), but before I get neck-deep in everything I thought I'd ask if anyone sees any fundamental issues with this approach, or has any better ideas.

- Dave Sweeris

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

Slava

···

On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org> wrote:

Hi everyone! I'm trying to implement literal values as generic types.

Hi David,

You’re arguably deep in the weeds. Try starting by adding new nodes to ExprNodes.def and DeclNodes.def and watch what blows up. Once you do that, crib code similar nodes to get your new Expr/Decl nodes to start compiling.

Please also keep in mind that LLVM and derived projects like Swift have custom RTTI logic (which is partly why these “.defs” files exist), and therefore language features like multiple inheritance don’t “just work” like you might expect.

Dave

···

On Aug 29, 2017, at 14:04, David Sweeris via swift-dev <swift-dev@swift.org> wrote:

Hi everyone! I'm trying to implement literal values as generic types. So far, I've made `LiteralExpr` inherit from both `Expr` and `GenericTypeDecl` (instead of just `Expr`), and did whatever other changes were necessary to get that compiling (mostly putting several "using Expr::setImplicit;" kind of lines within `LiteralExpr`'s declaration and prepending "(Expr*)" to a bunch of variables in what are now ambiguous function calls).

Anyway, I haven't done any testing yet to see how much I've broken (compiles != works), but before I get neck-deep in everything I thought I'd ask if anyone sees any fundamental issues with this approach, or has any better ideas.

- Dave Sweeris
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

No, I mean so that a vector's or matrix's dimensions can be part of its type (strawman syntax and protocol name, but this is pretty much what I'll be trying to support, at least at first):
struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start, until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Anyway, I think it'll be worth writing up as a proposal, once it's working and I've figured out how to present these "literal types" to Swift's type system (I'd like to keep it as a literal of some sort, so that, say, `L` in this example could be used to set the value of any type that conforms to `ExpressibleByIntegerLiteral`).

- Dave Sweeris

···

On Aug 29, 2017, at 1:49 PM, Slava Pestov <spestov@apple.com> wrote:

On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org> wrote:

Hi everyone! I'm trying to implement literal values as generic types.

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

I was thinking I'd probably eventually need to muck about with those, but it's good to know that I should start poking around in there sooner rather than later. Thanks!

···

On Aug 29, 2017, at 11:21 AM, David Zarzycki <zarzycki@icloud.com> wrote:

Hi David,

You’re arguably deep in the weeds. Try starting by adding new nodes to ExprNodes.def and DeclNodes.def and watch what blows up. Once you do that, crib code similar nodes to get your new Expr/Decl nodes to start compiling.

Please also keep in mind that LLVM and derived projects like Swift have custom RTTI logic (which is partly why these “.defs” files exist), and therefore language features like multiple inheritance don’t “just work” like you might expect.

Dave

On Aug 29, 2017, at 14:04, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

Hi everyone! I'm trying to implement literal values as generic types. So far, I've made `LiteralExpr` inherit from both `Expr` and `GenericTypeDecl` (instead of just `Expr`), and did whatever other changes were necessary to get that compiling (mostly putting several "using Expr::setImplicit;" kind of lines within `LiteralExpr`'s declaration and prepending "(Expr*)" to a bunch of variables in what are now ambiguous function calls).

Anyway, I haven't done any testing yet to see how much I've broken (compiles != works), but before I get neck-deep in everything I thought I'd ask if anyone sees any fundamental issues with this approach, or has any better ideas.

- Dave Sweeris
_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev

Hi everyone! I'm trying to implement literal values as generic types.

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

No, I mean so that a vector's or matrix's dimensions can be part of its type (strawman syntax and protocol name, but this is pretty much what I'll be trying to support, at least at first):

I think instead of modeling these generic parameters as types, you should look into generalizing GenericSignatures to contain ‘literal requirements’. Right now, we have four kinds of requirements:

- A is a subclass of B
- A conforms to B
- A is the same type as B
- A has a known layout

All of these except for the last one have a generic parameter as their right hand side. All of them have a generic parameter on their left hand side. I think what you want is to add a new ‘value parameter’ that is not a type, but instead has a value. Requirements would be placed on these to constrain them to known kinds of literals (integers, strings, etc).

struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start,

The compiler’s constant folding operates on SIL instructions, not Exprs directly. However constant folding is not generally what you want for this, because constant folding is a ‘best effort’ kind of optimization (it may or may not fold your calculation down to a constant) and also it produces code that evaluates the result (even if its a constant) and not the result itself.

I think if you want to explore type-level computation like this, you should define a very small subset of the language which can be computed by the type checker like this.

···

On Aug 29, 2017, at 2:21 PM, David Sweeris <davesweeris@mac.com> wrote:

On Aug 29, 2017, at 1:49 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Anyway, I think it'll be worth writing up as a proposal, once it's working and I've figured out how to present these "literal types" to Swift's type system (I'd like to keep it as a literal of some sort, so that, say, `L` in this example could be used to set the value of any type that conforms to `ExpressibleByIntegerLiteral`).

- Dave Sweeris

Hi David,

To add to what I wrote earlier, the design of the Swift is such that adding new features isn’t “hard”, but it does require time and patience because most new features have ripple effects on other parts of the compiler that need to be thought through. Fortunately, the design of the compiler is such that vast majority of those ripple effects will cause a compile time failure. This is why I encouraged you to add new “.def” nodes up front. That’s the good news. The bad news is that you still need to think through the ripple effects and that means developing some minimal design and debugging expertise over the compiler as a whole. If you’re not discouraged by the process, the experience is quite rewarding.

One more tip: if you’re going to add a new language feature, focus first on the type system and writing tests. Once you feel that implications are fully thought through, you can move on to “code gen” (i.e. SILGen and IRGen). If you rush to “code gen” first so that you can play with your shiny new feature, then you’re likely to get frustrated by self-inflicted bugs. You can experience the blissful pre-code-gen world by invoking swift like so: swift -frontend -typecheck file.swift

Dave

···

On Aug 29, 2017, at 17:26, David Sweeris <davesweeris@mac.com> wrote:

I was thinking I'd probably eventually need to muck about with those, but it's good to know that I should start poking around in there sooner rather than later. Thanks!

On Aug 29, 2017, at 11:21 AM, David Zarzycki <zarzycki@icloud.com <mailto:zarzycki@icloud.com>> wrote:

Hi David,

You’re arguably deep in the weeds. Try starting by adding new nodes to ExprNodes.def and DeclNodes.def and watch what blows up. Once you do that, crib code similar nodes to get your new Expr/Decl nodes to start compiling.

Please also keep in mind that LLVM and derived projects like Swift have custom RTTI logic (which is partly why these “.defs” files exist), and therefore language features like multiple inheritance don’t “just work” like you might expect.

Dave

On Aug 29, 2017, at 14:04, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

Hi everyone! I'm trying to implement literal values as generic types. So far, I've made `LiteralExpr` inherit from both `Expr` and `GenericTypeDecl` (instead of just `Expr`), and did whatever other changes were necessary to get that compiling (mostly putting several "using Expr::setImplicit;" kind of lines within `LiteralExpr`'s declaration and prepending "(Expr*)" to a bunch of variables in what are now ambiguous function calls).

Anyway, I haven't done any testing yet to see how much I've broken (compiles != works), but before I get neck-deep in everything I thought I'd ask if anyone sees any fundamental issues with this approach, or has any better ideas.

- Dave Sweeris
_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev

Hi everyone! I'm trying to implement literal values as generic types.

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

No, I mean so that a vector's or matrix's dimensions can be part of its type (strawman syntax and protocol name, but this is pretty much what I'll be trying to support, at least at first):
struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start, until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Constant folding occurs too late in the compilation process for you to take advantage of it in the solver. You’ll need to write your own evaluator and teach CSSimplify to invoke it during the type matching process. I would start by adding a new kind of Type and TypeRepr that hold SequenceExpr nodes and a CanType that holds an APSInt representing the result of evaluating the expression.

···

On Aug 29, 2017, at 5:22 PM, David Sweeris via swift-dev <swift-dev@swift.org> wrote:

On Aug 29, 2017, at 1:49 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

Anyway, I think it'll be worth writing up as a proposal, once it's working and I've figured out how to present these "literal types" to Swift's type system (I'd like to keep it as a literal of some sort, so that, say, `L` in this example could be used to set the value of any type that conforms to `ExpressibleByIntegerLiteral`).

- Dave Sweeris

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

That's a great choice :wink: — if I had the time, this would be the Swift-feature I'd spend it for…
I hope you have the stamina to finish that, and although it's no real help for implementation, I have some remarks that might be useful in the long run.

There are already some thoughts on the topic online (afair even core had something to say about it — I can try to find it if you didn't do so already ;-).
Your Vector example illustrates one problem: "T: X" can now have two different meanings; either T is a subtype of X, or T is a literal of type X.
Even if the compiler can figure out what meaning to chose, it would help humans to have a slightly different syntax.

There's also the idea of labeled generic parameters, and default values for them.
Both aren't required, but imho at least the labels might be something that people request before another kind of generics is accepted.

struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

I have strong hope that literal generics will form the basis for fixed-size vectors, so that there will be no need to define such a type :wink: — but besides that:
Why is T restricted here? Even if it should be a numeric type, I would allow floats as well.

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start, until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Even without such calculations, the feature would be quite useful:
For type-safe matrices, it would already be enough to have the dimensions attached to the type.

Hi everyone! I'm trying to implement literal values as generic types.

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

No, I mean so that a vector's or matrix's dimensions can be part of its type (strawman syntax and protocol name, but this is pretty much what I'll be trying to support, at least at first):

I think instead of modeling these generic parameters as types, you should look into generalizing GenericSignatures to contain ‘literal requirements’. Right now, we have four kinds of requirements:

- A is a subclass of B
- A conforms to B
- A is the same type as B
- A has a known layout

All of these except for the last one have a generic parameter as their right hand side. All of them have a generic parameter on their left hand side. I think what you want is to add a new ‘value parameter’ that is not a type, but instead has a value. Requirements would be placed on these to constrain them to known kinds of literals (integers, strings, etc).

Thanks, will do!

struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start,

The compiler’s constant folding operates on SIL instructions, not Exprs directly. However constant folding is not generally what you want for this, because constant folding is a ‘best effort’ kind of optimization (it may or may not fold your calculation down to a constant) and also it produces code that evaluates the result (even if its a constant) and not the result itself.

Yeah, I didn't mean I was going to literally use constant-folding (is that a pun? The world may never know...) for exactly the reasons you mentioned. I just meant "the kinds of expressions that constant-folding would reasonably be expected to probably optimize". The only two that I think are pretty much necessary are "+" and "-" on integer literals, so that functions can combine and break-up types like "vector" and "matrix". String concatenation with "+" seems pretty reasonable, too. Speaking of which...

I think if you want to explore type-level computation like this, you should define a very small subset of the language which can be computed by the type checker like this.

I do want to do that. My initial thoughts on the matter is that such a subset would probably essentially be "any function that can be evaluated a compile-time and returns a type", but then that requires having a good notion of "pure" / "constexpr" / "whateverWeCallIt", and I'm not sure I want to open two cans of worms at once.

That said, I'm a huge fan of the approach the core team has taken WRT defining everything in the stdlib and minimizing "compiler magic". It might be more than I want to bite off in a single proposal, but getting the supported functions out of the compiler and allowing user-defined functions to get a type is an obvious next step.

- Dave Sweeris

···

On Aug 29, 2017, at 14:31, Slava Pestov <spestov@apple.com> wrote:

On Aug 29, 2017, at 2:21 PM, David Sweeris <davesweeris@mac.com> wrote:

On Aug 29, 2017, at 1:49 PM, Slava Pestov <spestov@apple.com> wrote:
On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org> wrote:

Hi David,

To add to what I wrote earlier, the design of the Swift is such that adding new features isn’t “hard”, but it does require time and patience because most new features have ripple effects on other parts of the compiler that need to be thought through. Fortunately, the design of the compiler is such that vast majority of those ripple effects will cause a compile time failure. This is why I encouraged you to add new “.def” nodes up front. That’s the good news. The bad news is that you still need to think through the ripple effects and that means developing some minimal design and debugging expertise over the compiler as a whole. If you’re not discouraged by the process, the experience is quite rewarding.

Not from that, no. All the meta-programming stuff in the .def files (and the enums that reference them) is still hard for me to follow, but I'll figure it out, and it takes more than mere confusion to discourage me. :slight_smile:

Honestly, the relative difficultly of getting a working dev environment going is *way* worse than trying to figure out how to implement a feature...
Somewhere along the line today I synced my repo with Apple's to make sure nobody had refactored the type system or something while I wasn't looking, and unknown to me until a few minutes ago, the newest version requires Xcode beta 6 to build, which requires macOS 10.12.6. Well, I'm still on 10.12.5 because 10.12 doesn't support my 2008 Mac Pro at all (even though everything except the wifi works perfectly... in fact better than 10.11, at least for me), so updating the OS is a huge hassle... you've gotta jump through a bunch of hoops, else you'll end up with a boot volume which has incorrect (yet strongly held) views on whether your computer can boot off of it. Having stuff suddenly fail to compile after you've been mucking in files that you don't understand yet (*.def) -- even after saving copy of the files you were editing then telling Xcode to discard all changes -- is enough to make you think you somehow messed something up. Then you delete everything in the "sources" directory, and spend 3-4 hours or so starting over with an attempt at getting a clean install/build, only to notice halfway through that it's not going to work because now the compiler needs a newer version of Xcode (does it really, or does it only need the newer command line stuff?).

Anyway, not knowing an easy way to tell if the problem is in your code or the dev environment, and not knowing how to fix the problem if it is in the environment (short of spending 3+ hours hoping that following the directions from the top will make everything happy) -- that can get discouraging.

One more tip: if you’re going to add a new language feature, focus first on the type system and writing tests. Once you feel that implications are fully thought through, you can move on to “code gen” (i.e. SILGen and IRGen). If you rush to “code gen” first so that you can play with your shiny new feature, then you’re likely to get frustrated by self-inflicted bugs. You can experience the blissful pre-code-gen world by invoking swift like so: swift -frontend -typecheck file.swift

Sounds like good advice... I think I'll experiment with that tomorrow, after I figure out how I'm going to get beta 6 running.

- Dave Sweeris

···

On Aug 29, 2017, at 15:47, David Zarzycki <zarzycki@icloud.com> wrote:

That's a great choice :wink: — if I had the time, this would be the Swift-feature I'd spend it for…

Yeah, I remember your enthusiasm for it back when the topic first came up :slight_smile:

I hope you have the stamina to finish that

lol, me too!

and although it's no real help for implementation, I have some remarks that might be useful in the long run.

There are already some thoughts on the topic online (afair even core had something to say about it — I can try to find it if you didn't do so already ;-).
Your Vector example illustrates one problem: "T: X" can now have two different meanings; either T is a subtype of X, or T is a literal of type X.
Even if the compiler can figure out what meaning to chose, it would help humans to have a slightly different syntax.

Maybe, yeah... I'm trying to make it so that the literal value itself is what's conforming to X (which is why this feature is only about literal values, and not arbitrary values of a type that conforms to the relevant `ExpressibleBy*Literal` protocol), to preserve the "untyped" nature of literal values. This would let us simply use the "type" by itself (like in the init body from the original Vector example) wherever an integer literal would work. How such a protocol would be implemented is something I haven't worked on yet, but I'll probably start by taking a look at how the existing `ExpressibleBy*Literal` protocols work. Fully implementing this (by which I mean allowing for adding generic literals like in the "join" function) would require some way of having the compiler track which identifiers can be evaluated at compile-time and which are strictly run-time objects, which is why I think this is so closely tied to what C++ calls "constexpr".

Failing that, plan B is to write a protocol something like:
protocol IntegerLiteralExpr {
  static var value: IntegerLiteralType {get}
}
which would change the init function in my example to;
  init() {
    elements = [T](repeating: 0, count: L.value)
  }

There's also the idea of labeled generic parameters, and default values for them.
Both aren't required, but imho at least the labels might be something that people request before another kind of generics is accepted.

I might start on those next, if nobody else has done it before I get this feature working. No promises, though... not until I've gone through the process once and have a better idea of what I'm getting into.

struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

I have strong hope that literal generics will form the basis for fixed-size vectors, so that there will be no need to define such a type :wink: — but besides that:
Why is T restricted here? Even if it should be a numeric type, I would allow floats as well.

Floating point types do conform to `ExpressibleByIntegerLiteral`. Unless you meant to ask why `L` was restricted, in which case it's simply that Vector/Matrix types are my main motivation, so I started with integers. My intention is to implement it in such a way that any literal value will work, even, say, a hypothetical "ClosureLiteralExpr", "let x = Foo<let bar = whatever(something)>()". Anyway, the point is that I very much don't want to tie this feature's implementation to the existing set of literal expressions. As a more likely example, given how the evolution discussions were going, it seems reasonable to think it's possible we might get a RegExLiteral some day. IIUC (which is far from certain), in the worst case, I could just store the relevant characters of source code. Come to think of it, I might want to do that even for integer literals, if that makes it easier to preserve their "literalness" in the type system, but I'll cross that bridge when I come to it.

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start, until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Even without such calculations, the feature would be quite useful:
For type-safe matrices, it would already be enough to have the dimensions attached to the type.

Agreed, but it'd be a pretty severe limitation, IMHO, to not be able to split/join values of such types.

- Dave Sweeris

···

On Aug 30, 2017, at 8:15 AM, Tino Heth <2th@gmx.de> wrote:

Thanks! I'll keep this in mind when it's time to implement it.

- Dave Sweeris

···

On Aug 30, 2017, at 8:01 AM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Aug 29, 2017, at 5:22 PM, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

On Aug 29, 2017, at 1:49 PM, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Aug 29, 2017, at 11:03 AM, David Sweeris via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

Hi everyone! I'm trying to implement literal values as generic types.

Can you briefly explain what you mean by this?

Are you referring to let-polymorphism, like

let fn = { $0 }
let f1: (Int) -> Int = fn
let f2: (Float) -> Float = fn

No, I mean so that a vector's or matrix's dimensions can be part of its type (strawman syntax and protocol name, but this is pretty much what I'll be trying to support, at least at first):
struct Vector<T: ExpressibleByIntegerLiteral, L: IntegerLiteralExpr> {
  var elements: [T]
  init() {
    elements = [T](repeating: 0, count: L)
  }
}

let vect = Vector<Int, 5>()

And, once that's working, I'm going to add support simple "type functions":
func join <T, L1, L2> (_ lhs: Vector<T, L1>, _ rhs: Vector<T, L2>) -> Vector<T, L1 + L2 > {...}
I think restricting the supported "type functions" to expressions that could be evaluated by the compiler's "constant folding" code would be a reasonable place to start, until we figure out what we want to do about "pure"/"constexpr" stuff... even just "+" for numeric and string literals, and "-" for numeric literals, seems like a reasonable starting goal, and I think that'd be simple enough to implement (famous last words, right?)... It's all academic until I get the simple cases working first, though.

Constant folding occurs too late in the compilation process for you to take advantage of it in the solver. You’ll need to write your own evaluator and teach CSSimplify to invoke it during the type matching process. I would start by adding a new kind of Type and TypeRepr that hold SequenceExpr nodes and a CanType that holds an APSInt representing the result of evaluating the expression.