Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala methods and higher-kinded type parameters

I am trying to define a method in scala that takes a generic type of S[_] <: Seq[Double] and returns a S[FixedLoad] (FixedLoad is a concrete type). But my implementation gives me errors and I can't figure out why. Despite I have tried so many times to understand parametric types and higher-kinded types, my knowledge grows so slow.

What I am trying to achieve is to not lose the concrete type of S (the sequence subtype).

Here is the code:

import scala.collection.generic.CanBuildFrom

class FixedLoad(val id: Int, val positionInT: Int, val amplitude: Double) {
  override def toString: String = s"FixedLoad($id, $positionInT, $amplitude)"
}

object Load {

  implicit def toFixedLoads[S[_] <: Seq[Double]](l: S[Double])(implicit cbf: CanBuildFrom[Nothing, FixedLoad, S[FixedLoad]]): S[FixedLoad] = {
    l.map(_ => new FixedLoad(1, 1, 1)).to[S]
  }

  def main(args: Array[String]): Unit = {
    println(toFixedLoads(List(1.0, 2.0, 3.0)))
  }

}

and the errors:

Error:(16, 13) inferred type arguments [List] do not conform to method toFixedLoads's type parameter bounds [S[_] <: Seq[Double]]
    println(toFixedLoads(List(1.0, 2.0, 3.0)))

Error:(16, 30) type mismatch;
 found   : List[Double]
 required: S[Double]
    println(toFixedLoads(List(1.0, 2.0, 3.0)))
like image 437
vicaba Avatar asked May 21 '19 14:05

vicaba


People also ask

What languages support higher Kinded types?

Haskell has good support for higher kinded types. Every type constructor such as they [] can be used as a "first class type". This is specifically relevant to typeclasses such as the Functor typeclass. Each of those instances are implementations of the Functor class for a particular higher kinded type.

Why are there high Kinded types?

Higher-kinded types are useful when we want to create a container that can hold any type of items; we don't need a different type for each specific content type.

Does rust have higher Kinded types?

Rust does not have higher-kinded-types. For example, functor (and thus monad) cannot be written in Rust.

Does OCaml have higher Kinded types?

OCaml excludes higher-kinded type expressions syntactically by requiring that the type operator be a concrete name: 'a list is a valid type expression, but 'a 'f is not.


1 Answers

Short answer:

Change toFixedLoads[S[_] <: Seq[Double]] to toFixedLoads[S[A] <: Seq[A]]

Long answer:

When you say S[_], that's a higher kinded type. Or, in other words, it's a type constructor. That means it takes a type to produce the final proper type. Here are some examples:

  • List - takes a type, e.g. Int, to produce the proper type List[Int]
  • Option - takes a type, e.g. Int, to produce the proper type Option[Int]

etc.

Type constructors of this kind are often represented as * -> *. You provide one type and you get a type back. There are other kinds as well; for example, Map and Either need two types to produce the proper type (e.g. Map[Int, String] or Either[Error, Foo]), so their kind is * -> * -> *. Think of it as a curried type constructor; takes a type and returns a function that takes a type and then you get the final, proper type. Or in other words, takes two types to produce the final, proper type. You might also have a type constructor that needs a type constructor to build the proper type (e.g. Monad[F[_]]), in which case the kind is (* -> *) -> * (for example, List -> Monad[List]).

So when you say your method is expecting a parameter of type S[Double] and you pass List(1.0, 2.0, 3.0), compiler infers S to be List, and it complains about List[A] not being a subtype of Seq[Double] for any A. Your first attempt at fixing this might be F[_] <: Seq[_], but that can't compile, because the inner types still don't align. We need to "connect" them with something like F[A] <: Seq[A] for some A, which can be written simply as F[A] <: Seq[A].

A good question might be "can I say S <: Seq[Double]?" Sure, S represents a proper type, so you totally could! Something like this works just fine:

def foo[S <: Seq[Double]](s: S) = println(s)
foo(List(1.0, 2.0)) // prints List(1.0, 2.0)

But of course, your S has a "hole" in it, because your method parameter is of type S[Double], so it's not applicable in your case.

like image 137
slouc Avatar answered Sep 26 '22 01:09

slouc