diff options
Diffstat (limited to 'core/shared/src/main/scala/interface.scala')
-rw-r--r-- | core/shared/src/main/scala/interface.scala | 36 |
1 files changed, 26 insertions, 10 deletions
diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala index 193a6f9..b06f350 100644 --- a/core/shared/src/main/scala/interface.scala +++ b/core/shared/src/main/scala/interface.scala @@ -1,6 +1,7 @@ package magnolia import language.higherKinds +import scala.annotation.tailrec /** represents a subtype of a sealed trait * @@ -122,13 +123,28 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] ( * @param makeParam lambda for converting a generic [[Param]] into the value to be used for * this parameter in the construction of a new instance of the case class * @return a new instance of the case class */ - def construct[Return](makeParam: Param[Typeclass, Type] => Return): Type + final def construct[Return](makeParam: Param[Typeclass, Type] => Return): Type = + rawConstruct(parameters map makeParam) + + /** constructs a new instance of the case class type + * + * Like [[construct]] this method is implemented by Magnolia and let's you construct case class + * instances generically in user code, without knowing their type concretely. + * + * `rawConstruct`, however, is more low-level in that it expects you to provide a [[Seq]] + * containing all the field values for the case class type, in order and with the correct types. + * + * @param fieldValues contains the field values for the case class instance to be constructed, + * in order and with the correct types. + * @return a new instance of the case class + * @throws IllegalArgumentException if the size of `paramValues` differs from the size of [[parameters]] */ + def rawConstruct(fieldValues: Seq[Any]): Type /** a sequence of [[Param]] objects representing all of the parameters in the case class * * For efficiency, this sequence is implemented by an `Array`, but upcast to a * [[scala.collection.Seq]] to hide the mutable collection API. */ - def parameters: Seq[Param[Typeclass, Type]] = parametersArray + final def parameters: Seq[Param[Typeclass, Type]] = parametersArray } /** represents a sealed trait and the context required to construct a new typeclass instance @@ -157,12 +173,12 @@ final class SealedTrait[Typeclass[_], Type](val typeName: String, * matches * @return the result of applying the `handle` lambda to subtype of the sealed trait which * matches the parameter `value` */ - def dispatch[Return](value: Type)(handle: Subtype[Typeclass, Type] => Return): Return = - subtypes - .map { sub => - sub.cast.andThen { v => - handle(sub) - } - } - .reduce(_ orElse _)(value) + def dispatch[Return](value: Type)(handle: Subtype[Typeclass, Type] => Return): Return = { + @tailrec def rec(ix: Int): Return = + if (ix < subtypesArray.length) { + val sub = subtypesArray(ix) + if (sub.cast.isDefinedAt(value)) handle(sub) else rec(ix + 1) + } else throw new IllegalArgumentException(s"The given value `$value` is not a sub type of `$typeName`") + rec(0) + } } |