An open API service indexing awesome lists of open source software.

https://github.com/fsmaxb/type-safe-builder-experiment

Experimenting with the type safe builder pattern in different languages.
https://github.com/fsmaxb/type-safe-builder-experiment

Last synced: about 2 months ago
JSON representation

Experimenting with the type safe builder pattern in different languages.

Awesome Lists containing this project

README

          

Type Safe Builder Pattern
=========================

This is an experiment to explore the idea of a type safe builder pattern.

Essentially the idea is to have a builder that checks if all the necessary properties have been provided at compile time.
If any are missing, the program should not compile.

Bonus: Try to make the implementation as efficient (runtime + memory) as possible.

## Languages

A list of all the languages in which this pattern has been implemented here, how the usage looks like and the error message.

### C++ 17

Usage:
```cpp
constexpr auto point = point3d()
.z(3.0)
.y(2.0)
.x(1.0)
.build();
```

Error message:
```cpp
constexpr auto point = point3d()
.z(3.0)
.y(2.0)
.build();
```

`g++ 11.2.0`
```
type-safe-builder/cpp17/main.cpp: In instantiation of ‘constexpr Point3D Point3DBuilder::build() const [with A = std::enable_if; B = std::enable_if; C = std::enable_if; X = StaticNone; Y = StaticSome; Z = StaticSome]’:
type-safe-builder/cpp17/main.cpp:75:10: required from here
type-safe-builder/cpp17/main.cpp:63:43: error: ‘const struct StaticNone’ has no member named ‘element’
63 | return Point3D(this->ourX.element, this->ourY.element, this->ourZ.element);
| ~~~~~~~~~~~^~~~~~~
type-safe-builder/cpp17/main.cpp: In function ‘int main()’:
type-safe-builder/cpp17/main.cpp:75:10: in ‘constexpr’ expansion of ‘point3d().Point3DBuilder, StaticNone, StaticNone >::z<>(3.0e+0).Point3DBuilder, StaticNone, StaticSome >::y<>(2.0e+0).Point3DBuilder, StaticSome, StaticSome >::build<>()’
type-safe-builder/cpp17/main.cpp:75:32: error: ‘constexpr’ call flows off the end of the function
75 | .build();
| ^
```

`clang++ 13.01`
```
type-safe-builder/cpp17/main.cpp:63:29: error: no member named 'element' in 'StaticNone'
return Point3D(this->ourX.element, this->ourY.element, this->ourZ.element);
~~~~~~~~~~ ^
type-safe-builder/cpp17/main.cpp:75:5: note: in instantiation of function template specialization 'Point3DBuilder, StaticSome, StaticSome>::build, std::enable_if, std::enable_if>' requested here
.build();
^
type-safe-builder/cpp17/main.cpp:75:5: error: constexpr variable 'point' must be initialized by a constant expression
.build();
~^~~~~~~
```

Notes:
* Fully `constexpr`, so the builder can be used to build values at compile time.

### C#

Usage:
```cs
var point = Point3DBuilderExtensions.Point3D
.Z(3.0)
.Y(2.0)
.X(1.0)
.Build();
```

Error message:
```cs
var point = Point3DBuilderExtensions.Point3D
.Z(3.0)
.Y(2.0)
.Build();
```

```
type-safe-builder/cs/type-safe-builder-test/BuilderTest.cs(11,16): error CS1929: 'Point3DBuilder, StaticSome, StaticSome>' does not contain a definition for 'Build' and the best extension method overload 'Point3DBuilderExtensions.Build(Point3DBuilder, StaticSome, StaticSome>)' requires a receiver of type 'Point3DBuilder, StaticSome, StaticSome>' [type-safe-builder/cs/type-safe-builder-test/type-safe-builder-test.csproj]
```

### Java 17

Usage:
```java
var point = build(x(y(z(point3d(), 3.0), 2.0), 1.0));
```

Error message:
```java
var point = build(y(z(point3d(), 3.0), 2.0));
```

