简体   繁体   中英

How can I define a type, that represents all binary or unary function taking ArgType as arguments and returning ReturnType?

I'm trying to create a type UnaryOrBinaryFunc , that could conceptually be defined like this:

 type A = ArgType => ReturnType
 type B = (ArgType, ArgType) => ReturnType
 type UnaryOrBinaryFunc = <Some code saying that UnaryOrBinaryFunc is either a A or a B>

in my code I tried to define UnaryOrBinaryFunc as: type UnaryOrBinaryFunc = A with B and it seemed to be acceptable for Eclipse, I sadly didn't find anything about using the with keyword in association with the type keyword on the Internet.

Now if you want a little bit of context on why I want to do that, I wrote something that looks like what I want to accomplish inside my project:

 sealed trait Token
 case object AND extends Token
 case object NOT extends Token

 type ArgType
 type ReturnType

 type A = ArgType => ReturnType
 type B = (ArgType, ArgType) => ReturnType
 type UnaryOrBinaryFunc = A with B

 def f: A = ???
 def g: B = ???

 Map[TokenType, UnaryOrBinaryFunc] (
    NOT -> f,
    AND -> g
 )

Notice that I used ??? for the definitions of f and g because I didn't want to bother defining them.

Now I get the following error, the same as in my project:

type mismatch; found : TypeDecl.A (which expands to) TypeDecl.ArgType ⇒
 TypeDecl.ReturnType required: TypeDecl.UnaryOrBinaryFunc (which expands to)
 TypeDecl.ArgType ⇒ TypeDecl.ReturnType with (TypeDecl.ArgType,  
 TypeDecl.ArgType) ⇒ TypeDecl.ReturnType

I really don't know how to do that, anyone has an idea of how I could define UnaryOrBinaryFunc so I don't have to modify what is inside my Map ?

Many thanks.

To answer the question directly....

There are several ways of defining a coproduct in Scala.

The simplest, and probably the best with just two types is to use Either :

type UnaryOrBinaryFunc =
  Either[(ArgType, ArgType) => ReturnType, ArgType => ReturnType]

Map[TokenType, UnaryOrBinaryFunc] (
    NOT -> Left(f),
    AND -> Right(g)
 )

You can write some aliases for Left and Right to make things more readable.

If you are using the Shapeless library, you can implement this with Coproducts as explained in this question:

type UnaryOrBinaryFunc =
  (ArgType, ArgType) => ReturnType :+: ArgType => ReturnType

With plain scala, it's also possible to use a trick to create coproducts using implicits as shown here

sealed trait UnaryOrBinaryFunc
object UnaryOrBinaryFunc {
  implicit object UnaryFunc extends UnaryOrBinaryFunc[ArgType => ReturnType]
  implicit object BinaryFunc extends UnaryOrBinaryFunc[(ArgType, ArgType) => ReturnType]
}

Every function that processes UnaryOrBinaryFunc should take an implicit parameter of UnaryOrBinaryFunc as shown in the link given.

... However, I would question whether this complexity is necessary. To solve this problem I would do one of:

  • Define your functions to take List s or Product s, and switch on their lengths at runtime.

     type UnaryOrBinaryFunc = Seq[ArgType] => ReturnType 
  • Keep Unary and Binary functions separate, and make generic any functions that must process either.

     val unaryFns = Map[TokenType, UnaryFunc] ( NOT -> f, ... ) val binaryFns = Map[TokenType, BinaryFunc] ( AND -> g, ... ) def processUnaryOrBinaryFn[T](fn: T) = { ... } 

Which of all the above solutions is most appropriate will depend on what else you need to do :-).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM