fsharp / fslang-design

RFCs and docs related to the F# language design process, see https://github.com/fsharp/fslang-suggestions to submit ideas
520 stars 143 forks source link

Support for TypeProvider nested types with static parameters #88

Closed JasonKleban closed 8 years ago

JasonKleban commented 8 years ago

Can we please track support for TypeProviders providing nested types with static parameters? It seems to me it would be valuable for metaprogramming similar to how static parameterized method support is valuable compared to the language without them. It would allow a fluent-like construction of types and partially make up for the non-variable length static parameters on a single type. Is there a complicated reason that nested parameterized types cannot be supported?

You wrote "Things generally get very nasty when you have a primary set of static parameters on a type" but I'm not sure whether this refers to this support which I'm asking about. I think you're just saying that a statically-parameterized method returning a type dependent on its parameters can get complicated to think about.

An example: Internal DSLs for defining types from other languages can take advantage of the nesting type provision to label different components that aren't clearly delineated in the source language. In this example of a fictitious type provider using this fictitious feature, the Extend schema definitions can be split up as shown but could just as well be combined for defining the types in the domain, but the RequireZero domain invariant specifications which communicate individual domain invariants can need multiple statements and could not clearly be delineated in the source language without comments or manifest files referencing other files or something obnoxious like that.

type myDomain = 
    EmptyDomain
    .Extend<"CREATE TABLE Customers ...">
    .Extend<"CREATE TABLE Charges ...">
    .RequireZero<"SELECT ...", "Rule 1", "Customers may not share an email address with more than 3 other Customers.">
    .RequireZero<"SELECT ... INTO @Intermediate; SELECT TOP 1 1 FROM @Intermediate", "Rule 2", "A Customer's Charges may not total more than the Customer's CreditLimit.">

let updateCustomerName custId name= 
    myDomain.OpDefine<"UPDATE Customer SET Name= @fullname WHERE Id = @Id">(name, custId)
let updateCustomerEmail custId address = 
    myDomain.OpDefine<"UPDATE Customer SET Email = @email WHERE Id = @Id">(address, custId)
let addCharge custId amount description = 
    myDomain.OpDefine<"INSERT INTO Charges ...">(custId, amount, description)
let chargeEverybody amount description = 
    myDomain.OpDefine<"INSERT INTO Charges (...) SELECT @amount, @desc FROM Customer">(amount, description)
let adjustLimit custId delta= 
    myDomain.OpDefine<"UPDATE Customer SET CreditLimit = CreditLimit + @delta WHERE Id = @Id">(delta, custId)

updateCustomerEmail 2823 "Jim Smith" // Emits only the original update - no rules are possibly violated
updateCustomerEmail 2823 "no@thanks.com" // Emits the original update plus some assertion sql for Rule 1 because of potential impact
adjustLimit 2823 -500.00 // Emits the original update plus some assertion sql for Rule 2 because of potential impact
addCharge 2823 630.00 "Oculus Rift" // Emits the original update plus some assertion sql for Rule 2 because of potential impact
chargeEverybody 5.00 "Membership Fee" // Emits the original update plus some assertion sql for Rule 2 because of potential impact

// analysis and embellished sql scripts are generated at compile time.  Type safe sql statement 
// parameterization included.  Domain invariants are enforced.  Can also generate and use 
// deployed stored procs for the benefit of compiled execution plans.

The current alternative is type myDomain = Domain<"CREATE TABLE Customers ... CREATE TABLE Charges ...", "SELECT ... -- RESERVED_DELIM SELECT ... INTO @Intermediate; SELECT @Intermediate"> but this is inconvenient, even if the literal script were instead a file with those contents.

dsyme commented 8 years ago

@JasonKleban Thanks for the extensive writeup. For now this should really be added to http://fslang.uservoice.com.

My main follow-up question is if you can get the effect you want purely through provided parameters on methods?

JasonKleban commented 8 years ago

can get the effect you want purely through provided parameters on methods?

@dsyme - I don't think so - this example translates parameterized sql into function parameters. To gain type safety for those operations, this information needs to be available at compile-time and be part of the types.

dsyme commented 8 years ago

@JasonKleban Thanks - can you add a link to the UV request please?

JasonKleban commented 8 years ago

https://fslang.uservoice.com/forums/245727-f-language/suggestions/14558223-support-for-typeprovider-nested-types-with-static