Exhaustive type matching for sealed trait - scala

I'd like to be able to exhaustively match on the types for implementations of a sealed trait, something like in the example below. Ideally I'd like to avoid using reflection (TypeTags) and implicit conversions if possible. Is there any way to exhaustively match on the type of a sealed trait?
object DataTypes {
sealed trait StrFy {
def stringify: String
}
final case class StrFyString(s: String) extends StrFy {
def stringify = s
}
final case class StrFyInt(i: Int) extends StrFy {
def stringify = i.toString
}
def stringifyThings[T <: StrFy](values: T*): String = {
val label = T match {
case StrFyString => "string"
case StrFyInt => "integer"
// cases that don't extend StrFy cause a compile error
}
"The " + label + " values are: " + values.map(_.stringify.fold("")(_+", "+_))
}
def printStringified(): Unit = {
println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
}
}

There is no way to get a value given a type unless you use a typeclass which you ruled out by "no implicits". Therefore, you need to match against an instance.
object DataTypes extends App {
sealed trait StrFy {
def stringify: String
}
final case class StrFyString(s: String) extends StrFy {
def stringify = s
}
final case class StrFyInt(i: Int) extends StrFy {
def stringify = i.toString
}
def stringifyThings[T <: StrFy](values: T*): String = {
def label(value: T) = value match {
case _:StrFyString => "string"
case _:StrFyInt => "integer"
// cases that don't extend StrFy cause a compile error
}
// Will throw if values is empty
"The " + label(values.head) + " values are: " + values.map(_.stringify).mkString(", ")
}
def printStringified(): Unit = {
println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
}
printStringified()
}
Implicits are not that scary though :) have a look:
object DataTypes extends App {
sealed trait StrFy[T] {
def stringify(value: T): String
def label: String
}
implicit object StrFyString extends StrFy[String] {
override def stringify(value: String): String = value
override def label: String = "string"
}
implicit object StrFyInt extends StrFy[Int] {
override def stringify(value: Int): String = value.toString
override def label: String = "integer"
}
def stringifyThings[T: StrFy](values: T*): String = {
val strFy = implicitly[StrFy[T]]
// Safe even on empty values
"The " + strFy.label + " values are: " + values.map(strFy.stringify).mkString(", ")
}
def printStringified(): Unit = {
println(stringifyThings("foo", "bar")) // should print: "the string values are: foo, bar"
println(stringifyThings(1, 2, 3)) // should print: "the integer values are: 1, 2, 3"
}
printStringified()
}
The compiler does the "pattern matching" for you, providing the correct instance given the input type.
You see that typeclasses allow you to get a value, here label, given only a type. This is a quite fundamental concept that makes typeclass-like polymorphism stronger than the subtyping one :)

Related

General-purpose method that returns a union type

