Skip to content

Instantly share code, notes, and snippets.

@nelanka
Created September 25, 2014 19:40
Show Gist options
  • Save nelanka/9e84b2e5514c22e7b688 to your computer and use it in GitHub Desktop.
Save nelanka/9e84b2e5514c22e7b688 to your computer and use it in GitHub Desktop.
MA Magic
Goal: To understand what this does:
mapChargesOverrides(iChargeOverrides).map(validateCharge).sequence[PartialApply1Of2[ValidationNEL, String]#Apply, Charge]
Lets look at the types leading up to the sequence operation:
mapChargesOverrides(iChargeOverrides) : Seq[Charge]
mapChargesOverrides(iChargeOverrides).map(validateCharge) : Seq[ValidationNEL[String, Charge]]
We can decompose using the type alias:
type ValidationNEL[E, X] = Validation[NonEmptyList[E], X]
So we're calling sequence on a value of type: Seq[Validation[NonEmptyList[String], Charge]]
Looking at the type parameters for the sequence call:
mapChargesOverrides(iChargeOverrides).map(validateCharge).sequence[PartialApply1Of2[ValidationNEL, String]#Apply, Charge]
Lets see what PartialApply1Of2 does:
trait PartialApply1Of2[T[_, _], A] { type Apply[B] = T[A, B] }
ValidationNEL is a type constructor with two arguments: ValidationNEL[_, _]
PartialApply1Of2[T[_, _], A] matches to PartialApply1Of2[ValidationNEL[_, _], String]
So, T is ValidationNEL and A is String
What does #Apply do?
trait PartialApply1Of2[T[_, _], A] { type Apply[B] = T[A, B] }
The #Apply returns the type of ValidationNel[String, _]
The second type parameter is unspecified.
Now back to sequence:
sequence[N[_], B] becomes sequence[ValidationNel[String, _], Charge]
This is fine since ValidationNel[String, _] is a type constructor of one argument; N[_]
So the original line becomes:
mapChargesOverrides(iChargeOverrides).map(validateCharge).sequence[ValidationNel[String, _], Charge]
Sequence will return the following type:
ValidationNEL[String, M[Charge]]
What is M?
In the definition of sequence, we have:
trait MA[M[_], A] extends PimpedType[M[A]] with MASugar[M, A] { ...
def sequence[N[_], B](implicit a: A <:< N[B], t: Traverse[M], n: Applicative[N]): N[M[B]] =
traverse((z: A) => (z: N[B]))
...
}
To nail down the types, look at what .sequence is called on: Seq[Validation[NonEmptyList[String], Charge]]
MA[M[_], A] matches to Seq[Validation[NonEmptyList[String], Charge]]
So, MA is Validation, M is NonEmptyList, and A is Charge
Now that we know what M is, we know that there must be a Traverse[NonEmptyList] in scope, which it is:
implicit def NonEmptyListTraverse: Traverse[NonEmptyList] = new Traverse[NonEmptyList] { ...
Finally, we have our return type: ValidationNEL[String, NonEmptyList[Charge]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment