News for hackers. Stuff that matters.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by simias on Sunday June 14, 2020 @11:43AM (#23518498)
I like named/keyword arguments too but I think one needs to be careful not to overdo it, especially in statically typed languages. I think it's sort of a last resort when the call syntax can't be disambiguated by other means.

As an example imagine a search function that returns the position of a character `c` in a string `str` that looks like that:

    int search(String str, char c, bool case_sensitive, bool from_end)
I'd say that I would avoid this signature in general, because those two `bool`s make for an awkward call syntax:

    search("foo", 'f', true, false)
That's pretty opaque, even if you know what the two options are it's easy to forget the order. Now you might say "well see, that's where keyword parameters shine!" and you'd be right, it does help a lot:

    search("foo", 'f', case_sensitive: true, from_end: false)
But I'd argue that while this is good enough if you're coding in a dynamically typed language, it's actually sub-obtimal if you're coding in Rust for instance. Here you can do something even better: give these two options different types, this way not only it makes the code more explicit, it also means that it gets enforced by the compiler:

    enum SearchCase { Sensitive, Insensitive };
    enum SearchDirection { FromStart, FromEnd };

    search("foo", 'f', SearchCase::Sensitive, SearchDirection::FromStart);
Now that's not to say that I don't want or like keyword arguments, I just think that it's one of these situations where "when you have a hammer, everything looks like a nail". I sometimes miss keyword arguments in Rust, but it's the exception rather than the rule for me.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by skohan on Sunday June 14, 2020 @12:32PM (#23519043)
I would question that your rust example is better than the named argument example. I think it's situational at best, and a workaround at worst.

For instance, even in the example you gave, it could be argued that the SearchCase type is really just a boolean which is being introduced to do the job of an argument label. If this enum is only used in this function call, that's going to add noise to the docs with fairly tenuous justification.

And this only works in the case of arguments which can be easily converted to enums. For instance if I have a signature which looks like this:

   void search_buffer(int match, int start, int end, int stride)
It's going to be very difficult to look at the call site and infer what the meanings of these arguments are.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by simias on Sunday June 14, 2020 @03:37PM (#23520642)
>For instance, even in the example you gave, it could be argued that the SearchCase type is really just a boolean which is being introduced to do the job of an argument label. If this enum is only used in this function call, that's going to add noise to the docs with fairly tenuous justification.

I don't really agree with this, I think in general it's a good idea to type your parameters as tightly as it's reasonable to do. I don't think that introducing a one-time enum is particularly unreasonable. I don't really get the argument for reducing noise in the docs, typically nowadays API documentations are hyperlinked document, not a big PDF, you either look the symbol up or you don't.

An argument in favour of using types here would be in the situation where the "case_sensitive" parameter is not a literal in the code but something that's being passed as argument from an other function or stored in an object. The names could be slightly different, the conventions could change from one part of the code to an other etc... With dedicated types you can be reasonably certain that if the types match then it's probably safe to pass to the function, with human-level semantics that may require more caution.

Of course my example is so trivial that it could be argued to be over-engineered, but consider a function that takes angles as floating point numbers vs. a dedicated "Angle" type for instance, with the float you have to make sure that the underlying representation is correct (degrees? radians? turns?) whereas a dedicated type would typically have well-defined semantics.

>And this only works in the case of arguments which can be easily converted to enums. For instance if I have a signature which looks like this:

   void search_buffer(int match, int start, int end, int stride)
I agree completely with you here. Keyword parameters definitely have their use cases. I just think that they should be used parsimoniously and, IMO, can be a code smell if used too often, especially in languages with a decent type system.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by oefrha on Sunday June 14, 2020 @11:49AM (#23518573)
You’re forgetting another important use case of keyword/named arguments: default values. In Rust either every call site needs to specify the default values, or you use a None for the default which is not at all readable.

Also, if Rust were to support named arguments (even better if with default values), you can use explicit types for your bools all the same. It’s not an either or situation.

Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by kbenson on Sunday June 14, 2020 @03:10PM (#23520417)
Wouldn't a struct for named options that have defaults which is passed as the last option emulate optional named arguments with default values?
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by skohan on Sunday June 14, 2020 @03:54PM (#23520781)
Of course in the case of Rust this would mean potentially one type per function being added to your implementation. That’s a lot of boilerplate for a problem which is solved really well by defaultable named parameters
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by kbenson on Sunday June 14, 2020 @04:17PM (#23520945)
I agree it seems like a lot of boilerplate, but from what little I've played around with Rust, it seems they've already adopted strategies similar to this for instantiating objects with the builder pattern[1], but the nuts and bolts are abstracted away so the interface is slightly different.

If I understand it correctly, the equivalent builder pattern in Rust (which is essentially using methods to build a struct used to instantiate the object) would be:

    let search = Search::new("foo","f")
      .option_case_sensitive(true)
      .option_from_end(false)
      .build();
Where "new" returns a builder object/struct with passed in and default params, those options methods are optional and can change those defaults, and the build method actually builders the search object from the search builder struct with all the params.

I'm not sure how well this style works with simple functions, unless there's a macro or crate that can do most of this work for you from a a spec.

1: https://doc.rust-lang.org/1.0.0/style/ownership/builders.htm...

Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by saagarjha on Sunday June 14, 2020 @01:07PM (#23519362)
> Here you can do something even better: give these two options different types, this way not only it makes the code more explicit, it also means that it gets enforced by the compiler

Bool is also enforced by the compiler; adding ad-hoc types for things that have only two values is generally not something you should do without good reason. By the way, constructs like these are fairly common in Swift:

  enum SearchType {
      case literal
      case regex
      case extendedRegex
  }

  extension String {
      func substring(matching needle: String, interpretedAs type: SearchType = .literal) -> Substring
  }
which you'd use as something like

  "foobar".substring(matching: "o*ba", interpretedAs: .regex) // "ooba"
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by barrkel on Sunday June 14, 2020 @02:45PM (#23520149)
In the absence of keyword arguments as a language feature, boolean arguments should almost always be avoided, and replaced by enums where possible. Boolean arguments are almost always inscrutable at the call site.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by skohan on Sunday June 14, 2020 @02:59PM (#23520300)
Then it would be very convenient to have keyword arguments, as in the case presented by the previous poster
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by kiawe_fire on Sunday June 14, 2020 @11:59AM (#23518710)
I still think there are wider use cases for named arguments. For example, even disambiguating between which argument is the "string" and which is the "substring to be found", or the classic "x, y, width, height" list in graphics programming.

But your point is well taken that, like many tools, you shouldn't abandon one set of good practices for another. There are certainly cases where a builder pattern or enum values are a better fit, which could be obscured as an option because keyword arguments "sorta kinda but don't really" address the same thing.

Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by ridiculous_fish on Sunday June 14, 2020 @03:10PM (#23520413)
If this were a real API, you would need to add the trimmings:

    /// Doc comment doc comment doc comment
    #[derive(Debug, Copy, Clone, Eq, PartialEq)]
    pub enum SearchCase { Sensitive, Insensitive }
now it starts to feel like heavyweight clutter.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by simias on Sunday June 14, 2020 @03:43PM (#23520691)
I see what you mean, but if that's the problem I'd argue that the weakness is that Rust lacks a shorthand to declare simple enums, not necessarily that it lacks keyword parameters. #[derive] soup aside I don't think too bad at the moment though, and I'm not really sure how you could simplify that without introducing too much magic into the language.

After all, even with keyword parameters there'll always be plenty of opportunity for one-off enums, if only for situations where more than two choices are possible. Take a typical "seek" function for instance, who can usually be performed relative to the current location, from the start or from the end.

Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by atombender on Sunday June 14, 2020 @02:49PM (#23520202)
In Go (and, I think, Rust?) variadic arguments are often used for optionals:

    search("foo", CaseSensitive, FromStart)
or options that take values:

    search("foo", WithCaseSensitivity(false))
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by yoz-y on Sunday June 14, 2020 @12:44PM (#23519145)
That's why Swift does support unnamed parameters as well, and they can interweave with named ones. If the type of the argument is explicit enough, you can make the parameter name at call site optional.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by wool_gather on Sunday June 14, 2020 @01:16PM (#23519438)
Nitpick (I think this is just a miswording rather than a misunderstanding): it's not optional. You can have unlabelled args, but that's enforced too: the label must be included or omitted at the call site according to the function declaration.
Re:The Curious Case of Swift's Adoption of Smalltalk Keyword... by yoz-y on Sunday June 14, 2020 @07:03PM (#23522176)
TBH I was usure and kind of lazy to check. Thanks for pointing it out!