```
type-safe-builder/java/lib/src/test/java/builder/BuilderTest.java:10: error: incompatible types: inference variable X#1 has incompatible equality constraints StaticSome,StaticNone,X#2
var point = build(y(z(point3d(), 3.0), 2.0));
^
where X#1,Z,X#2,Y are type-variables:
X#1 extends StaticOptional declared in method y(Point3DBuilder,Z>,double)
Z extends StaticOptional declared in method y(Point3DBuilder,Z>,double)
X#2 extends StaticOptional declared in method z(Point3DBuilder>,double)
Y extends StaticOptional declared in method z(Point3DBuilder>,double)
```

### Kotlin

Usage:
```kotlin
val point = point3d()
.z(3.0)
.y(2.0)
.x(1.0)
.build();
```

Error message:
```kotlin
val point = point3d()
.z(3.0)
.y(2.0)
.build();
```

```
type-safe-builder/kotlin/lib/src/test/kotlin/builder/BuilderTests.kt: (12, 5): Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun Point3DBuilder, StaticSome, StaticSome>.build(): Point3D defined in builder
```

### Rust

Usage:
```rust
let point = point3d()
.z(3.0)
.y(2.0)
.x(1.0)
.build();
```

Error message:
```rust
let point = point3d()
.z(3.0)
.y(2.0)
.build();
```

```
error[E0599]: no method named `build` found for struct `Point3DBuilder, StaticSome, StaticSome>` in the current scope
--> src/lib.rs:28:5
|
28 | .build();
| ^^^^^ method not found in `Point3DBuilder, StaticSome, StaticSome>`
|
::: src/builder.rs:5:1
|
5 | / pub struct Point3DBuilder
6 | | where
7 | | X: StaticOptional,
8 | | Y: StaticOptional,
... |
13 | | z: Z,
14 | | }
| |_- method `build` not found for this
|
= note: the method was found for
- `Point3DBuilder, StaticSome, StaticSome>`
```

### Swift

Usage:
```swift
let point = point3d()
.z(3.0)
.y(2.0)
.x(1.0)
.build()
```

Error Message:
```swift
let point = point3d()
.z(3.0)
.y(2.0)
.build()
```

```
type-safe-builder/swift/Tests/TypeSafeBuilderTests/TypeSafeBuilderTests.swift:8:5: error: referencing instance method 'build()' on 'Point3DBuilder' requires the types 'StaticNone' and 'StaticSome' be equivalent
.y(2.0)
^
type-safe-builder/swift/Sources/TypeSafeBuilder/Point3DBuilder.swift:43:1: note: where 'X' = 'StaticNone'
extension Point3DBuilder
^
```

### TypeScript

Usage:
```typescript
const point = build(x(y(z(point3d(), 3.0), 2.0), 1.0));
```

Error message:
```typescript
const point = build(y(z(point3d(), 3.0), 2.0));
```

```
src/index.ts:58:21 - error TS2345: Argument of type 'Point3DBuilder, StaticSome, StaticSome>' is not assignable to parameter of type 'Point3DBuilder, StaticSome, StaticSome>'.
Property 'element' is missing in type 'StaticNone' but required in type 'StaticSome'.

58 const point = build(y(z(point3d(), 3.0), 2.0));
~~~~~~~~~~~~~~~~~~~~~~~~~

src/index.ts:5:2
5 element: Type;
~~~~~~~
'element' is declared here.
```

### Go

Usage:
```golang
point := Build(X(Y(Z(Point3D(), 3.0), 2.0), 1.0))
```

Error message:

```golang
point := Build((Y(Z(Point3D(), 3.0), 2.0)))
```

`go1.18`
```
# type-safe-builder/builder [type-safe-builder/builder.test]
./point3DBuilder_test.go:9:23: cannot use (Y(Z(Point3D(), 3.0), 2.0)) (value of type Point3DBuilder[StaticNone, StaticSome[float64], StaticSome[float64]]) as type Point3DBuilder[StaticSome[float64], StaticSome[float64], StaticSome[float64]] in argument to Build
```