I have a union type Int and String from Union type Scala and I want to add it to general-purpose method. Could u help me to write this method, without compile error.
object OrTypeMain extends App {
class StringOrInt[T]
object StringOrInt {
implicit object IntWitness extends StringOrInt[Int]
implicit object StringWitness extends StringOrInt[String]
}
object Bar {
def foo[T: StringOrInt](x: T): Unit = x match {
case _: String => println("str")
case _: Int => println("int")
}
// target method
def reverse[T: StringOrInt](x: T): StringOrInt = x match { // not compile
def reverse[T: StringOrInt](x: T): T = x match { // not compile too
case x: String => x + "new"
case y: Int => y + 5
}
}
Bar.reverse(123)
Bar.reverse("sad")
}
Why reverse doesn't compile is explained here:
Why can't I return a concrete subtype of A if a generic subtype of A is declared as return parameter?
Type mismatch on abstract type used in pattern matching
Replace runtime pattern matching with compile-time type class. StringOrInt is already a type class. Just move your operations there.
trait StringOrInt[T] {
def reverse(t: T): T
}
object StringOrInt {
implicit object IntWitness extends StringOrInt[Int] {
override def reverse(t: Int): Int = t + 5
}
implicit object StringWitness extends StringOrInt[String] {
override def reverse(t: String): String = t + "new"
}
}
def reverse[T: StringOrInt](x: T): T = implicitly[StringOrInt[T]].reverse(x)

Impicit function can't be injected

I have the following code:
case class Number (value:Int)
and
class Calculator {
def performCalc( input:Number)(implicit calc: (Number=>Number) ) = calc(input)
}
Now, when I, in a specs2 test try this:
class CalculatorTest extends mutable.Specification {
"Calculator" should {
"* Accept explicit calculation parameter" in {
implicit val addTwelve = (input: Number) => Number(input.value + 12)
val calc = new Calculator()
val result = calc.performCalc(Number(4))
result must beEqualTo(16)
}
}
}
I expected the 'addTwelve' function to be injected implicitly as a parameter of performCalc. However, I get the following failure:
Error:(49, 42) ambiguous implicit values:
both method $conforms in object Predef of type [A]=> <:<[A,A]
and value addTwelve of type nl.example.Number => nl.example.Number
match expected type nl.example.Number => nl.example.Number
val result = calc.performCalc(Number(4))
^
What am I doing wrong? It should be possible to use methods as implicits, right?
Scala: 2.11.7
Yes this is technically a valid use of implicit, but it's not a very strong use case. Specifically, there is a pre-existing implicit that provides Number=>Number. The compiler is having trouble telling which implicit method you really want.
What's better is to wrap this method into a trait as a "tag" for the implicit type.
case class Number(value: Int)
trait CalcMethod {
def perform(n: Number): Number
}
class Calculator {
def performCalc(input:Number)(implicit calc: CalcMethod) = calc.perform(input)
}
class CalculatorTest extends mutable.Specification {
"Calculator" should {
"* Accept explicit calculation parameter" in {
implicit val addTwelve: CalcMethod = new CalcMethod {
def perform(input: Number) = Number(input.value + 12)
}
val result = new Calculator().performCalc(Number(4))
result must beEqualTo(16)
}
}
}
EDIT:
This is maybe closer to what you want:
case class Number(value: Int)
implicit class CalcMethod(val perform: Number => Number)
class Calculator {
def performCalc(input:Number)(implicit calc: CalcMethod) = calc.perform(input)
}
Then you can use it like so:
implicit val addTwelve: CalcMethod = (input: Number) => Number(input.value + 12)
val result = new Calculator().performCalc(Number(4))

Scala - Creating a function to produce Unary string or integer values

Hello fellow Scala programmers,
I have a question or need assurance that this is the correct why to write a function which can only return the values "+", "-" or 1, -1. I'm basically creating a unary function which can return 1 or -1 for arithmetic purposes and "+" or "-" display purposes depending on the parameters passed. Here's the code...
package testit
object testit {
abstract class UPairs[T]{
def getFirst():T
def getSecond():T
}
class UString() extends UPairs[String]{
def getFirst():String = "+"
def getSecond():String = "-"
}
object UString {
def apply() = new UString()
}
class UInt() extends UPairs[Int]{
def getFirst():Int = 1
def getSecond():Int = -1
}
object UInt {
def apply() = new UInt()
}
abstract class Unary
case class Positive() extends Unary
case class Negative() extends Unary
def unaryFunc[T](u:Unary, p:UPairs[T]):T = {
u match {
case Positive() => p.getFirst()
case Negative() => p.getSecond()
}
}
def main(args:Array[String]):Unit = {
println(unaryFunc(Positive(), UString()))
println(unaryFunc(Negative(), UInt()))
println(unaryFunc(Positive(), UInt()))
}
}
Note: The above code works and produces the correct results and restricts the input to valid parameters.
I guess I'll have to go with this solution.
package exprP
object ExprObj {
abstract class UPair[T] {
def getPositive():T
def getNegative():T
}
object UString extends UPair[String] {
def getPositive():String = "+"
def getNegative():String = "-"
}
object UInt extends UPair[Int] {
def getPositive():Int = 1
def getNegative():Int = -1
}
abstract class Unary
case class Positive() extends Unary
case class Negative() extends Unary
def unaryFunc[T](u:Unary, p:UPair[T]):T = {
u match {
case Positive() => p.getPositive()
case Negative() => p.getNegative()
}
}
}

Accessing class-level values of type parameters in Scala

I have a trait and a case class implementing it. One of the features of the trait that the implementations override is a default value. I can't find a good way to access this default value from a class that is parametrized by a specific implementation of that trait.
This is minimal code, so it doesn't really demonstrate the motivation anymore, but it does demonstrate the error:
import scala.language.implicitConversions
trait Distance[T] {
val Zero: T
def +( that: T ): T
}
case class DoubleDistance( val v: Double ) extends Distance[DoubleDistance] {
val Zero = DoubleDistance( 0.0 )
def +( that: DoubleDistance ) = DoubleDistance( v + that.v )
}
object DistanceImplicits {
implicit def DoubleToDistance( v: Double ) = new DoubleDistance( v )
}
class User[T<:Distance[T]] {
val default: T = T.Zero // This line gives me a compilation error
}
The error I get is
not found: value T
When I needed to construct an Array[T] inside my User class I could get that to work by adding implicit typetag:ClassTag[T] to my arguments, but that doesn't seem to have any effect here.
First, why this doesn't work: consider a different implementation of Distance.
case class DoubleDistance1(val v: Double) extends Distance[DoubleDistance1] {
val Zero = this
def +(that: DoubleDistance1) = ??? // doesn't matter
}
What would you expect DoubleDistance1.Zero to mean? You can make it work using a "type class":
trait DistanceOps[T] {
val zero: T
def add(d1: T, d2: T): T
}
// just to let you write distance1 + distance2
implicit class RichDistance[T](d: T)(implicit ops: DistanceOps[T]) {
def +(other: T) = ops.add(d, other)
}
case class DoubleDistance(v: Double)
object DoubleDistance {
implicit object DoubleDistanceOps extends DistanceOps[DoubleDistance] {
val zero = DoubleDistance(0.0)
def add(d1: DoubleDistance, d2: DoubleDistance) = DoubleDistance(d1.v + d2.v)
}
}
// how to use
class User[T](implicit ops: DistanceOps[T]) {
val default: T = ops.zero
}
This looks like a classic use case for the type class pattern. Rather than associating an instance of the Distance trait with each value of interest, you associate one with each type of interest:
trait Distance[T] {
val Zero: T
def +( a: T, b: T ): T
}
implicit object DoubleDistance extends Distance[Double] {
val Zero = 0.0
def +( a: Double, b: Double ) = a + b
}
class User[T : Distance] {
val default: T = implicitly[Distance[T]].Zero
}
Where is that Zero supposed to come from? Are you looking to do something like this?
class User[T<:Distance[T]] {
self:Distance[T] =>
val default: T = Zero
}

How do I create an enum in scala that has an extra field

In Java I have something like this
public enum FlatFileHeaderMapping {
HEADER_EL(1),
HEADER_RESERVED1(5),
HEADER_RESERVED2(2),
HEADER_MESSAGE_TYPE(4)
public final int fieldSize;
private FlatFileHeaderMapping(int fieldSize) {
this.fieldSize = fieldSize;
}
}
which I can then use it place each line into a map and later access the keys in the map via this enum (like symbols)
Enumeration does not have this quality as far as I can see, and case classes are not ordered like the enum declarations - so cannot be used to match a record layout as shown above. At least not without the support of an ordered collection.
I could be missing something obvious, hence the question!
Thanks
Ray
overthink is right, but there's a less verbose way of declaring the case objects:
sealed abstract class FlatFileHeaderMapping(val fieldSize: Int)
case object HEADER_EL extends FlatFileHeaderMapping(1)
case object HEADER_RESERVED1 extends FlatFileHeaderMapping(5)
case object HEADER_RESERVED2 extends FlatFileHeaderMapping(2)
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping(4)
You could try using case objects:
sealed trait FlatFileHeaderMapping { val fieldSize: Int }
case object HEADER_EL extends FlatFileHeaderMapping { val fieldSize = 1 }
case object HEADER_RESERVED1 extends FlatFileHeaderMapping { val fieldSize = 5 }
case object HEADER_RESERVED2 extends FlatFileHeaderMapping { val fieldSize = 2 }
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping { val fieldSize = 4 }
You can then use the enum like so:
object Test {
def foo(x: FlatFileHeaderMapping) {
val result =
x match {
case HEADER_EL => "it's a HEADER_EL!"
case other => "its field size is: " + other.fieldSize
}
println(result)
}
def main(args: Array[String]) {
foo(HEADER_EL)
foo(HEADER_MESSAGE_TYPE)
}
}
The main nicety you get here is compile-time checking that all enum values are handled. i.e in the x match { ... } code above you'd get a compile error if you didn't have the 'case other => ...` clause in there.
I'm pretty much just restating this answer, which lists pros and cons of this approach.
object Direction extends Enumeration {
val North = Value("North")
val East = Value("East")
val South = Value("South")
val West = Value("West")
}
scala> import Direction._
scala> values foreach println
scala> val map = HashMap(North -> 1, South -> 2)
This is answered in Enumeration with constructor and lookup table
A simpler solution exist for integer value:
object FlatFileHeaderMapping extends Enumeration {
type FlatFileHeaderMapping = Value
val HEADER_EL = Value(1, "HEADER_EL")
val HEADER_RESERVED1 = Value(5, "HEADER_RESERVED1")
val HEADER_RESERVED2 = Value(2, "HEADER_RESERVED2")
val HEADER_MESSAGE_TYPE = Value(4, "HEADER_MESSAGE_TYPE")
}
Reproducing the contents of the accepted answer, as it's hidden behind a broken Tumblr link (that I accessed via Archive.org), which in turn points to this page.
trait Enum { //DIY enum type
import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia
type EnumVal <: Value //This is a type that needs to be found in the implementing class
private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values
//Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
val oldVec = get
val newVec = oldVec :+ newVal
if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
}
def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type
//This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal
def name: String //All enum values should have a name
override def toString = name //And that name is used for the toString operation
override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
}
}
//And here's how to use it, if you want compiler exhaustiveness checking
object Foos extends Enum {
sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/
val F = new EnumVal { val name = "F" }
val X = new EnumVal { val name = "X" }
}
/**
scala> Foos.values.find(_.name == "F")
res3: Option[Foos.EnumVal] = Some(F)
scala> Foos.X.ordinal
res4: Int = 1
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
}
<console>:10: warning: match is not exhaustive!
missing combination $anon$1
missing combination $anon$2
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
case Foos.F => println("dogpig")
}
doSmth: (foo: Foos.EnumVal)Unit
**/
//But if you don't care about getting exhaustiveness warnings, you can do:
object Foos extends Enum {
case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */
val F = EnumVal("F")
val X = EnumVal("X")
}
/**
Which is a bit less boilerplatey.
Cheers,
√
**/