Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for user defined functions #10465

Merged
merged 19 commits into from
May 1, 2023
Merged

Support for user defined functions #10465

merged 19 commits into from
May 1, 2023

Conversation

anthony-c-martin
Copy link
Member

@anthony-c-martin anthony-c-martin commented Apr 18, 2023

See https://github.com/Azure/bicep/blob/ant/exp/func/src/Bicep.Core.Samples/Files/Functions_LF/main.bicep for a syntax example.

Notes/limitations:

  • UDFs in JSON require declaring the return type, so I've modified the spec to require the user to declare the return type.
  • User-defined types in UDF params & outputs are unsupported.
  • UDFs are currently quite limited - they can't call UDFs, or access outer scoped variables.
  • This is currently behind an experimental feature flag ("userDefinedFunctions")

Closes #447
Closes #9239

Microsoft Reviewers: Open in CodeFlow

@dciborow
Copy link
Collaborator

Here is an example of something I would replace with a UDF. I use this to generation the connection string for Cassandra DB, which includes each region.

param location string
param secondaryLocations array = []

var secondaryRegions = [for (region, i) in secondaryLocations: {
  locationName: contains(region, 'locationName') ? region.locationName : region
  failoverPriority: contains(region, 'failoverPriority') ? region.failoverPriority : i + 1
  isZoneRedundant: contains(region, 'isZoneRedundant') ? region.isZoneRedundant : isZoneRedundant
}]

var locations = union([
    {
      locationName: location
      failoverPriority: 0
      isZoneRedundant: isZoneRedundant
    }
  ], secondaryRegions)


var unwind = [for location in locations: '${toLower(cassandraName)}-${location.locationName}.cassandra.cosmos.azure.com']
var locationString = replace(substring(string(unwind), 1, length(string(unwind))-2), '"', '')

@jeskew
Copy link
Contributor

jeskew commented Apr 19, 2023

  • We don't support user-defined types in UDF params & outputs. Should we block this, or attempt to support it?

It would be a small-ish change in the ARM runtime to support user-defined types, and if it were up to me, I would rather put in the effort to close that feature gap than document and field questions about the limitation.

But it did get me thinking: because types can contain symbolic references to other types, I can't think of any reason we would allow user-defined types and still disallow references to variables and parameters. Should we look at removing that restriction on UDFs, too?

@anthony-c-martin
Copy link
Member Author

But it did get me thinking: because types can contain symbolic references to other types, I can't think of any reason we would allow user-defined types and still disallow references to variables and parameters. Should we look at removing that restriction on UDFs, too?

I think so - I just wanted to keep the initial implementation as simple as possible. One complexity is that UDFs overloads parameters(...) to refer to parameters of the UDF and not parameters of the template. We'd most likely need to introduce scoping rules to deal with this.

@anthony-c-martin anthony-c-martin changed the title [WIP] Support for user defined functions Support for user defined functions Apr 26, 2023
@anthony-c-martin anthony-c-martin marked this pull request as ready for review April 26, 2023 11:24
docs/grammar.md Outdated Show resolved Hide resolved
@@ -60,44 +60,53 @@ module moduleWithConditionAndSelfCycle './main.bicep' = if ('foo' == 'bar') {

module './main.bicep' = {
//@[007:021) [BCP096 (Error)] Expected a module identifier at this location. (CodeDescription: none) |'./main.bicep'|
//@[007:021) [BCP094 (Error)] This module references itself, which is not allowed. (CodeDescription: none) |'./main.bicep'|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were these new diagnostics intentional?

src/Bicep.Core/Syntax/TypedLambdaSyntax.cs Outdated Show resolved Hide resolved
src/Bicep.Core/TypeSystem/DeployTimeConstantValidator.cs Outdated Show resolved Hide resolved
docs/grammar.md Show resolved Hide resolved
@@ -48,6 +48,8 @@ public static EmitLimitationInfo Calculate(SemanticModel model)
BlockModuleOutputResourcePropertyAccess(model, diagnostics);
BlockSafeDereferenceOfModuleOrResourceCollectionMember(model, diagnostics);
BlockCyclicAggregateTypeReferences(model, diagnostics);
BlockUserDefinedFunctionsWithoutExperimentalFeaure(model, diagnostics);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BlockUserDefinedFunctionsWithoutExperimentalFeaure

This is certainly a convenient place to put these checks, but I wonder if we need to rename this class. (Blocking UDFs if the experimental feature is disabled isn't really due to JSON limitations.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, it's a convenient place to put any sort of ad hoc check. Will raise an issue to rename it.

@anthony-c-martin anthony-c-martin merged commit 23ebbef into main May 1, 2023
@anthony-c-martin anthony-c-martin deleted the ant/exp/func branch May 1, 2023 22:23
@SimonWahlin
Copy link
Collaborator

🤩🎉🚀

@ayoussef-insight
Copy link

Support to outer scoped variables and include/import feature would be very useful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal - Support user defined functions in Bicep Support for functions
6 participants