Suppose I have a class defined as
case class MyClass(myArray: Array[Int])
I want to only allow instances where the myArray parameter is of a specific length, say 3.
Can I enforce that myArray.size == 3 at type/compile level?
Would it be different with another collection? (say, an immutable list)
The only way I found to this is via a smart constructor at runtime, checking the size of myArray and failing - using for instance require.
Can I enforce that myArray.size == 3 at type/compile level?
Yes Scala can represent numbers at type-level. For example, using Sized from shapeless
import shapeless._
import syntax.sized._
import nat._
case class MyClass(myArray: Sized[Array[Int], _3])
MyClass(Sized[Array](1,2,3)) // ok
MyClass(Sized[Array](1,2,3,4)) // compile-time error
Array(1,2,3,4).sized(3).map(MyClass) // None
Here is how you might go about it in Scala 3 using scala.compiletime.ops facilities
import scala.compiletime.ops.int.S
enum MyArray[Size, +A]:
case Nil extends MyArray[0, Nothing]
case Cons[N <: Int, B](head: B, tail: MyArray[N, B]) extends MyArray[S[N], B]
import MyArray._
val xs: MyArray[3, Int] = Cons(1, Cons(2, Cons(3, Nil)))
val ys: MyArray[4, Int] = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
case class MyClass(myArray: MyArray[3, Int])
MyClass(xs) // ok
MyClass(ys) // compile-time error
You can use require with everything you want :)
case class MyClass(myArray: Array[Int]) {
require(myArray.size == 3, "Only arrays with 3 items are allowed!")
}
In case you pass an array that does not meet the requirement, it will throw an IllegalArgumentException.
Related
I am reading Functional Programming in Scala from Manning, authored by Paul Chiusano and Runar Bjarnason. In its 3rd chapter, there is a code to create a List and there are assignments to implement various methods of the list. Following is partial implementation of the my List
package src.Cons
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
object List {
//my issue is I do not want to pass a list to sum but want to use objectName.sum notation
def sum(ints:List[Int]):Int = ints match {
case Nil => 0
case Cons(x,xs) => x+sum(xs)
}
}
Question - How can I create my list such that I can call l.sum instead of List.sum(l)?
You can "PmL", as #Gabriele Petronella has suggested, or you can move the sum() method to the Cons class, as #DeadNight wrote, but before either of those can work you have to resolve the current conflict between your List object and your List trait.
The sum() in your List object can only sum a List[Int] but your class definitions use a more generic type member and, as such, you can't use + because the compiler doesn't know how to add two A types.
If you want to restrict your List to only handling numeric types then this will work.
case class Cons[A: Numeric](h:A, t:List[A]) extends List[A] {
def sum: A = List.sum(this)
}
object List {
def sum[A](ints:List[A])(implicit ev: Numeric[A]):A = ints match {
case Nil => ev.zero
case Cons(x,xs) => ev.plus(x, sum(xs))
}
}
val x = Cons(4, Cons(2, Nil))
x.sum // res0: Int = 6
Making sum a member
The problem is, you don't know how to sum the List[A] for every type A, only a List[Int]. If there was a way to allow calls when A is an Int...
Let's take a look at the standard library for that. We're interested in Option#flatten method because:
val o1 = Option(Option(3)).flatten // compiles
val o2 = Option(4).flatten // does not compile
Notice the weird implicit ev: <:<[A, Option[B]]. This is the key here - it's a thing that compiler provides for you, but only if it is known at compile time, that your Option[A] is a subtype of Option[Option[B]] for some type B. This is the trick that we can use.
sealed trait List[+A] {
def sum(implicit ev: A <:< Int): Int = this match {
case Nil => 0
case Cons(x, xs) => x + xs.sum // <- here x is magically converted to Int, so we can use plus
}
}
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
println(Cons(4, Cons(38, Nil)).sum) // 42
ScalaFiddle
Notice that you can write <:<[A, B] as A <:< B.
NB: there's also =:=[A, B] type, for when your A is exactly Int - you can use either of those
Doing better?
Actually, std library has sum method and it's type is even weirder:
def sum(implicit ev: Numeric[A]). Doing so allows it to work on any number-like type like Double and Int, and has the operations for comparison, subtraction, multiplication, etc. So you can make it even more generic. I suggest you do it after reading a chapter about Monoids, tho :)
You can use the so-called "Pimp my Library" pattern.
Define an implicit class ListOps
implicit class ListOps[+A](list: List[A]) {
def sum = List.sum(this)
}
and now you can call list.sum. The implicit conversion will be triggered and the compiler will interpret it as ListOps(list).sum.
Move the definition of sum inside the definition of List trait
You can leave the concrete definitions to Nil & Cons
package src.Cons
sealed trait List[+A] {
def sum: Int
}
case object Nil extends List[Nothing] {
val sum: Int = 0
}
case class Cons[+A](h:A, t:List[A]) extends List[A] {
def sum: Int = h + t.sum
}
Given a function f, that, given a Map[String, MyType], returns a HList:
package net
import shapeless._
sealed trait MyType
case object MyInt extends MyType
case object MyStr extends MyType
object Mapper {
def f(m: Map[String, MyType]): HList = m.foldLeft[HList](HNil){ (acc, elem) =>
val (key, t) = elem
t match {
case MyInt => classOf[Int] :: acc
case MyStr => classOf[String] :: acc
}
}
}
I tested it:
import net._
val list = Map("foo" -> MyInt, "bar" -> MyStr)
scala> Mapper.f(list)
res0: shapeless.HList = class java.lang.String :: int :: HNil
How can I use the above approach (or another one) to build a case class with members matching the String keys, and the types given by the output of f?
So, I'm looking for:
g(Map("foo" -> MyInt, "bar" -> MyStr)) to output case class X(foo: Int, bar: String) where X is arbitrarily chosen, i.e. not important at this point.
I thought of using Generic[X], but I don't know how to get a Generic without a case class first.
The thing you're trying to do can't happen in Scala with compile time verification. The issue here is exactly as you've elaborated, you don't have the definition of the case class you're trying to build in advance. That definition provides the scaffolding to use a Record type to construct the isomorphism.
That said, we might be able to work something with invoke dynamic and reflection but I'm unclear on how you'd even take advantage of that in code. You wouldn't know the field names in advance nor their types. So how would you even write code around them?
This is my second try to define the problem, I can't get my head around it.
I want to be able to define an algebraic type and define a simple typeclass over it, let's say Show. In haskell I do:
data Tree a = EmptyTree | Node a deriving (Show)
Now, if I type EmptyTree - haskell can show it, so it belongs to Show.
Now I am trying to do the same in scala:
sealed abstract class Tree[+T]
case object EmptyTree extends Tree[Nothing]
case class Node[T](value: T) extends Tree[T]
Then I define Show around it:
implicit def show[T] = Show.showA[Tree[T]]
I can do println((EmptyTree : Tree[Int]).show). But I can't do println(EmptyTree.show) (response is value show is not a member of object EmptyTree)
I have to write additional:
implicit class MyShowOps[A, +T <: Tree[A]](t: T) {
def showMy(implicit ev: Show[Tree[A]]): String = ev.shows(t)
}
And only then I can do println(EmptyTree.showMy)
It still doesn't sound correct, I believe either I am trying to do a wrong thing and I am not supposed to apply Show like that and should use my construction only as Tree[T] or I am missing a proper construction from Scalaz.
Scala's representation of ADTs differs from Haskell's in that its constructors have their own types. This is partly about practical interoperability—using subtyping is natural on the JVM—and it has both advantages and disadvantages.
You're running into one of the disadvantages, which is that having values that are statically typed as a constructor type often complicates type inference and implicit resolution.
Type class instances are statically resolved, and in your case specifically Show isn't contravariant, so an instance for Tree[T] isn't an instance for EmptyTree.type. The most idiomatic solution from the Scalaz perspective is to provide smart constructors that return the ADT type:
import scalaz.Show, scalaz.syntax.show._
sealed abstract class Tree[+T]
object Tree {
private[this] case object EmptyTree extends Tree[Nothing]
private[this] case class Node[T](value: T) extends Tree[T]
val emptyTree: Tree[Nothing] = EmptyTree
def node[T](value: T): Tree[T] = Node(value)
implicit def show[T]: Show[Tree[T]] = Show.showA[Tree[T]]
}
Now you can write Tree.emptyTree.show.
Note that this problem also turns up in even simpler contexts. For example, suppose we want to fold over a list with an Option as the accumulator:
scala> List(1, 2, 3).foldLeft(Some(0))((acc, i) => acc.map(_ + i))
<console>:11: error: type mismatch;
found : Option[Int]
required: Some[Int]
List(1, 2, 3).foldLeft(Some(0))((acc, i) => acc.map(_ + i))
^
Because the inferred type for Some(0) is Some[Int], not Option[Int], the type parameter that's inferred for the foldLeft method is too restrictive for the result of the map.
It would be nice if the standard library provided Option.none and Option.some "constructors" for cases like this, but it doesn't, so you either have to put a type annotation on the first argument or use something like Scalaz's none and some:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> List(1, 2, 3).foldLeft(some(0))((acc, i) => acc.map(_ + i))
res0: Option[Int] = Some(6)
In your case you presumably control the ADT definition, so you can provide smart constructors like this yourself.
I have a Scala collection that contains objects of different subtypes.
abstract class Base
class A extends Base
class B extends Base
val a1 = new A()
val a2 = new A()
val b = new B()
val s = List(a1, a2, b)
I'd like to filter out all the A objects or the B objects. I can do this easily if I know the object I want to filter on at compile time.
s.filter(_.isInstanceOf[A]) // Give me all the As
s.filter(_.isInstanceOf[B]) // Give me all the Bs
Can I do it if I only know the object type to filter on at runtime? I want to write a function like this.
def filterType(xs:List[Base], t) = xs.filter(_.isInstanceOf[t])
Where t indicates whether I want objects of type A or B.
Of course I can't actually write it this way because of type erasure. Is there an idiomatic Scala way to work around this using type tags? I've been reading the Scala type tag documentation and relevant StackOverflow posts, but I can't figure it out.
This has come up a few times. Duplicate, anyone?
scala> trait Base
defined trait Base
scala> case class A(i: Int) extends Base
defined class A
scala> case class B(i: Int) extends Base
defined class B
scala> val vs = List(A(1), B(2), A(3))
vs: List[Product with Serializable with Base] = List(A(1), B(2), A(3))
scala> def f[T: reflect.ClassTag](vs: List[Base]) = vs collect { case x: T => x }
f: [T](vs: List[Base])(implicit evidence$1: scala.reflect.ClassTag[T])List[T]
scala> f[A](vs)
res0: List[A] = List(A(1), A(3))
Type erasure will destroy any information in type parameters, but objects still know what class they belong to. Because of this, we cannot filter on arbitrary types, but we can filter by class or interface/trait. ClassTag is preferable to TypeTag here.
import scala.reflect.ClassTag
def filterType[T: ClassTag](xs: List[Base]) = xs.collect {
case x: T => x
}
Which we can use like:
scala> filterType[B](s)
res29: List[B] = List(B#42096939)
scala> filterType[Base](s)
res30: List[Base] = List(A#8dbc09c, A#625f8cc7, B#42096939)
This method is safe at run-time if type T is not generic. If there was a class C[T] extends Base we could not safely filter on C[String].
Is it possible to generically replace arguments in a case class? More specifically, say I wanted a substitute function that received a "find" case class and a "replace" case class (like the left and right sides of a grammar rule) as well as a target case class, and the function would return a new case class with arguments of the find case class replaced with the replace case class? The function could also simply take a case class (Product?) and a function to be applied to all arguments/products of the case class.
Obviously, given a specific case class, I could use unapply and apply -- but what's the best/easiest/etc way to generically (given any case class) write this sort of function?
I'm wondering if there is a good solution using Scala 2.10 reflection features or Iso.hlist from shapeless.
For example, what I really want to be able to do is, given classes like the following...
class Op[T]
case class From(x:Op[Int]) extends Op[Int]
case class To(x:Op[Int]) extends Op[Int]
case class Target(a:Op[Int], b:Op[Int]) extends ...
// and lots of other similar case classes
... have a function that can take an arbitrary case class and return a copy of it with any elements of type From replaced with instances of type To.
If you'll pardon the plug, I think you'll find that the rewriting component of our Kiama language processing library is perfect for this kind of purpose. It provides a very powerful form of strategic programming.
Here is a complete solution that rewrites To's to From's in a tree made from case class instances.
import org.kiama.rewriting.Rewriter
class Op[T]
case class Leaf (i : Int) extends Op[Int]
case class From (x : Op[Int]) extends Op[Int]
case class To (x : Op[Int]) extends Op[Int]
case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int]
case class Target2 (c : Op[Int]) extends Op[Int]
object Main extends Rewriter {
def main (args : Array[String]) {
val replaceFromsWithTos =
everywhere {
rule {
case From (x) => To (x)
}
}
val t1 = Target1 (From (Leaf (1)), To (Leaf (2)))
val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4)))))
println (rewrite (replaceFromsWithTos) (t1))
println (rewrite (replaceFromsWithTos) (t2))
}
}
The output is
Target1(To(Leaf(1)),To(Leaf(2)))
Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4)))))
The idea of the replaceFromsWithTos value is that the rule construct lifts a partial function to be able to operate on any kind of value. In this case the partial function is only defined at From nodes, replacing them with To nodes. The everywhere combinator says "apply my argument to all nodes in the tree, leaving unchanged places where the argument does not apply.
Much more can be done than this kind of simple rewrite. See the main Kiama rewriting documentation for the gory detail, including links to some more examples.
I experimented a bit with shapeless and was able to come up with the following, relatively generic way of converting one case class into another:
import shapeless._ /* shapeless 1.2.3-SNAPSHOT */
case class From(s: String, i: Int)
case class To(s: String, i: Int)
implicit def fromIso = Iso.hlist(From.apply _, From.unapply _)
implicit def toIso = Iso.hlist(To.apply _, To.unapply _)
implicit def convert[A, B, L <: HList]
(a: A)
(implicit srcIso: Iso[A, L],
dstIso: Iso[B, L])
: B =
dstIso.from(srcIso.to(a))
val f1 = From("Hi", 7)
val t1 = convert(f1)(fromIso, toIso)
println("f1 = " + f1) // From("Hi", 7)
println("t1 = " + t1) // To("Hi", 7)
However, I was not able to get the implicits right. Ideally,
val t1: To = f1
would be sufficient, or maybe
val t1 = convert(f1)
Another nice improvement would be to get rid of the need of having to explicitly declare iso-implicits (fromIso, toIso) for each case class.
I don't think you'll really find a better way than just using unapply/apply through pattern matching:
someValue match {
case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c)
// . . .
}
You have to write out the rules to associate FindCaseClass with ReplaceCaseClass somehow, and although you might be able to do it a little more succinctly by somehow just using the names, this has the added benefit of also checking the number and types of the case class fields at compile time to make sure everything matches just right.
There is probably some way to do this automatically using the fact that all case classes extend Product, but the fact that productElement(n) returns Any might make it a bit of a pain—I think that's where reflection would have to come in. Here's a little something to get you started:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
val iter = From(5,"x",Nil).productIterator
val f = To.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
// res0: Any = To(5,x,List())
But really, I think you're better off with the pattern-matching version.
Edit: Here is a version with the relavent code refactored into a method:
case class From(i: Int, s: String, xs: Seq[Nothing])
case class To(i: Int, s: String, xs: Seq[Nothing])
type Curryable = { def curried: _ => _ }
def recase(from: Product, to: Curryable) = {
val iter = from.productIterator
val f = to.curried
iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) }
}
recase(From(5,"x",Nil), To)
// res0: Any = To(5,x,List())