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 named parameters at function call sites (kinda like Objective-C) #4507

Closed
raould opened this issue Aug 28, 2015 · 20 comments
Closed
Assignees

Comments

@raould
Copy link

raould commented Aug 28, 2015

https://groups.google.com/d/topic/haxelang/muY28cCQ8-g/discussion

Given a function/method signature like

function foobar( a:Int, b:String ):Float { ... }

we would normally call it like this:

var f = foobar( 4, "foo" );

I would additionally like to be able to name the arguments with exactly the names & order used in the signature.

var f = foobar( a:4, b:"foo" );

I am not seeking anything else. E.g. allowing the parameters to appear in different orders is to me a future enhancement to do later on top of this basic feature request. :-) Also, that is just my guess at what the syntax could be, ideally.

@andyli andyli assigned andyli and ncannasse and unassigned andyli Aug 28, 2015
@cristibaluta
Copy link

Good proposal, i'm used like this from objc and swift, i forgot my own Haxe code what is calling in the meantime. This will bring some clarity to code that will become old in the future. Hope this doesn't slow down the compiler too much if implemented, because the workarounds surely do slowdown the runtime.

@nadako
Copy link
Member

nadako commented Aug 28, 2015

I like the idea, but it seems to me that there's a consitency problem with function types:

function foobar(a:Int, b:String) { ... }
var f:Int->String = foobar;
f(???); <- argument names are lost

C# solves this issue quite elegantly since their delegate types have named arguments. Maybe if we allow names in function type syntax var f:a:Int->b:String = foobar this would be the same.

@ncannasse
Copy link
Member

Fist, and most importantly, we cannot use the : syntax since it's already used in Haxe, (foo() : Int)

Also, it is considered a good practice if you have named args to forbid developers from using unamed args when several of them have the same type, in order to to make sense of foo(1,2,true,5,6,false). I don't want to encourage developers to write such functions by making them think that users will used named args, and I don't want to enforce the usage of named args either.

I think using a typedef with optional fields if required is already a quite good alternative, and it has the additional bonus of being able to pass the whole "arguments" to sub methods without forgetting any.

@jcward
Copy link
Contributor

jcward commented Aug 28, 2015

I want named parameters, but I disagree with the OP's request (emphasis mine):

I would additionally like to be able to name the arguments with exactly the names & order used in the signature.

If they have to be fully enumerated and in order, you lose the obvious named parameter benefit of "I only want to specify 3 of 20 optional parameters, in any order, without building a typedef to do it."

Just now @ncannasse's comment came in...

Writing typedef's is fine, but it means I have to own the code (and change my signatures), or write wrappers around library code and re-express the default values of the signature in my wrapper.

Simply stated: I want to use library functions without knowing the order or default values of the parameters.

e.g. openfl.text.TextFormat:

public function new (font:String = null, size:Null<Int> = null, color:Null<Int> = null, bold:Null<Bool> = null, italic:Null<Bool> = null, underline:Null<Bool> = null, url:String = null, target:String = null, align:TextFormatAlign = null, leftMargin:Null<Int> = null, rightMargin:Null<Int> = null, indent:Null<Int> = null, leading:Null<Int> = null)

So I want: var tf = new TextFormat(font:"DroidSans", leading:5, underline:true)

@nadako
Copy link
Member

nadako commented Aug 28, 2015

@jcward what's wrong with the wrapper?

@nadako
Copy link
Member

nadako commented Aug 28, 2015

@ncannasse I added a link to your comment to my "awesome-haxe" link list here: https://github.com/nadako/awesome-haxe

@jcward
Copy link
Contributor

jcward commented Aug 28, 2015

Let's think through the wrapper. I write:

typedef TextFormatOptions = { // Moar writing
  @:optional var font:String;
  @:optional var size:Null<Int>;
  ...
}

class MyUtilClass { // Still more writing
  public static function namedTextFormat(args:TextFormatOptions):TextFormat
  {
    // Do I have to re-express defaults here if they're non-null? e.g. Imagine size
    // had a default of 12...
    return new TextFormat(args.font, args.size==null ? 12 : args.size, ...);
  }
}

LMK if I've done anything dumb.

To me, this is a ton of writing, and a custom solution that reduces portability of my code.

@nadako
Copy link
Member

nadako commented Aug 28, 2015

But that can be auto-generated with macros

@jcward
Copy link
Contributor

jcward commented Aug 28, 2015

Apparently, which is why I plan on looking at Brendon's named macro.

But now you've limited "named parameters" to a small sub-set of Haxe users -- namely, those who know what macros are and how to use them.

@nadako
Copy link
Member

nadako commented Aug 28, 2015

Well, we're talking about extern library, aren't we? An extern maker can integrate that macro in their library, I think.

@jcward
Copy link
Contributor

jcward commented Aug 28, 2015

Perhaps, but now we have fragmentation / varying user experience, and new users won't understand why. Which honestly seems prevalent in Haxe, and one reason OpenFL is so popular is that @jgranick cares about the total user experience (so it's understandable that he forked hxcpp at one point -- not great for the haxe community, but great for his new users.)

But I suppose that's the crux. In open source, you either share and agree, or splinter, over-reach, and duplicate effort. Sadly, agreeing is usually too hard.

@Gama11
Copy link
Member

Gama11 commented Aug 28, 2015

I don't mind typedefs, they're great, but don't they incur a severe performance loss on targets like cpp if used a lot?

From http://old.haxe.org/manual/struct:

However for platforms that after a strictly typed runtime, such as Flash, C++, C# and Java, each access to a structure field will perform a dynamic lookup. Structures will then be slower than classes instances on these platforms. However you shouldn't worry to much about it unless you write performance-critical code.

I guess the question there is - how much slower, approximately?

@nadako
Copy link
Member

nadako commented Aug 28, 2015

I don't mind typedefs, theye're great, but don't they incur a severe performance loss on targets like cpp if used a lot?

I think one could write a macro function that analyzes "object declaration" expressions and generates code that simply passes arguments to the function call without actually creating an object. Also I wonder if this could be automatically inlined.

@jcward
Copy link
Contributor

jcward commented Aug 28, 2015

Juraj posted an example with an inline helper -- I'm not sure if this provides the same level of optional parameters and default values that typedef does... Maybe if it's handled by a macro it can be worked out regardless.

@kevinresol
Copy link
Contributor

I think one could write a macro function that analyzes "object declaration" expressions and generates code that simply passes arguments to the function call without actually creating an object. Also I wonder if this could be automatically inlined.

That is feasible, but I think that still suffers from the problem of function declared in the form Int->Int

@nadako
Copy link
Member

nadako commented Aug 28, 2015

most importantly, we cannot use the : syntax since it's already used in Haxe, (foo() : Int)

BTW about that, Can we get rid of mandatory parens and define operator precedence for ternary already? @ncannasse

@ncannasse
Copy link
Member

@nadako that would cause some kind of syntax conflict with ternary operator

@nadako
Copy link
Member

nadako commented Aug 29, 2015

Yes, that's why I was talking about defining operator precedence for ETypeCheck to avoid ambiguity. E.g. a : Bool ? b : Int : Float could be defined to be if (a : Bool) { (b : Int) } else { Float }. It's not like typecheck and ternary are used together that frequently to cause the precedence question anyway (and for those cases one can still use parens).

@kevinresol
Copy link
Contributor

How about the as keyword?

@ncannasse
Copy link
Member

@nadako That would as assume that you have (expr : expr) while we currently parse (expr : type)

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

No branches or pull requests

8 participants