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

Lambda as a top-level data type #15387

Open
gaorlov opened this issue Oct 21, 2024 · 0 comments
Open

Lambda as a top-level data type #15387

gaorlov opened this issue Oct 21, 2024 · 0 comments
Assignees
Labels
enhancement New feature or request Needs: Triage 🔍

Comments

@gaorlov
Copy link

gaorlov commented Oct 21, 2024

Is your feature request related to a problem? Please describe.

I maintain a set of modules that export a set of types that overlap in their interfaces, both in terms of data structure and functionality.
While I can declare the type data structures in a way that lets me use "duck typing" to unify them:

// interface.bicep
type MyInterface = {
  sharedField: string
}

// a.bicep
type A = {
  sharedField: string
  aField: bool
}

// b.bicep
type B = {
  sharedField: string
  bField: object
}

Which allows me to pass either an A or a B into a method that expects an Interface. And if i'm missing a field it expects, i will get errors letting me know that i'm passing in an incompatible object.

func interfaceCall(interface MyInterface) any => doStuff(interface.sharedField)

var a = {
  sharedField: 'a'
  otherField: false
}

// success
interfaceCall(a)

// build errors
interfaceCall({})

However, if I want Interface to define not just the properties, but also member methods, there's really not a way to do this today. There's no way to express that the interface expects a method to be present. Best I can do is to export the same function name in all my interface-bound modules.

// a.bicep
type A = { /* same as above*/ }

@export()
func interfaceCall(interface MyInterface) any => // A-specific operation

// b.bicep
@export()
type B = { /* same as above*/ }

@export()
func interfaceCall(b B) any => // B-specific operation

But now, I have to know what import brought in what functions in order to call them

// main.bicep
import * as A from './a.bicep'
import * as B from './b.bicep'

param MyInterface x
param B b

x.interfaceCall(x) // syntax error

A.interfaceCall(x) // maybe correct? I don't actually know what x is
B.interfaceCall(b) // correct

Describe the solution you'd like

I would love to hear what the thoughts are on adding lambdas to the top-level data type set, since they are already present in the built-in functions like map.

  • signature could be lambda(paramType*) outputType
  • same limitations as func would be reasonable:
    • no var references
    • no resource references

Using this proposal I can express the examples above as

// interface.bicep
@export()
type MethodType = lambda(MyInterface) any

@export()
type Type = {
  sharedField: sharedFieldType
  method: MethodType 
}

// a.bicep
import * as Interface from './interface.bicep'

type A = {
  sharedField: string
  method: Interface.MethodType 
  aField: bool
}

func interfaceCall(a A) any => // A-specific operation

@export()
func new(o object) A => union({method: interfaceCall}, o)

// b.bicep
import * as Interface from './interface.bicep'

type B = {
  sharedField: string
  method: Interface.MethodType 
  bField: object
}

func interfaceCall(b B) any => // B-specific operation

@export()
func new(o object) B => union({method: interfaceCall}, o)


// main.bicep
import * as A from './a.bicep'
import * as B from './b.bicep'

module _ './my-module.bicep' = {
  name: '_'
  params:{
    interface: A.new( {
      sharedField: 'a'
      aField: true
    })
  }
}

// my-module.bicep
import * as Interface from './interface.bicep'

param interface Interface

// doesn't have to know what concrete type the object is
var result = interface.method(interface)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Needs: Triage 🔍
Projects
Status: Todo
Development

No branches or pull requests

2 participants