DynamoDS / DesignScript

About the DesignScript language
Apache License 2.0
11 stars 11 forks source link

Imperative block syntax, function definition syntax #11

Open pboyer opened 8 years ago

pboyer commented 8 years ago

There is a case to be made for [Imperative] attributes for function definitions, e.g.:

[Imperative]
def : Number boolToNum( val : bool ) {
  if ( val ) { return = 1;} else { return = 0; }
}

This could also clearly be inferred.

This is an assigned imperative block, with proposed dependency capture vars:

val = true;
a = [Imperative]( val ){
  if ( val ) { return = 1;} else { return = 0; }
}
// a == 1

An assigned imperative block is thus a lot like an immediately evaluated anonymous function, in proposed syntax:

a = ([Imperative] lambda : Number ( val : bool ){ if ( val ) { return = 1;} else { return = 0; } )( false )
// a == 1

However, this form actually is more powerful, as the type declaration allows for replication to be performed:

a = ([Imperative] lambda : Number ( val : bool ){ if ( val ) { return = 1;} else { return = 0; } )( {false, true} )
// a == { 0, 1 }

IOW, the user can specify whether the the captured variables should be interpreted as a compound or singular value.

Perhaps, we can modify the Imperative block capture list to include a type annotation:

a = [Imperative]( val : bool ){ if ( val ) { return = 1;} else { return = 0; }

@lukechurch

ke-yu commented 8 years ago

I'd suggest that function doesn't have return type, or always be var[]..[], or the return type is just for annotation, instead of doing type coercion for return value.

lukechurch commented 8 years ago

@pboyer I think considering applying [Imperative] to other things than 'bare blocks' is a good idea. Where it's reasonable, it's really just an annotation on the structure that adjusts the semantics.

I'm very cautious about the UX impact of inference. It makes code terse to write, but difficult to read and can cause unexpected behaviour changes. What happens if the first statement of a function isn't imperative? Do you switch the whole function to imperative because of a single statement?

In general in user studies elsewhere we've seen that inference only works well when it's very local, predictable and has good tool support to help the user reason about what is going on.

I like the direction the anonymous functions are going in, but find the examples a little hard to read above and I'm not overly keen on introducing the word lambda :) I think the () are getting over-used for too many different things as a syntactic construct.

lukechurch commented 8 years ago

@ke-yu Functions having return types is UX helpful - it helps people know what they're going to get back from the function.

If the function type is an annotation only and gets ignored at runtime, now you potentially a function that says one thing and actually does another. That seems like it leads to confusions.

lukechurch commented 8 years ago

BTW @pboyer Do feel free to assign things like this to me if you're asking me a question - or drop me a note. I didn't reply for such a long time because the notification got lost in the noise of my inbox. Sorry about that.

pboyer commented 8 years ago

@lukechurch I just put your name on it if you had any thoughts. Task assignment seems so formal...

The syntax for lambda was more speculative than anything. I simply wanted to demonstrate that such a syntax is more powerful than imperative blocks (without having type annotations on captured variables). It's a bit odd though, as that means putting a type annotation on an already defined variable...

ke-yu commented 8 years ago

@pboyer lambda could be more general

a = ( val : bool ){ [Imperative] {if ( val ) { return = 1;} else { return = 0; }}
pboyer commented 7 years ago
[Imperative] 
def foo(){

}

could be compiled to

def foo() {
  return [ Imperative ] {

  }
}