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.
- Host: GitHub
- URL: https://github.com/fsmaxb/type-safe-builder-experiment
- Owner: FSMaxB
- License: other
- Created: 2019-12-07T19:03:10.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2022-03-19T04:44:36.000Z (about 4 years ago)
- Last Synced: 2026-03-29T16:54:00.266Z (2 months ago)
- Language: C#
- Size: 711 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
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
```