Skip to content

Instantly share code, notes, and snippets.

@rahilb
Last active November 7, 2016 15:35
Show Gist options
  • Save rahilb/c37686954a28b70062938aa40ebe1ae9 to your computer and use it in GitHub Desktop.
Save rahilb/c37686954a28b70062938aa40ebe1ae9 to your computer and use it in GitHub Desktop.
Onzo

Implemented using typeclasses.

Implementation:

  1. Define the problem with types :
  • Encoder[A] & Decoder[A]. Encoder takes an A, and produces an AttributeValue. Decoder does the opposite.
  • KeyLike[A] coproduct for things that can be keys.
  • NamedKeyLike[A] a key that has a name, an encoder, and a decoder.
    • PrimaryKey - I suppose it's the Primary Key of the table
    • RangeKey ... a range key?
    • Key - just a regular key
    • MapKey[A] - helper for working with maps with a string key Map[String, A] to a Map[String, AttributeValue]
  • TableMapper[A]
    • key definition used by a Table[A]
    • defines encode & decode for A
  • Table[A] where we wire our code to the AWS lib
    • *: A TableMapper[A]
  1. Write some boilerplate
  • to summon instances of an Encoder and Decoder for an A (apply[A](implicit d: Decoder[A]) = d)
  • to create a Decoder from a AttributeValue => A (do the same for encoder)
  • implicits for things that we care about (string, bool, &c)
  • for higher kinded types in the std lib (Option, CBF, Map)
  • for coproducts (either, not shapeless' :+:: is that thing called a CList? I don't remember.)
  • for cats' monad type
  1. Write some slightly more involved boilerplate
  • dynamodb package object integrates an HList of KeyLikes with our implicits:
    • add a method as[B] to an Hlist that only contains KeyLike things, and it returns an anonymous TableMapper[B]
    • takes a Generic (shapeless) so we can get the type of the Repr of the type we're working with (will be an HList)
    • takes a Zipper so we can zip with the names from the keylikes
      • I suppose labelledgeneric wasn't used as that's too much convention?
    • Anonymous TableMappers
      • primaryKey: Find the first PK in our list, it is the primary key.
        • set up our PK encoder to delegate to the instance resolved by the PK
        • is there no witness type that can make sure there's only one PK?
      • rangeKey: do the same procedure as PK for a range key.
      • encode: zip our hlist of keylikes (a) with the Hlist of our values from Generic a.zip(entityGen.to(b)) and foldLeft, adding them all to a map[string, attributevalue]
      • decode: do the opposite of encode
        • TODO: checkout Poly and Case.Aux and why we need so many cases for Poly.

Testing

  • Done with property based testing
  • Using scalacheck for Arbitrary generators
  • Discipline for Laws - looks like an alternative to the Scalacheck builtin test runner... not sure what the benefits are.
    • nice reduction of boilerplate, though I suppose the effect is the same as other libraries, you still have to have the checkAll("Codec[Double]", CodecTests[Double].codec) boilerplate, and it's more to grok than just a def
    • has a pretty cool name
  • lots of different stuff used, lots to grok
    • org.typelevel.discipline.scalatest.Discipline (???) this is where the Laws thing comes in.
    • cats for some laws integration (wildcard import so hard to tell, some implicits somewhere I imagine)... looks like for Equality also.
    • scalacheck arbitrary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment