You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
...
finalcaseclassTNull[A]() extendsMuF[A]
finalcaseclassTDouble[A]() extendsMuF[A]
finalcaseclassTFloat[A]() extendsMuF[A]
finalcaseclassTInt[A]() extendsMuF[A]
finalcaseclassTLong[A]() extendsMuF[A]
finalcaseclassTBoolean[A]() extendsMuF[A]
finalcaseclassTString[A]() extendsMuF[A]
finalcaseclassTByteArray[A]() extendsMuF[A]
finalcaseclassTNamedType[A](name: String) extendsMuF[A]
finalcaseclassTOption[A](value: A) extendsMuF[A]
finalcaseclassTEither[A](left: A, right: A) extendsMuF[A]
finalcaseclassTList[A](value: A) extendsMuF[A]
finalcaseclassTMap[A](value: A) extendsMuF[A]
...
We repeat basically all the leaves of the AST, because all different
IDLs allow to represent more or less the same types, with minor
differences.
Also, having all those different ASTs means that we cannot create
functions that work on generic ASTs, but need to specify them.
The solution
I would prefer using a unique set of classes representing different
types possible in any schema, and then the specific schemata pick and
choose from them in order to assemble their ADT, as follows.
First we would need to declare all our base leaves:
finalcaseclassTNull[A]()
finalcaseclassTBoolean[A]()
finalcaseclassTInt[A]()
finalcaseclassTLong[A]()
finalcaseclassTFloat[A]()
finalcaseclassTDouble[A]()
finalcaseclassTBytes[A]()
finalcaseclassTString[A]()
finalcaseclassTNamedType[A](name: String)
finalcaseclassTArray[A](item: A) // sugar over Generic(NamedType("Map"), A, A)finalcaseclassTMap[A](keys: A, values: A) // sugar over Generic(NamedType("Map"), A, A)finalcaseclassTFixed[A](name: String, size: Int)
finalcaseclassTOption[A](value: A) // sugar over Generic(NamedType("Option"), A)finalcaseclassTEither[A](left: A, right: A) // sugar over Generic(NamedType("Option"), A)finalcaseclassTList[A](value: A) // sugar over Generic(NamedType("Option"), A)finalcaseclassTGeneric[A](generic: A, params: List[A])
finalcaseclassTRecord[A](name: String, fields: List[Field[A]])
finalcaseclassTEnum[A](name: String, symbols: List[String])
finalcaseclassTUnion[A](options: NonEmptyList[A])
N.B: these classes do not extend anything, they're plain old case
classes.
Then, for combining those case classes into an Algebraic Data Type, we
can use coproducts. Coproducts and its motivations are well explained
in this paper.
Higher kind coproducts are implemented in several libraries in the
scala FP world, such as Scalaz, Cats, and Iota. In order to do some
dogfooding, we'll be using iota's implementation.
Then, we would assemble our types using coproducts, as follows:
See how, in this case, each AST declaration decided to pick only the
types it needed.
Usage
The only missing part of this idea is how to use it. One of the
features motivating this change is code reuse, and generic
programming. In the following code block you can see how some generic
transformations on these ASTs could be implemented.
defdesugarList[F[α] <:CopK[_, α], A](
implicitL:CopK.Inject[TList, F],
G:CopK.Inject[TGeneric, F],
N:CopK.Inject[TNamedType, F],
A:Embed[F, A]
):Trans[F, F, A] =Trans {
caseL(TList(t)) => generic[F, A](namedType[F, A]("List").embed, List(t))
case x => x
}
defdesugarOption[F[α] <:CopK[_, α], A](
implicitO:CopK.Inject[TOption, F],
G:CopK.Inject[TGeneric, F],
N:CopK.Inject[TNamedType, F],
A:Embed[F, A]
):Trans[F, F, A] =Trans {
caseO(TOption(a)) => generic[F, A](namedType[F, A]("Option").embed, List(a))
case x => x
}
defdesugarEither[F[α] <:CopK[_, α], A](
implicitE:CopK.Inject[TEither, F],
G:CopK.Inject[TGeneric, F],
N:CopK.Inject[TNamedType, F],
A:Embed[F, A]
):Trans[F, F, A] =Trans {
caseE(TEither(a, b)) => generic[F, A](namedType[F, A]("Either").embed, List(a, b))
case x => x
}
As you can see, these functions do not work on a specific AST.
Instead, they are generic, meaning that they will work on any AST for
which some injections exist.
work to be done
We need to implement a way of traverse derivation for Iota's
coproducts. Traverse can be derived mechanically, and I think the
fastest way would be a generic-like derivation a la shapeless.
resources
some useful resources for understanding this approach are this
talk and this paper.
The text was updated successfully, but these errors were encountered:
introducing UAST
disclaimer: UAST term is borrowed from https://doc.bblf.sh/uast/uast-specification.html
Currently we declare different ASTs for different protocols. We
have
AvroF
for Avro,ProtobufF
for Protocol Buffers,MuF
fordescribing Mu services...
Table of Contents
the problem
However, we are repeating ourselves a lot. See the implementation of
AvroF:
And the implementation of MuF:
We repeat basically all the leaves of the AST, because all different
IDLs allow to represent more or less the same types, with minor
differences.
Also, having all those different ASTs means that we cannot create
functions that work on generic ASTs, but need to specify them.
The solution
I would prefer using a unique set of classes representing different
types possible in any schema, and then the specific schemata pick and
choose from them in order to assemble their ADT, as follows.
First we would need to declare all our base leaves:
N.B: these classes do not extend anything, they're plain old case
classes.
Then, for combining those case classes into an Algebraic Data Type, we
can use coproducts. Coproducts and its motivations are well explained
in this paper.
Higher kind coproducts are implemented in several libraries in the
scala FP world, such as Scalaz, Cats, and Iota. In order to do some
dogfooding, we'll be using iota's implementation.
Then, we would assemble our types using coproducts, as follows:
See how, in this case, each AST declaration decided to pick only the
types it needed.
Usage
The only missing part of this idea is how to use it. One of the
features motivating this change is code reuse, and generic
programming. In the following code block you can see how some generic
transformations on these ASTs could be implemented.
As you can see, these functions do not work on a specific AST.
Instead, they are generic, meaning that they will work on any AST for
which some injections exist.
work to be done
We need to implement a way of traverse derivation for Iota's
coproducts. Traverse can be derived mechanically, and I think the
fastest way would be a generic-like derivation a la shapeless.
resources
some useful resources for understanding this approach are this
talk and this paper.
The text was updated successfully, but these errors were encountered: