www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Optional parameters?

reply Steven Schveighoffer <schveiguy yahoo.com> writes:
I currently have a situation where I want to have a function that 
accepts a parameter optionally.

I thought maybe Nullable!int might work:

void foo(Nullable!int) {}

void main()
{
    foo(1); // error
    int x;
    foo(x); // error
}

Apparently, I have to manually wrap an int to get it to pass. In other 
languages that support optional types, I can do such things, and it 
works without issues.

I know I can do things like this:

void foo(int x) { return foo(nullable(x)); }

But I'd rather avoid such things if possible. Is there a way around 
this? Seems rather limiting that I can do:

Nullable!int x = 1;

but I can't implicitly convert 1 to a Nullable!int for function calls.

-Steve
Apr 01 2018
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2018-04-01 17:54, Steven Schveighoffer wrote:
 I currently have a situation where I want to have a function that 
 accepts a parameter optionally.
 
 I thought maybe Nullable!int might work:
 
 void foo(Nullable!int) {}
 
 void main()
 {
     foo(1); // error
     int x;
     foo(x); // error
 }
 
 Apparently, I have to manually wrap an int to get it to pass. In other 
 languages that support optional types, I can do such things, and it 
 works without issues.
 
 I know I can do things like this:
 
 void foo(int x) { return foo(nullable(x)); }
 
 But I'd rather avoid such things if possible. Is there a way around 
 this? Seems rather limiting that I can do:
 
 Nullable!int x = 1;
 
 but I can't implicitly convert 1 to a Nullable!int for function calls.
Yeah, D doesn't allow user defined implicit conversions, which I think is required for this. I would make function overloading even more complex than it is today. Although it would be really handy for cases like this. -- /Jacob Carlborg
Apr 01 2018
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/1/18 12:00 PM, Jacob Carlborg wrote:

 
 Yeah, D doesn't allow user defined implicit conversions, which I think 
 is required for this. I would make function overloading even more 
 complex than it is today.
 
 Although it would be really handy for cases like this.
 
Not necessarily implicit conversion, but implicit construction. The nicety with D's overloading rules are they are simple -- if there is an exact match, use it. If there is a conversion possible, use it. If multiple conversions are possible, ambiguity error. Adding another way to convert doesn't seem like it would cause lot's of complication or harm. -Steve
Apr 02 2018
prev sibling next sibling parent Alex <sascha.orlov gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }
For the first line, I had the same problem a while ago... https://issues.dlang.org/show_bug.cgi?id=15792
Apr 01 2018
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, April 01, 2018 11:54:16 Steven Schveighoffer via Digitalmars-d-
learn wrote:
 I currently have a situation where I want to have a function that
 accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
     foo(1); // error
     int x;
     foo(x); // error
 }

 Apparently, I have to manually wrap an int to get it to pass. In other
 languages that support optional types, I can do such things, and it
 works without issues.

 I know I can do things like this:

 void foo(int x) { return foo(nullable(x)); }

 But I'd rather avoid such things if possible. Is there a way around
 this? Seems rather limiting that I can do:

 Nullable!int x = 1;

 but I can't implicitly convert 1 to a Nullable!int for function calls.
