I'm trying to port some code from Haskell to Scala and came across this pair of functions:
newtype OO f j a b
Composition of type constructors: unary with binary. Called
StaticArrow
in [1].Constructors
OO unOO :: f (a `j` b)
I tried to do this in Scala with something like
type OO[F[_], J[_, _], A, B] = F[J[A, B]]
but I think that's just unOO
.
Can someone explain two things:
(1) What do OO
and unOO
actually do? (I think they're doing some type coercions, but I can't be sure why.)
(2) Is there an equivalent to these in Scala? Do I even need an equivalent to these in Scala?
For context, I'm trying to port the module fungll-combinators. There are examples of the use of OO
and unOO
in the file Join.hs file
Any help appreciated.
-- Haskell
data OO f j a b = OO { unOO :: f (a `j` b) }
corresponds to
// Scala
case class OO[F[_], J[_, _], A, B](unOO: F[A J B])
while
-- Haskell
newtype OO f j a b = OO { unOO :: f (a `j` b) }
corresponds to
// Scala
case class OO[F[_], J[_, _], A, B](unOO: F[A J B]) extends AnyVal
But value classes (... extends AnyVal
) have limitations: 1 2.
Besides value classes, newtype
can be implemented in Scala with the library scala-newtype (Scala 2)
import io.estatico.newtype.macros.newtype
@newtype case class OO[F[_], J[_, _], A, B](unOO: F[A J B])
//scalac: {
// type OO[F[_$$1], J[_$$2, _$$3], A, B] = OO.Type[F, J, A, B];
// object OO extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def apply[F[_$$1], J[_$$2, _$$3], A, B](unOO: F[J[A, B]]): OO[F, J, A, B] = unOO.asInstanceOf[OO[F, J, A, B]];
// final implicit class Ops$newtype[F[_$$1], J[_$$2, _$$3], A, B] extends AnyVal {
// <paramaccessor> val $this$: Type[F, J, A, B] = _;
// def <init>($this$: Type[F, J, A, B]) = {
// super.<init>();
// ()
// };
// def unOO: F[J[A, B]] = $this$.asInstanceOf[F[J[A, B]]]
// };
// implicit def opsThis[F[_$$1], J[_$$2, _$$3], A, B](x: Ops$newtype[F, J, A, B]): Type[F, J, A, B] = x.$this$;
// @new _root_.scala.inline() implicit def unsafeWrap[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[Repr[F, J, A, B], Type[F, J, A, B]] = Coercible.instance;
// @new _root_.scala.inline() implicit def unsafeUnwrap[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[Type[F, J, A, B], Repr[F, J, A, B]] = Coercible.instance;
// @new _root_.scala.inline() implicit def unsafeWrapM[M[_], F[_$$1], J[_$$2, _$$3], A, B]: Coercible[M[Repr[F, J, A, B]], M[Type[F, J, A, B]]] = Coercible.instance;
// @new _root_.scala.inline() implicit def unsafeUnwrapM[M[_], F[_$$1], J[_$$2, _$$3], A, B]: Coercible[M[Type[F, J, A, B]], M[Repr[F, J, A, B]]] = Coercible.instance;
// @new _root_.scala.inline() implicit def unsafeWrapK[T[_[F[_$$1], J[_$$2, _$$3], A, B]]]: Coercible[T[Repr], T[Type]] = Coercible.instance;
// @new _root_.scala.inline() implicit def unsafeUnwrapK[T[_[F[_$$1], J[_$$2, _$$3], A, B]]]: Coercible[T[Type], T[Repr]] = Coercible.instance;
// @new _root_.scala.inline() implicit def cannotWrapArrayAmbiguous1[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[_root_.scala.Array[Repr[F, J, A, B]], _root_.scala.Array[Type[F, J, A, B]]] = Coercible.instance;
// @new _root_.scala.inline() implicit def cannotWrapArrayAmbiguous2[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[_root_.scala.Array[Repr[F, J, A, B]], _root_.scala.Array[Type[F, J, A, B]]] = Coercible.instance;
// @new _root_.scala.inline() implicit def cannotUnwrapArrayAmbiguous1[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[_root_.scala.Array[Type[F, J, A, B]], _root_.scala.Array[Repr[F, J, A, B]]] = Coercible.instance;
// @new _root_.scala.inline() implicit def cannotUnwrapArrayAmbiguous2[F[_$$1], J[_$$2, _$$3], A, B]: Coercible[_root_.scala.Array[Type[F, J, A, B]], _root_.scala.Array[Repr[F, J, A, B]]] = Coercible.instance;
// def deriving[TC$macro$1[_], F[_$$1], J[_$$2, _$$3], A, B](implicit ev: TC$macro$1[Repr[F, J, A, B]]): TC$macro$1[Type[F, J, A, B]] = ev.asInstanceOf[TC$macro$1[Type[F, J, A, B]]];
// def derivingK[TC$macro$1[_[_[_$$1], _[_$$2, _$$3], _, _]]](implicit ev: TC$macro$1[Repr]): TC$macro$1[Type] = ev.asInstanceOf[TC$macro$1[Type]];
// type Repr[F[_$$1], J[_$$2, _$$3], A, B] = F[J[A, B]];
// type Base = _root_.scala.Any {
// type __OO__newtype
// };
// abstract trait Tag[F[_$$1], J[_$$2, _$$3], A, B] extends _root_.scala.Any;
// type Type[F[_$$1], J[_$$2, _$$3], A, B] <: Base with Tag[F, J, A, B]
// };
// ()
//}
and opaque types (Scala 3)
// outside the current scope it's not known that OO is F[A J B]
opaque type OO[F[_], J[_, _], A, B] = F[A J B]
object OO:
def apply[F[_], J[_, _], A, B](x: F[A J B]): OO[F, J, A, B] = x
def unOO[F[_], J[_, _], A, B](x: OO[F, J, A, B]): F[A J B] = x