Jump to page: 1 2 3
Thread overview
Optional parameters?
Apr 01, 2018
Jacob Carlborg
Apr 01, 2018
Alex
Apr 01, 2018
Jonathan M Davis
Apr 01, 2018
Boris-Barboris
Apr 01, 2018
Jonathan M Davis
Apr 01, 2018
Boris-Barboris
Apr 01, 2018
Jonathan M Davis
Apr 01, 2018
Boris-Barboris
Apr 01, 2018
Seb
Apr 01, 2018
Jonathan M Davis
Apr 01, 2018
Ali
Apr 02, 2018
Norm
Apr 02, 2018
Timoses
Apr 02, 2018
Cym13
Apr 04, 2018
Dejan Lekic
Apr 04, 2018
Timoses
Apr 04, 2018
Dukc
Apr 04, 2018
Dukc
April 01, 2018
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
April 01, 2018
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
April 01, 2018
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
April 01, 2018
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

April 01, 2018
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.

April 01, 2018
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

April 01, 2018
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)
April 01, 2018
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);


April 01, 2018
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

April 01, 2018
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

« First   ‹ Prev
1 2 3