You'll have to call nullable. D has no form of implicit construction. You can use alias this to define how to convert _from_ a type but not _to_ a type, and alias this is the only way to define implicit conversions in D. I think that it works with variable initialization, because on some level, the compiler treats Type a = args; the same as auto a = Type(args); e.g. struct S { int _i; this(int i) { _i = i; } } void main() { S s = 42; } compiles with no alias this at all. Curiously though, if you remove the explicit constructor, it doesn't compile, even though auto s = S(42); would still compile. Another area where this behavior can be annoying is when returning from a function call. e.g. this won't compile: Nullable!int foo(int i) { if(i != 42) return i; return Nullable!int.init; } i needs to be wrapped in a call to nullable or to Nullable!int's constructor in order for it to compile. As I understand it, the lack of ability to define implicit construction is part of the attempt to avoid some of the problems with regards to stuff like function hijacking that come in C++ from allowing all of the implicit conversions that it allows. It may also be in part to prevent issues related to being able to define the same implicit conversion multiple ways (e.g. if type A implictly casts to B, and you can implicitly construct B from A, which conversion does the compiler use when converting A to B?). Ultimately, it's a bit of a double-edged sword in that it prevents certain classes of bugs but also makes it impossible to do something like have a function parameter be a wrapper type while the function argument is the type being wrapped. So, you couldn't do something like use string for IP addresses everywhere in your code and then change it to a struct later, and have all of the function calls that passed strings still work without updating them (which you can do in C++). Given how problematic implicit conversions tend to be in generic code, I often think that we might be better off with no user-defined implicit conversions in D at all, but Nullable is one case where the fact that we can't define implicit construction gets annoying. - Jonathan M Davis
Apr 01 2018
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/1/18 6:01 PM, Jonathan M Davis wrote:
 On Sunday, April 01, 2018 11:54:16 Steven Schveighoffer via Digitalmars-d-
 learn wrote:
 I currently have a situation where I want to have a function that
 accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
      foo(1); // error
      int x;
      foo(x); // error
 }

 Apparently, I have to manually wrap an int to get it to pass. In other
 languages that support optional types, I can do such things, and it
 works without issues.

 I know I can do things like this:

 void foo(int x) { return foo(nullable(x)); }

 But I'd rather avoid such things if possible. Is there a way around
 this? Seems rather limiting that I can do:

 Nullable!int x = 1;

 but I can't implicitly convert 1 to a Nullable!int for function calls.
You'll have to call nullable. D has no form of implicit construction. You can use alias this to define how to convert _from_ a type but not _to_ a type, and alias this is the only way to define implicit conversions in D. I think that it works with variable initialization, because on some level, the compiler treats Type a = args; the same as auto a = Type(args); e.g. struct S { int _i; this(int i) { _i = i; } } void main() { S s = 42; } compiles with no alias this at all.
This is my main reason for confusion -- it should work in all cases, not just this one.
 As I understand it, the lack of ability to define implicit construction is
 part of the attempt to avoid some of the problems with regards to stuff like
 function hijacking that come in C++ from allowing all of the implicit
 conversions that it allows. It may also be in part to prevent issues related
 to being able to define the same implicit conversion multiple ways (e.g. if
 type A implictly casts to B, and you can implicitly construct B from A,
 which conversion does the compiler use when converting A to B?).
This isn't that hard. You just define an order (obvious choice here is that implicit conversions win over construction).
 Ultimately, it's a bit of a double-edged sword in that it prevents certain
 classes of bugs but also makes it impossible to do something like have a
 function parameter be a wrapper type while the function argument is the type
 being wrapped. So, you couldn't do something like use string for IP
 addresses everywhere in your code and then change it to a struct later, and
 have all of the function calls that passed strings still work without
 updating them (which you can do in C++).
I'd be fine with a built-in option type, but we have delegated that to the library. But the library isn't up to the (complete) task. It makes things less pleasant, as it exposes a bit of internal implementation for the caller. While I get annoyed quite a bit with Swift's usage of optionals everywhere, they can make dealing with optional data much more concise and straightforward.
 Given how problematic implicit conversions tend to be in generic code, I
 often think that we might be better off with no user-defined implicit
 conversions in D at all, but Nullable is one case where the fact that we
 can't define implicit construction gets annoying.
I agree, they can be annoying. Sometimes you just want to write: a = b; and not worry about all the trouble this can cause. Phobos is littered with stuff like hasElaborateCopyConstructor, etc. But it's annoying that we still have to worry about it *and* we can't get all the benefits of having full-blown implicit conversion. -Steve
Apr 02 2018
prev sibling next sibling parent reply Boris-Barboris <ismailsiege gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.
I would simply use a pointer for this. Fighting D grammar seems too much of a hassle for such simple task.
Apr 01 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, April 01, 2018 22:06:57 Boris-Barboris via Digitalmars-d-learn 
wrote:
 On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer

 wrote:
 I currently have a situation where I want to have a function
 that accepts a parameter optionally.
I would simply use a pointer for this. Fighting D grammar seems too much of a hassle for such simple task.
How would a pointer help? Instead of doing foo(nullable(42)) he'd have to do foo(new int(42)) which is just one character shorter and ends up allocating on the heap, unlike with Nullable. - Jonathan M Davis
Apr 01 2018
parent reply Boris-Barboris <ismailsiege gmail.com> writes:
On Sunday, 1 April 2018 at 22:25:45 UTC, Jonathan M Davis wrote:

 How would a pointer help? Instead of doing

 foo(nullable(42))

 he'd have to do

 foo(new int(42))

 which is just one character shorter and ends up allocating on 
 the heap, unlike with Nullable.

 - Jonathan M Davis
foo(&x);
Apr 01 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, April 01, 2018 22:37:17 Boris-Barboris via Digitalmars-d-learn 
wrote:
 On Sunday, 1 April 2018 at 22:25:45 UTC, Jonathan M Davis wrote:
 How would a pointer help? Instead of doing

 foo(nullable(42))

 he'd have to do

 foo(new int(42))

 which is just one character shorter and ends up allocating on
 the heap, unlike with Nullable.

 - Jonathan M Davis
foo(&x);
which doesn't work in safe code and doesn't work when you have an rvalue as you would when passing 42. Ultimately, using pointers ultimately either requires explicitly allocating stuff on the heap to be able to pass rvalues, or it has the same restrictions that ref does in terms of passing rvalues. You can certainly take that approach if you'd like, but overall, I think that it's safe to say that using Nullable generally causes fewer problems. - Jonathan M Davis
Apr 01 2018
parent Boris-Barboris <ismailsiege gmail.com> writes:
On Sunday, 1 April 2018 at 22:44:45 UTC, Jonathan M Davis wrote:
 Which doesn't work in  safe code and doesn't work when you have 
 an rvalue as you would when passing 42. Ultimately, using 
 pointers ultimately either requires explicitly allocating stuff 
 on the heap to be able to pass rvalues, or it has the same 
 restrictions that ref does in terms of passing rvalues. You can 
 certainly take that approach if you'd like, but overall, I 
 think that it's safe to say that using Nullable generally 
 causes fewer problems.
1). There's nothing wrong with trusted. 2). Rvalue it trivially converted to lvalue on the stack using local variable. 3). You haven't shown syntax for passing null. Pointer is foo(null). Yours will probably be foo(nullable!int()); 4). I certanly wouldn't like typing nullable(...) for each optional parameter, I see it as a much bigger problem.
Apr 01 2018
prev sibling next sibling parent reply Seb <seb wilzba.ch> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }

 Apparently, I have to manually wrap an int to get it to pass. 
 In other languages that support optional types, I can do such 
 things, and it works without issues.

 I know I can do things like this:

 void foo(int x) { return foo(nullable(x)); }

 But I'd rather avoid such things if possible. Is there a way 
 around this? Seems rather limiting that I can do:

 Nullable!int x = 1;

 but I can't implicitly convert 1 to a Nullable!int for function 
 calls.

 -Steve
