ml86
ml86

Reputation: 1

Why is type parameter bound in higher order function not correctly applied?

I tried to implement a function which takes another function and applies it on the passed list and than on the result from the previous invocation and so on. I came up with two different function signatures but for both the compiler does not derive the type parameters as i expect and i am now wondering whether i am screwing things up or the compiler.

object Repeater {
  // The first parameter is passed in a separate parameter list.
  def repeat[DstType >: ElemType, ElemType](list: List[ElemType])
                                           (function: DstType => DstType,
                                            times: Int): List[DstType] = {
    var currentList: List[DstType] = list
    for (_ <- 0 until times) {
      currentList = currentList.map(function)
    }
    currentList
  }

  // The first parameter is passed in the same parameter list as the others.
  def repeat2[DstType >: ElemType, ElemType](list: List[ElemType],
                                             function: DstType => DstType,
                                             times: Int): List[DstType] = {
    var currentList: List[DstType] = list
    for (_ <- 0 until times) {
      currentList = currentList.map(function)
    }
    currentList
  }
}

class Base()

class Extended() extends Base

object Test extends App {
  val list: List[Extended] = Nil

  Repeater.repeat(list)( x => x, 1)
  Repeater.repeat(list)( (x: Base) => x, 1)

  Repeater.repeat2(list, x => x, 1)
  Repeater.repeat2(list, (x: Base) => x, 1)
}

The first call to repeat() compiles and i do not need to provide the parameter type in the function given to repeat() which is my intended behavior.

The second call to repeat() does not compile:

Error:(25, 39) type mismatch;
 found   : pipeops.Base
 required: pipeops.Extended
  Repeater.repeat(list)( (x: Base) => x, 1)

I do not understand this error because the found type base is a super type of Extended and thus fits into my provide type bound DstType >: ElemTyp.

The first call to repeat2() does not compile:

Error:(27, 26) missing parameter type
  Repeater.repeat2(list, x => x, 1)

I find it really strange that the compiler in this case expects me to specify the functions parameter type.

The second call to repeat2() does compile and is a intended.

I now have two questions:

  1. Can somebody explain to me where those difference come from?

  2. Is there maybe another way of specifying a repeat function which does not require my to specify the parameter type if the passed function goes from ElemType to ElemType but allows me to widen the passed functions type to work on a super type of ElemType if i want to.

Upvotes: 0

Views: 123

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51683

What's wrong with one type parameter?

  object Repeater {
    def repeat[ElemType](list: List[ElemType])
                                             (function: ElemType => ElemType,
                                              times: Int): List[ElemType] = {
      var currentList: List[ElemType] = list
      for (_ <- 0 until times) {
        currentList = currentList.map(function)
      }
      currentList
    }

    def repeat2[ElemType](list: List[ElemType],
                                               function: ElemType => ElemType,
                                               times: Int): List[ElemType] = {
      var currentList: List[ElemType] = list
      for (_ <- 0 until times) {
        currentList = currentList.map(function)
      }
      currentList
    }
  }

  class Base()

  class Extended() extends Base

  object Test extends App {
    val list: List[Extended] = Nil
    val list1: List[Base] = list

    Repeater.repeat(list)( (x: Extended) => x, 1)
    Repeater.repeat(list1)( (x: Base) => x, 1)

    Repeater.repeat2(list, (x: Extended) => x, 1)
    Repeater.repeat2(list, (x: Base) => x, 1)
    Repeater.repeat2(list1, (x: Base) => x, 1)

    Repeater.repeat[Extended](list)( (x: Extended) => x, 1)
    Repeater.repeat[Base](list1)( (x: Base) => x, 1)
    Repeater.repeat2[Extended](list, (x: Extended) => x, 1)
    Repeater.repeat2[Base](list, (x: Base) => x, 1)
  }

If sometimes compiler doesn't make you specify types explicitly this means it can infer them itself, otherwise it can't and you should specify.

Upvotes: 1

Related Questions