Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to extend type declarations?

I'm trying to use shapeless' Coproduct for error type aggregation. The following is an attempt to isolate the problem I currently have:

import shapeless._

case object F1
case object F2
type F12 = F1.type :+: F2.type :+: CNil

case object F3
case object F4
type F34 = F3.type :+: F4.type :+: CNil

type F1234 = F1.type :+: F2.type :+: F3.type :+: F4.type :+: CNil

def custom(f: Either[F12, F34]): F1234 = // how can I declare the resulting type?
  f.fold(_.extendRightBy[F34], _.extendLeftBy[F12])

object F1234Handler extends Poly1 {
  implicit def caseF1 = at[F1.type](_ => "got F1")
  implicit def caseF2 = at[F2.type](_ => "got F2")
  implicit def caseF3 = at[F3.type](_ => "got F3")
  implicit def caseF4 = at[F4.type](_ => "got F4")
}

custom(Left(Coproduct[F12](F2))).fold(F1234Handler) // got F2

How can I declare the result type in the custom fold without having to repeat myself? Ideally I don't want to declare F1234 in the way I did, I want to declare it by just making a union of the two existing type declarations, F12 and F34. This way I don't need to update the F1234 declaration whenever I add another failure type to either of these declarations. I can declare type F1234 = F1.type :+: F2.type :+: F34 but I can't declare type F1234 = F12 :+: F34 due to the CNil tail of the F12, which gets dropped by the extendBy operations.

like image 479
sparkle Avatar asked Jun 24 '15 07:06

sparkle


People also ask

Can you extend type TypeScript?

To extend types in TypeScript, we can use the extends keyword. to create the UserEvent interface that extends the Event type. We extend it by adding the string UserId field in UserEvent and inheriting the rest from Event .

Can an interface extend another interface TypeScript?

An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces. Use the extends keyword to implement inheritance among interfaces.

How do you use extended interface TypeScript?

Extending Interfaces in TypeScript # Use the extends keyword to extend interfaces in TypeScript, e.g. interface Dog extends Animal {age: number;} . The extends keyword allows us to copy the members from other named types and add new members to the final, more generic interface. Copied!


1 Answers

The situation isn't quite as bad as lmm's answer suggests, in part because Shapeless provides a ExtendBy type class that packages up ExtendLeftBy and ExtendRightBy. So if you really wanted a return type for custom that you didn't compute yourself and write out by hand, you could use ExtendBy:

import shapeless._, ops.coproduct.ExtendBy

case object F1
case object F2
type F12 = F1.type :+: F2.type :+: CNil

case object F3
case object F4
type F34 = F3.type :+: F4.type :+: CNil

def custom(f: Either[F12, F34])(implicit ext: ExtendBy[F12, F34]): ext.Out =
  f.fold(ext.right(_), ext.left(_))

Even if you did need to use ExtendLeftBy and ExtendRightBy directly, you could convince the compiler that they have the same output type a little more cleanly with Aux types and a single shared type parameter. So instead of this (a complete working version of lmm's code):

import ops.coproduct.{ ExtendLeftBy, ExtendRightBy }

def custom[ERO, ELO](f: Either[F12, F34])(implicit
  el: ExtendRightBy[F12, F34] { type Out = ELO },
  er: ExtendLeftBy[F12, F34] { type Out = ERO },
  w: ELO =:= ERO
): ERO = f.fold(l => w(el(l)), er(_))

You would just write this:

def custom[Out <: Coproduct](f: Either[F12, F34])(implicit
  extL: ExtendRightBy.Aux[F12, F34, Out],
  extR: ExtendLeftBy.Aux[F12, F34, Out]
): Out = f.fold(extL(_), extR(_))

In most cases if you know the input types statically, though, you'd just write out the return type yourself and skip the implicit parameter business altogether. The implicit evidence is only necessary when you're working with generic types, like this:

def custom[A <: Coproduct, B <: Coproduct](f: Either[A, B])(implicit
  ext: ExtendBy[A, B]
): ext.Out = f.fold(ext.right(_), ext.left(_))

This works for any two coproducts, not just F12 and F34.

like image 74
Travis Brown Avatar answered Sep 30 '22 03:09

Travis Brown