My workaround is to use struct initialization: --- import std.stdio, std.typecons; static struct FooConfig { Nullable!int a; } void foo(FooConfig optionalConfig = FooConfig.init) { optionalConfig.writeln; } void main() { foo(); FooConfig params = { a: 42, }; foo(params); //foo(FooConfig(42)); // <- hehe, no implicit conversion } --- https://run.dlang.io/is/HvN701 I know the separate line and variable is annoying. With the in-place struct-initialization DIP (https://github.com/dlang/DIPs/pull/71), it would become sth. like: foo(FooConfig({a : 42})); foo(FooConfig{a : 42}); (syntax is not clear yet and I still haven't gotten around implementing this in DMD)
Apr 01 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, April 01, 2018 22:34:16 Seb via Digitalmars-d-learn wrote:
 On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer

 wrote:
 I currently have a situation where I want to have a function
 that accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {

    foo(1); // error
    int x;
    foo(x); // error

 }

 Apparently, I have to manually wrap an int to get it to pass.
 In other languages that support optional types, I can do such
 things, and it works without issues.

 I know I can do things like this:

 void foo(int x) { return foo(nullable(x)); }

 But I'd rather avoid such things if possible. Is there a way
 around this? Seems rather limiting that I can do:

 Nullable!int x = 1;

 but I can't implicitly convert 1 to a Nullable!int for function
 calls.

 -Steve
My workaround is to use struct initialization: --- import std.stdio, std.typecons; static struct FooConfig { Nullable!int a; } void foo(FooConfig optionalConfig = FooConfig.init) { optionalConfig.writeln; } void main() { foo(); FooConfig params = { a: 42, }; foo(params); //foo(FooConfig(42)); // <- hehe, no implicit conversion } --- https://run.dlang.io/is/HvN701 I know the separate line and variable is annoying. With the in-place struct-initialization DIP (https://github.com/dlang/DIPs/pull/71), it would become sth. like: foo(FooConfig({a : 42})); foo(FooConfig{a : 42}); (syntax is not clear yet and I still haven't gotten around implementing this in DMD)
How is any of that better than just using nullable(42)? The whole annoyance here is that there is no implicit conversion and that something explicit is required. Changing what the explicit construction is doesn't help much, from where I sit, something like foo(FooConfig({a : 42:})); is way worse than foo(nullable(42)); and even if you're sticking to FooConfig, foo(FooConfig(42)); would be less verbose. The whole {a : 42} thing only starts making sense when you have a struct with several members where you want to be able to initialize only certain ones at a time without declaring all of the various constructors to allow all of the combinations and/or you have enough members of the same type that you pretty much need to provide the names with the arguments for it to be clear what's being initialized. Otherwise, normal construction works just fine, and it wouldn't help at all in a case like Steven has where he's trying to pass a type and have it implicitly converted to another when it's passed. If you're going to do something explicit, nullable(value) works just fine. It's the fact that something explicit is required at all that's the problem. - Jonathan M Davis
Apr 01 2018
prev sibling next sibling parent Ali <fakeemail example.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.
why not simply use function overloading?
Apr 01 2018
prev sibling next sibling parent Norm <norm.rowtree gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }

 Apparently, I have to manually wrap an int to get it to pass. 
 In other languages that support optional types, I can do such 
 things, and it works without issues.

 I know I can do things like this:

 void foo(int x) { return foo(nullable(x)); }

 But I'd rather avoid such things if possible. Is there a way 
 around this? Seems rather limiting that I can do:

 Nullable!int x = 1;

 but I can't implicitly convert 1 to a Nullable!int for function 
 calls.

 -Steve
I don't know if this helps but when I hit this situation I usually resort to templates, e.g. --- void foo(T)(T val = Nullable!int()) if(is(T : int) || is(T == Nullable!int)) { writeln(val); } void main() { foo(1); // prints: 1 int x; foo(x); // prints: 0 auto val = Nullable!int(5); foo(val); // prints: 5 foo(); // prints: Nullable.null } --- Cheers, Norm
Apr 01 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }
Can somebody enlighten me what this topic is about? I thought an optional parameter would be as easy as void foo(int i = 0) { writeln(i); } void main() { int x; foo(x); foo(1); foo(); } Is the Nullable!int approach because 'i' would always "optionally" be 0 if not passed with above 'foo'?
Apr 02 2018
next sibling parent Cym13 <cpicard openmailbox.org> writes:
On Monday, 2 April 2018 at 09:31:35 UTC, Timoses wrote:
 On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
 wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }
Can somebody enlighten me what this topic is about? I thought an optional parameter would be as easy as void foo(int i = 0) { writeln(i); } void main() { int x; foo(x); foo(1); foo(); } Is the Nullable!int approach because 'i' would always "optionally" be 0 if not passed with above 'foo'?
Same feeling here, this situation really asks for a Null Object pattern, not nullable. It's sad that nullable isn't very good in that situation but trying to force it in place doesn't seem very reasonnable.
Apr 02 2018
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/2/18 5:31 AM, Timoses wrote:
 On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer wrote:
 I currently have a situation where I want to have a function that 
 accepts a parameter optionally.

 I thought maybe Nullable!int might work:

 void foo(Nullable!int) {}

 void main()
 {
    foo(1); // error
    int x;
    foo(x); // error
 }
Can somebody enlighten me what this topic is about? I thought an optional parameter would be as easy as     void foo(int i = 0) { writeln(i); }     void main()     {         int x;         foo(x);         foo(1);         foo();     } Is the Nullable!int approach because 'i' would always "optionally" be 0 if not passed with above 'foo'?
I'm talking about optionals as they are in other languages, such as Swift: https://en.wikipedia.org/wiki/Option_type In other words, there is a distinct state of "not provided" from the other values it might take. Nullable works to fill this purpose, but I didn't realize that I'd have to wrap all calls to it. I was hoping for something more like Swift's option types. In Swift, I would do: function foo(x: Int?) And I can check x to see if it's nil inside the function. I can just call foo with a plain Int and it works fine. Int implicitly casts to Int?, but Int? needs an explicit conversion to Int. That was the relationship I was looking for. Apparently, not attainable in D, unless you do it on initialization. -Steve
Apr 02 2018
prev sibling next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.
This is what function overloading and/or default values are for, right?
Apr 04 2018
parent Timoses <timosesu gmail.com> writes:
On Wednesday, 4 April 2018 at 08:08:40 UTC, Dejan Lekic wrote:
 On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
 wrote:
 I currently have a situation where I want to have a function 
 that accepts a parameter optionally.
This is what function overloading and/or default values are for, right?
Not if you'd like to pass an actual _optional_ parameter for e.g. an int. Default parameter would actually assign a value. See also: https://forum.dlang.org/post/rzgcenuqiokknpsltlld forum.dlang.org
Apr 04 2018
prev sibling parent reply Dukc <ajieskola gmail.com> writes:
On Sunday, 1 April 2018 at 15:54:16 UTC, Steven Schveighoffer 
wrote:
 But I'd rather avoid such things if possible. Is there a way 
 around this? Seems rather limiting that I can do:
Is this what you're looking for? void foo(Nullable!int x = Nullable!int.init) { if (!x.isNull) x.get.writeln; else writeln; } void foo(int x) { return Nullable!int(x).foo; } void main() { foo(1); // 1 int x; foo(x++); // 0 foo; // empty line foo(x); // 1 readln(); }
Apr 04 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/4/18 8:59 AM, Dukc wrote:
 Is this what you're looking for?
See my original post:
 I know I can do things like this:
 
 void foo(int x) { return foo(nullable(x)); } 
-Steve
Apr 04 2018
parent Dukc <ajieskola gmail.com> writes:
On Wednesday, 4 April 2018 at 15:44:34 UTC, Steven Schveighoffer 
wrote:
 See my original post:

 I know I can do things like this:
 
 void foo(int x) { return foo(nullable(x)); }
-Steve
Oops, I read only the body of that function and thought it's a main function constructiong nullable when calling. Well, short of defining some sort of mixin to do this automatically, no better ideas :(.
Apr 04 2018