Skip to content

Commit

Permalink
minor named arg / optional param doc changes
Browse files Browse the repository at this point in the history
  • Loading branch information
rsmckinney committed Nov 19, 2024
1 parent 0a6422e commit ec40f68
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 54 deletions.
94 changes: 55 additions & 39 deletions manifold-deps-parent/manifold-ext/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1678,9 +1678,45 @@ See `manifold.collections.extensions.java.util.Map.MapStructExt.java` for detail

# Named arguments & optional parameters

Combining named arguments with optional parameters is a useful alternative for flexible and readable function calls. Although
Java does not provide either feature, we can utilize [tuples](https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-tuple)
You can combine named arguments with optional parameters to create flexible and readable function calls. Although Java
does not provide either feature, we can utilize [tuples](https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-tuple)
and [structural interfaces](#structural-interfaces-via-structural) for virtually the same functionality.

## Defining optional parameters

Instead of listing parameters directly in a method, you define them as members of a structural interface.
```java
@Structural
interface $greet {@val String name; @val int age = 20;}
public void greet($greet args) {
out.println("Hello, "+args.name+"! You are "+args.age+" years old.");
}
```
Notice `age` has a default value of 20, making it optional.

Also, notice the `$greet` structural interface uses [properties](https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-props)
instead of conventional _getter_ default interface methods, which looks like this:
```java
@Structural
interface $greet {String getName(); default int getAge() {return 20;}}
```
Both ways work equally well, but using `@val` is more concise and easier to read.

Note, you can use any name you like for the interface, `$greet` is just following a convention for consistency. The `$`
prefix indicates the type is not for general use.

## Using named arguments

Named arguments allow you to explicitly specify the parameter name when calling a function, improving code readability and
allowing you to pass arguments out of order.
```java
greet((name:"Alice")); // Uses the default value for age (20)

greet((name:"Bob", age:30)); // Explicitly sets age

greet((age:25, name:"Charlie")); // Order doesn't matter with named arguments
```
Notice arguments are passed using tuple syntax. This works because tuples are assignable to structural interfaces.

Consider a typical telescoping constructor in Java.
```java
Expand Down Expand Up @@ -1715,15 +1751,16 @@ public class Person {
this.address = address;
this.phone = phone;
}
. . .

// getters/setters etc. . .
}
```
While useful, this technique has become known as an "anti-pattern" due to its verbosity, maintenance concerns, and general inadequacy.
Although the Builder pattern is sometimes used to overcome some of the drawbacks, it too has its own set of problems, mostly
due to the boilerplate and tedium involved with writing/generating and maintaining them.
While useful, this technique is widely considered an "anti-pattern" due to its verbosity, maintenance concerns, and general
inadequacy--it does not cover all the different combinations of arguments, such as passing just `name` and `phone`. Although
the Builder pattern is sometimes used to overcome some of the drawbacks, it too has its own set of problems, mostly due
to the boilerplate and tedium involved with writing/generating and maintaining them.

Using tuples and structural typing to simulate named arguments and optional parameters offer a cleaner, easier to maintain
alternative to these strategies.
Named arguments and optional parameters offer a cleaner, more capable, and easier to maintain alternative to these strategies.

```java
public class Person {
Expand All @@ -1733,47 +1770,26 @@ public class Person {
private String address;
private String phone;

@Structural interface Options {
default int getAge() {return 0;}
default Gender getGender() {return null;}
default String getAddress() {return null;}
default String getPhone() {return null;}
}
public Person(String name, Options options) {
@Structural interface $person
{@val int age = 0; @val Gender gender = null; @val String address = null; @val String phone = null;}
public Person(String name, $person options) {
this.name = name;
this.age = options.getAge();
this.gender = options.getGender();
this.address = options.getAddress();
this.phone = options.getPhone();
this.age = options.age;
this.gender = options.gender;
this.address = options.address;
this.phone = options.phone;
}

. . .
}
```
Since tuples are assignable to structural interfaces, we can utilize them for a powerful named arguments & optional parameters syntax.
```java
Person person = new Person("Scott", (age:100, phone:"408-555-1234"));
```
Here, we create a `Person` using select options in the order of our choosing. Not only is this syntax more concise
and easier to read and use, the reduction in boilerplate also relieves the maintenance burden compared with telescoping
methods and builders.
Here, we create a `Person` with named arguments in the order of our choosing. Not only is this syntax more concise
and easier to read and use at the call site, the overall reduction in boilerplate also relieves the maintenance burden
compared with telescoping methods and builder classes.

Note, [properties](https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-props)
can further eliminate Java's boilerplate while increasing readability.
```java
@Structural interface Options {
@val int age = 0;
@val Gender gender = null;
@val String address = null;
@val String phone = null;
}
public Person(Options options) {
this.age = options.age;
this.gender = options.gender;
this.address = options.address;
this.phone = options.phone;
}
```

# Type-safe Reflection via `@Jailbreak`

Expand Down
17 changes: 2 additions & 15 deletions manifold-deps-parent/manifold-tuple/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,28 +147,15 @@ tuple.
```java
order(Large, (crust:Thick, pepperoni:true));

void order(Size size, Pizza pizza) {...}

@Structural
interface Pizza {
default Shape getShape() {return Round;}
default Crust getCrust() {return Thin;}
default Sauce getSauce() {return Red;}
default boolean isCheese() {return true;}
default boolean isPepperoni() {return false;}
default boolean isMushrooms() {return false;}
}
```
```java
// Even better with properties via manifold-props
@Structural interface Pizza {
@Structural interface $order {
@val Shape shape = Round;
@val Crust crust = Thin;
@val Sauce sauce = Red;
@val boolean cheese = true;
@val boolean pepperoni = false;
@val boolean mushrooms = false;
}
void order(Size size, $order options) {...}
```
This technique provides virtual language features for named arguments & optional parameters. Use it as a refreshing
alternative to telescoping methods/constructors, method overloading, and builders.
Expand Down

0 comments on commit ec40f68

Please sign in to comment.