Cats-based Scala library for free applicative schemas. Core module of morphling initially was a cats-based port of the excellent Kris Nuttycombe's xenomorph
All You need is love:
libraryDependencies ++= Seq(
"com.github.danslapman" %% "morphling" % "4.0.0", //core module
"com.github.danslapman" %% "morphling-circe" % "4.0.0",
"com.github.danslapman" %% "morphling-reactivemongo" % "4.0.0",
"com.github.danslapman" %% "morphling-typed-schema" % "4.0.0",
"com.github.danslapman" %% "morphling-scalacheck" % "4.0.0",
"com.github.danslapman" %% "morphling-tapir" % "4.0.0"
)
| morphling | cats | circe | reactivemongo | typed-schema | scalacheck | tofu | glass | tapir |
|---|---|---|---|---|---|---|---|---|
| 4.0 | 2.8 | 0.14.2 | 1.0.3 / 1.1.0-RC6 | 0.14.3 | 1.15.3 | - | 0.3 | 1.0.0 |
| 3.1 | 2.8 | 0.14.2 | 1.0.3 / 1.1.0-RC6 | 0.14.3 | 1.15.3 | - | 0.1 | 1.0.0 |
| 3.0 | 2.8 | 0.14.2 | 1.0.3 / 1.1.0-RC6 | 0.14.3 | 1.15.3 | - | 0.1 | - |
| 2.7-glass | 2.8 | 0.13.0 | 1.0.3 | 0.14.3 | 1.15.3 | - | 0.1 | - |
| 2.7 | 2.4.2 | 0.13.0 | 1.0.3 | 0.14.3 | 1.15.3 | 0.10.0 | - | - |
| 2.6 | 2.4.2 | 0.12.3 | 0.19.3 | 0.12.5.1 | 1.14.3 | 0.7.9 | - | - |
| 2.4 | 2.0.0 | 0.12.3 | 0.19.3 | 0.12.5.1 | 1.14.3 | 0.7.9 | - | - |
| 2.1 | 2.0.0 | 0.12.3 | 0.19.3 | 0.12.4 | 1.14.3 | 0.7.4 | - | - |
| 2.0 | 2.0.0 | 0.12.3 | 0.19.3 | 0.11.1 | 1.14.3 | 0.6.1 | - | - |
| 1.5.1 | 2.0.0 | 0.12.3 | 0.19.3 | 0.11.1 | 1.14.0 | - | - | - |
| 1.5 | 2.0.0 | 0.12.3 | 0.19.0 | 0.11.0 | 1.14.0 | - | - | - |
| 1.1 | 2.0.0 | 0.11.1 | 0.17.0 | 0.11.0-beta6 | 1.14.0 | - | - | - |
| 1.0 | 1.6.1 | 0.11.1 | 0.16.4 | 0.11.0-beta6 | 1.14.0 | - | - | - |
First of all, You need to define a set of "scalar" types You like to support.
They can be Ints, BigInts, Instants, any type You mean to treat as scalar, actually.
You can find an example protocol in tests of core module:
importmorphling.HMutuimportmorphling.Schema._sealedtraitSType[F[_], I]
caseclassSNullT[F[_]]() extendsSType[F, Unit]
caseclassSBoolT[F[_]]() extendsSType[F, Boolean]
caseclassSIntT[F[_]]() extendsSType[F, Int]
caseclassSLongT[F[_]]() extendsSType[F, Long]
caseclassSFloatT[F[_]]() extendsSType[F, Float]
caseclassSDoubleT[F[_]]() extendsSType[F, Double]
caseclassSCharT[F[_]]() extendsSType[F, Char]
caseclassSStrT[F[_]]() extendsSType[F, String]
caseclassSArrayT[F[_], I](elem: F[I]) extendsSType[F, Vector[I]]Also it will be convenient to define some helper methods (will see their purpose later):
objectSType {
typeSSchema[I] =HMutu[SType, Schema, I]
valsNull= prim(HMutu[SType, Schema, Unit](SNullT()))
valsBool= prim(HMutu[SType, Schema, Boolean](SBoolT()))
valsInt= prim(HMutu[SType, Schema, Int](SIntT()))
valsLong= prim(HMutu[SType, Schema, Long](SLongT()))
valsFloat= prim(HMutu[SType, Schema, Float](SFloatT()))
valsDouble= prim(HMutu[SType, Schema, Double](SDoubleT()))
valsChar= prim(HMutu[SType, Schema, Char](SCharT()))
valsStr= prim(HMutu[SType, Schema, String](SStrT()))
defsArray[I](elem: Schema[SSchema, I]) = prim(HMutu[SType, Schema, Vector[I]](SArrayT(elem)))
}Now we can define a schema for an arbitrary type using our protocol:
importcats.syntax.apply._importmorphling.Schemaimportmorphling.Schema._importglass.macros._importSType._//Defined abovecase@OpticsclassServer(host: String, port: Int)
objectServer {
valserverSchema:Schema[SSchema, Server] = rec(
(
required("host", sStr, Server.host),
required("port", sInt, Server.port)
).mapN(Server.apply)
)
}That's it.
morphling provides a set of modules which enables generation of typeclasses
from schema instances. To use them You need to define an "implementation"
of protocol You previously defined. Let's do it for circe Encoding:
importcats._importio.circe.{Decoder, Encoder, Json}
importSType.SSchemaimportmorphling.Schema.SchemadefsTypeEncoder[F[_]:ToJson]: (SType[F, *] ~>Encoder) =new (SType[F, *] ~>Encoder) {
importToJson._overridedefapply[A](st: SType[F, A]):Encoder[A] = st match {
caseSNullT() =>Encoder.encodeUnit
caseSBoolT() =>Encoder.encodeBoolean
caseSIntT() =>Encoder.encodeInt
caseSLongT() =>Encoder.encodeLong
caseSFloatT() =>Encoder.encodeFloat
caseSDoubleT() =>Encoder.encodeDouble
caseSCharT() =>Encoder.encodeChar
caseSStrT() =>Encoder.encodeString
caseSArrayT(elem) =>Encoder.encodeVector(elem.encoder)
}
}
implicitvalprimFromJson:FromJson[SSchema] =newFromJson[SSchema] {
valdecoder=new (SSchema~>Decoder) {
defapply[I](s: SSchema[I]):Decoder[I] = sTypeDecoder[SSchema[I]#Inner].apply(s.unmutu)
}With such a transformation defined we can derive an Encoder for Server:
valencoder=Server.schema.encoder