Skip to content

Commit

Permalink
Merge pull request #112 from widmogrod/feature/march-2024-1
Browse files Browse the repository at this point in the history
Documentation about state machines and few refinements along the way
  • Loading branch information
widmogrod authored May 18, 2024
2 parents 500cb84 + 73b719d commit 79e28c0
Show file tree
Hide file tree
Showing 63 changed files with 2,727 additions and 863 deletions.
23 changes: 18 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ jobs:
name: Test implementation in Golang
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ^1.21
id: go

- run: cd cmd/mkunion; go get -v -t -d ./...
- run: cd cmd/mkunion; go build -o mkunion

- run: go install github.com/matryer/moq@latest

- run: go get -v -t -d ./...
- run: go generate ./...

# initiate docker-compose services
- run: |
pip install awscli-local
- run: pip install awscli-local
- run: dev/bootstrap.sh -nologs

- run: |
find . -type f -name '*.go' -exec grep -C 2 -H 'github.com/opensearch-project/opensearch-go/v2' {} + &2>/dev/null || true
- run: |
ls -la /home/runner/go/pkg/mod/github.com/opensearch-project/opensearch-go
ls -la /home/runner/go/pkg/mod/cache/download/github.com/opensearch-project/opensearch-go
tree /home/runner/go/pkg/mod/github.com/opensearch-project/opensearch-go
tree /home/runner/go/pkg/mod/cache/download/github.com/opensearch-project/opensearch-go
- run: |
cat x/storage/schemaless/types_reg_gen.go
# run tests
- run: |
export RUN_EXPERIMENTAL_TEST="false"
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@


## About
Strongly typed **union type** in golang.
Strongly typed **union type** in golang with generics*.

* with full _pattern matching_ support
* with full _json marshalling_ support
* with exhaustive _pattern matching_ support
* with _json marshalling_ including generics
* and as a bonus, can generate compatible typescript types for end-to-end type safety in your application

## Why
Expand All @@ -19,7 +19,7 @@ Visitor pattern requires a lot of boiler plate code and hand crafting of the `Ac

On top of that, any data marshalling like to/from JSON requires additional, hand crafted code, to make it work.

MkUnion solves all of those problems, by generating opinionated and strongly typed mindful code for you.
MkUnion solves all of those problems, by generating opinionated and strongly typed meaningful code for you.

## Example

Expand Down
15 changes: 14 additions & 1 deletion cmd/mkunion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func main() {
},
&cli.BoolFlag{
Name: "type-registry",
Value: false,
Value: true,
},
},
Action: func(c *cli.Context) error {
Expand Down Expand Up @@ -563,7 +563,20 @@ func GenerateTypeRegistry(inferred *shape.IndexedTypeWalker) (bytes.Buffer, erro
shape.WithPkgImportName(),
)

// Register go type
contents.WriteString(fmt.Sprintf("\tshared.TypeRegistryStore[%s](%q)\n", instantiatedTypeName, fullTypeName))

// Try to register type JSON marshaller
if ref, ok := inst.(*shape.RefName); ok {
some, found := shape.LookupShapeOnDisk(ref)
if !found {
continue
}
some = shape.IndexWith(some, ref)
if shape.IsUnion(some) {
contents.WriteString(fmt.Sprintf("\t%s\n", generators.StrRegisterUnionFuncName(shape.ToGoPkgName(some), some)))
}
}
}

contents.WriteString("}\n")
Expand Down
10 changes: 10 additions & 0 deletions dev/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ project_root=$(dirname "$cwd")
envrc_file=$project_root/.envrc

echo "Check if necessary tools are installed"
command -v go >/dev/null 2>&1 || { echo >&2 "golang is not installed. Aborting."; exit 1; }
command -v docker >/dev/null 2>&1 || { echo >&2 "docker is not installed. Aborting."; exit 1; }
command -v docker-compose >/dev/null 2>&1 || { echo >&2 "docker-compose is not installed. Aborting."; exit 1; }
command -v awslocal >/dev/null 2>&1 || { echo >&2 "awslocal is not installed. Aborting. Please run
pip install awscli-local "; exit 1; }

# check for moq
command -v moq >/dev/null 2>&1 || { echo >&2 "moq is not installed. Aborting please run
go install github.com/matryer/moq@latest"; exit 1; }

echo "Creating volume directory"
mkdir -p $cwd/_volume

if [ "$1" == "-install-only" ]; then
trap - EXIT
exit 0
fi

echo "Starting localstack"
docker compose -f $cwd/compose.yml up -d
# trap exit and stop docker compose
Expand Down
3 changes: 2 additions & 1 deletion dev/docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ cwd=$(dirname "$0")
project_root=$(dirname "$cwd")

if [ "$1" == "run" ]; then
docker run --rm -it -p 8000:8000 -v ${project_root}:/docs squidfunk/mkdocs-material
echo "Serving documentation at http://localhost:8088"
docker run --rm -it -p 8088:8000 -v ${project_root}:/docs squidfunk/mkdocs-material
elif [ "$1" == "build" ]; then
docker run --rm -it -v ${project_root}:/docs squidfunk/mkdocs-material build
else
Expand Down
11 changes: 6 additions & 5 deletions docs/development/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ title: Contributing and development
---

# Contributing and development
## Contributing

If you want to contribute to `mkunion` project, please open issue first to discuss your idea.

I have opinions about how `mkunion` should work, how I want to evolve it, and I want to make sure that your idea fits into the project.

## Development

Checkout repo and run:
Expand All @@ -28,8 +34,3 @@ To preview documentation run:
```
./dev/docs.sh run
```

## Contributing

If you want to contribute to `mkunion` project, please open issue first to discuss your idea.
I have opinions about how `mkunion` should work, how I want to evolve it, and I want to make sure that your idea fits into the project.
23 changes: 12 additions & 11 deletions docs/examples/generic_union.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Generic unions
title: Union and generic types
---
# Generic unions
# Union and generic types
MkUnion will generate generic unions for you.

You only need to declare each variant type of the union with a type parameter,
Expand All @@ -27,18 +27,16 @@ type (
)
```

After you run generation (as described in [getting started](/getting_started.md)),
After you run generation (as described in [getting started](../getting_started.md)),
you have access to the same features as with non-generic unions.

## Matching function

Let's define higher order function `ReduceTree` that will travers leaves in tree and produce a single value.
Let's define higher order function `ReduceTree` that will travers leaves in `Tree` and produce a single value.

This function uses `MatchTreeR1` function that is generated automatically for you.

```go title="example/tree.go"

```go
func ReduceTree[A, B any](x Tree[A], f func(A, B) B, init B) B {
return MatchTreeR1(
x,
Expand Down Expand Up @@ -78,11 +76,7 @@ func ExampleTreeSumValues() {

You can also reduce tree to complex structure, for example to keep track of order of values in the tree, along with sum of all values in the tree.

```go title="example/tree.go"
```go title="example/tree_test.go"

```go
func ExampleTreeCustomReduction() {
tree := &Branch[int]{
L: &Leaf[int]{Value: 1},
Expand Down Expand Up @@ -154,4 +148,11 @@ func MapOption[A, B any](x Option[A], f func(A) B) Option[B] {
},
)
}
```
```

In above example, we define `MapEither` and `MapOption` functions that will apply function `f` to value inside `Either` or `Option` type.

It would be much better to have only one `Map` definition, but due to limitations of Go type system, we need to define separate functions for each type.

I'm considering adding code generation for such behaviours in the future. Not yet due to focus on validating core concepts.

81 changes: 81 additions & 0 deletions docs/examples/json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: Marshaling union as JSON
---

# Marshaling union as JSON

MkUnion provides you with utility function that allows you to marshal and unmarshal union types to JSON,
reducing burden of writing custom marshaling and unmarshaling functions for union types.

- `shared.JSONMarshal[A any](in A) ([]byte, error)`
- `shared.JSONUnmarshal[A any](data []byte) (A, error)`

Below is an example of how to use those functions and how the output JSON looks like.


```go title="example/tree_json_test.go"
import (
"github.com/widmogrod/mkunion/x/shared"
)

--8<-- "example/tree_json_test.go:8:30"
```

Formated JSON output of the example above:
```json
{
"$type": "example.Branch",
"example.Branch": {
"L": {
"$type": "example.Leaf",
"example.Leaf": {
"Value": 1
}
},
"R": {
"$type": "example.Branch",
"example.Branch": {
"L": {
"$type": "example.Branch",
"example.Branch": {
"L": {
"$type": "example.Leaf",
"example.Leaf": {
"Value": 2
}
},
"R": {
"$type": "example.Leaf",
"example.Leaf": {
"Value": 3
}
}
}
},
"R": {
"$type": "example.Leaf",
"example.Leaf": {
"Value": 4
}
}
}
}
}
}
```


There are few things that you can notice in this example:

- Each union type discriminator field `$type` field that holds the type name, and corresponding key with the name of the type, that holds value of union variant.
- This is opinionated way, and library don't allow to change it.
I was experimenting with making this behaviour customizable, but it make code and API mode complex, and I prefer to keep it simple, and increase interoperability between different libraries and applications, that way.

- Recursive union types are supported, and they are marshaled as nested JSON objects.]

- `$type` don't have to have full package import name, nor type parameter,
mostly because in `shared.JSONUnmarshal[Tree[int]](json)` you hint that your code accepts `Tree[int]`.
- I'm considering adding explicit type discriminators like `example.Branch[int]` or `example.Leaf[int]`.
It could increase type strictness on client side, BUT it makes generating TypeScript types more complex, and I'm not sure if it's worth it.

- It's not shown on this example, but you can also reference types and union types from other packages, and serialization will work as expected.
Loading

0 comments on commit 79e28c0

Please sign in to comment.