Closed brndt closed 11 months ago
So generally it's not possible to abstract over 'raw' opaque types (or maybe it is, extensions do show up in the symbol list when reflected over :thinking:) but that is easily addressable if you sprinkle some newtype
magic over what you're currently doing:
trait Newtype[A] {
opaque type Type = A
def apply(value: A): Type = value
def wrapAll[F[_]](unwrapped: F[A]): F[Type] = unwrapped
def unwrapAll[F[_]](wrapped: F[Type]): F[A] = wrapped
extension (self: Type) {
def value: A = self
}
given wrappingTransformer: Transformer[A, Type] = apply
given uwnrappingTransformer: Transformer[Type, A] = _.value
}
Given the above your example would look like this (impl taken from here):
object TypeName extends Newtype[String]
export TypeName.Type as TypeName
and all of the Transformers
declared inside Newtype
are automatically available in implicit scope for TypeNames
so now when you call team.to[TeamView]
it should all be resolved for you.
You could also take a look at a Newtype
that does validation here.
But yeah as I said abstracting over raw opaque types is still an open question (I haven't seen a library that does it, it'd have to be by some kind of arbitrary convention eg. when you find a single arg apply method on the companion you use it for wrapping and if a .value
extension is found you use it for unwrapping, dunno if any of that is possible tho :smile:)
Hope I answered your question :heart:
arainko, thanks for such a detail response! Yes, the approach you've showed worked for me.
And also thanks for the library, it's very useful.
Seems like this approach doesn't work for enums:
What's the expected semantic here? To generate a Transformer[TeamStatus, String]
and vice versa I presume?
Oh actually after taking another look at it can you try this instead:
enum TeamStatus:
case Enabled, Disabled
object TeamStatus extends Newtype[String](...)
Although I don't think newtypes are a good fit for this particular use case since what you're after in this case is an isomorphism between your enum and a String (or a refinement), a newtype isn't really that
I think this sadly will be a no go for enums, what's the expected semantic here? To generate a
Transformer[TeamStatus, String]
and vice versa I presume?
Yes. The only way I find it possible to do is by encapsulating enum in object, making something like this:
object TeamStatus
extends Newtype[String](value => TeamStatus.Enum.values.has(value), value => InvalidTeamStatusError(value)):
enum Enum:
case Opened, Closed
export TeamStatus.Type as TeamStatus
Hello! I have one question regarding transformers. I have implemented Value Object pattern using opaque types so my validated data looks like this:
opaque type TeamName = String object TeamName: extension (value: TeamName) def value: String = value def create(value: String): IO[InvalidTeamNameError, TeamName] = ZIO.cond(value.inLengthRange(2 to 50), value, InvalidTeamNameError(value))
The problem that I have is that I can’t define generic transformers for these type of data because even if I extract ‘value’ method to common interface that is implemented by all necessary structure after, the generic transformers don’t work.
Is it somehow possible to create a generic transformer for this type of structures? I don't want to create a custom transformer for every opaque type.
PS: I've managed to resolve it by using case class and AnyVal as it is said in documentation but it would like to have a similar solution without overhead.