This page is no longer maintained — Please continue to the home page at www.scala-lang.org

A bit of help with implicits please ...

5 replies
marius
Joined: 2008-08-31,
User offline. Last seen 3 years 19 weeks ago.
Hi,

Here is a program listing:

class Container[T](list: List[T]) {
  def get[U](pos: Int)(implicit c: Convert[T, U]): U = {
    c.cvt(list(pos))
  }
}

trait Convert[T, U] {
  def cvt(t: T): U
}

object Convertors {
    implicit object stringToInt extends Convert[String, Int] {
      def cvt(str: String): Int = java.lang.Integer.parseInt(str)
    }
}


object Main {
  def main(args : Array[String]) : Unit = {
    import Convertors._
    val c = new Container[String]("1" :: "2" :: Nil)
   
    var i: Int = c.get(0) // HERE IT FAILS TO COMPILE
    println(i)
  }
}

So basically we want to convert elements from a List of String (for example) to some other type using of course implicits. The above code fails to compile saying "no implicit argument matching parameter type com.Convert[String,Nothing] was found."

Looks like the type parameter U was inferred with Nothing instead of Int since i variable is of type Int. If I'm adding another argument to get function of type U it works ok. As far as I can see the Int type which would be inferred is not captured anywhere even if the return type is U.

Could you please shed some light?

Br's,
Marius
Erik Engbrecht
Joined: 2008-12-19,
User offline. Last seen 3 years 18 weeks ago.
Re: A bit of help with implicits please ...
1.  Drop the implicit parameter from Container.get2.  Make Convertors.stringToInt an implicit def instead of an implicit object
That should accomplish what you want and be simpler to boot.
On Fri, Jan 2, 2009 at 1:45 PM, Marius Danciu <marius [dot] danciu [at] gmail [dot] com> wrote:
Hi,

Here is a program listing:

class Container[T](list: List[T]) {
  def get[U](pos: Int)(implicit c: Convert[T, U]): U = {
    c.cvt(list(pos))
  }
}

trait Convert[T, U] {
  def cvt(t: T): U
}

object Convertors {
    implicit object stringToInt extends Convert[String, Int] {
      def cvt(str: String): Int = java.lang.Integer.parseInt(str)
    }
}


object Main {
  def main(args : Array[String]) : Unit = {
    import Convertors._
    val c = new Container[String]("1" :: "2" :: Nil)
   
    var i: Int = c.get(0) // HERE IT FAILS TO COMPILE
    println(i)
  }
}

So basically we want to convert elements from a List of String (for example) to some other type using of course implicits. The above code fails to compile saying "no implicit argument matching parameter type com.Convert[String,Nothing] was found."

Looks like the type parameter U was inferred with Nothing instead of Int since i variable is of type Int. If I'm adding another argument to get function of type U it works ok. As far as I can see the Int type which would be inferred is not captured anywhere even if the return type is U.

Could you please shed some light?

Br's,
Marius



--
http://erikengbrecht.blogspot.com/
Jim McBeath
Joined: 2009-01-02,
User offline. Last seen 42 years 45 weeks ago.
Re: A bit of help with implicits please ...

> val c = new Container[String]("1" :: "2" :: Nil)

I believe Scala is not inferring the Int part when instantiating
your Container object because the get(0) is a separate statement from
the creation of the Container object, so it is not using that
information when calculating the type of var c.

If there is some reason you don't want to use Erik's suggestion, your
current approach should work if you add the second type parameter
to your Container class rather than on its get method and specify
that type explicitly when instantiating it:

class Container[T,U](list: List[T]) {
def get(pos: Int)(implicit c: Convert[T, U]): U = {
c.cvt(list(pos))
}
}

and

val c = new Container[String,Int]("1" :: "2" :: Nil)

--
Jim

On Fri, Jan 02, 2009 at 01:59:20PM -0500, Erik Engbrecht wrote:
> Date: Fri, 2 Jan 2009 13:59:20 -0500
> From: Erik Engbrecht
> To: Marius Danciu
> Cc: Scala list
> Subject: Re: [scala] A bit of help with implicits please ...
>
> 1. Drop the implicit parameter from Container.get
>
> 2. Make Convertors.stringToInt an implicit def instead of an implicit
> object
>
> That should accomplish what you want and be simpler to boot.
>
> On Fri, Jan 2, 2009 at 1:45 PM, Marius Danciu
> wrote:
>
> Hi,
> Here is a program listing:
> class Container[T](list: List[T]) {
> def get[U](pos: Int)(implicit c: Convert[T, U]): U = {
> c.cvt(list(pos))
> }
> }
> trait Convert[T, U] {
> def cvt(t: T): U
> }
> object Convertors {
> implicit object stringToInt extends Convert[String, Int] {
> def cvt(str: String): Int = java.lang.Integer.parseInt(str)
> }
> }
> object Main {
> def main(args : Array[String]) : Unit = {
> import Convertors._
> val c = new Container[String]("1" :: "2" :: Nil)
>
> var i: Int = c.get(0) // HERE IT FAILS TO COMPILE
> println(i)
> }
> }
> So basically we want to convert elements from a List of String (for
> example) to some other type using of course implicits. The above
> code fails to compile saying "no implicit argument matching
> parameter type com.Convert[String,Nothing] was found."
> Looks like the type parameter U was inferred with Nothing instead of
> Int since i variable is of type Int. If I'm adding another argument
> to get function of type U it works ok. As far as I can see the Int
> type which would be inferred is not captured anywhere even if the
> return type is U.
> Could you please shed some light?
> Br's,
> Marius
>
> --
> http://erikengbrecht.blogspot.com/

marius
Joined: 2008-08-31,
User offline. Last seen 3 years 19 weeks ago.
Re: A bit of help with implicits please ...


On Fri, Jan 2, 2009 at 11:20 PM, Jim McBeath <scala [at] j [dot] jimmc [dot] org> wrote:
>          val c = new Container[String]("1" :: "2" :: Nil)

I believe Scala is not inferring the Int part when instantiating
your Container object because the get(0) is a separate statement from
the creation of the Container object, so it is not using that
information when calculating the type of var c.

Yes I kind of figured that out but why it is inferring Nothing and not Int?
 


If there is some reason you don't want to use Erik's suggestion, your
current approach should work if you add the second type parameter
to your Container class rather than on its get method and specify
that type explicitly when instantiating it:

  class Container[T,U](list: List[T]) {
    def get(pos: Int)(implicit c: Convert[T, U]): U = {
      c.cvt(list(pos))
    }
  }

Yes but if I do that I'm loosing the whole point. I wanted the resulting type to be inferred by compiler and with the help of implicits to have the correct conversion.

It is not that I don't want to use that suggestion (btw thank you Erik!), I'm quite aware of it but still I'd like to know what is really wrong with the original code.

Of course this would work:

class Container[T](list: List[T]) {
 
  def get[U](pos: Int)(implicit c: T => U): U = {
    c(list(pos))
  }
}

object Main {

  implicit def stringToInt(str: String) : Int = java.lang.Integer.parseInt(str)

  def main(args : Array[String]) : Unit = {

    val c = new Container[String]("1" :: "2" :: Nil)
   
    var i: Int = c.get(0)
    println(i)
  }
}



FWIW that this is not a "real life" case but rather an example so I would like to understand why Nothing is inferred instead on Int ... and my assumption right now is that because there is no parameter of type U for get(...) function ... as if I add one it works fine but the function signature is loosing its sense.



and

        val c = new Container[String,Int]("1" :: "2" :: Nil)

--
Jim

On Fri, Jan 02, 2009 at 01:59:20PM -0500, Erik Engbrecht wrote:
> Date: Fri, 2 Jan 2009 13:59:20 -0500
> From: Erik Engbrecht <erik [dot] engbrecht [at] gmail [dot] com>
> To: Marius Danciu <marius [dot] danciu [at] gmail [dot] com>
> Cc: Scala list <scala [at] listes [dot] epfl [dot] ch>
> Subject: Re: [scala] A bit of help with implicits please ...
>
>    1.  Drop the implicit parameter from Container.get
>
>    2.  Make Convertors.stringToInt an implicit def instead of an implicit
>    object
>
>    That should accomplish what you want and be simpler to boot.
>
>    On Fri, Jan 2, 2009 at 1:45 PM, Marius Danciu <marius [dot] danciu [at] gmail [dot] com>
>    wrote:
>
>      Hi,
>      Here is a program listing:
>      class Container[T](list: List[T]) {
>        def get[U](pos: Int)(implicit c: Convert[T, U]): U = {
>          c.cvt(list(pos))
>        }
>      }
>      trait Convert[T, U] {
>        def cvt(t: T): U
>      }
>      object Convertors {
>          implicit object stringToInt extends Convert[String, Int] {
>            def cvt(str: String): Int = java.lang.Integer.parseInt(str)
>          }
>      }
>      object Main {
>        def main(args : Array[String]) : Unit = {
>          import Convertors._
>          val c = new Container[String]("1" :: "2" :: Nil)
>
>          var i: Int = c.get(0) // HERE IT FAILS TO COMPILE
>          println(i)
>        }
>      }
>      So basically we want to convert elements from a List of String (for
>      example) to some other type using of course implicits. The above
>      code fails to compile saying "no implicit argument matching
>      parameter type com.Convert[String,Nothing] was found."
>      Looks like the type parameter U was inferred with Nothing instead of
>      Int since i variable is of type Int. If I'm adding another argument
>      to get function of type U it works ok. As far as I can see the Int
>      type which would be inferred is not captured anywhere even if the
>      return type is U.
>      Could you please shed some light?
>      Br's,
>      Marius
>
>    --
>    http://erikengbrecht.blogspot.com/

Jim McBeath
Joined: 2009-01-02,
User offline. Last seen 42 years 45 weeks ago.
Re: A bit of help with implicits please ...

On Sat, Jan 03, 2009 at 12:54:36AM +0200, Marius Danciu wrote:
> On Fri, Jan 2, 2009 at 11:20 PM, Jim McBeath wrote:
>
> > val c = new Container[String]("1" :: "2" :: Nil)
>
> I believe Scala is not inferring the Int part when instantiating
> your Container object because the get(0) is a separate statement
> from
> the creation of the Container object, so it is not using that
> information when calculating the type of var c.
>
> Yes I kind of figured that out but why it is inferring Nothing and not
> Int?

Ah, I see what you are saying: It works if you use this line:

val i = c.get[Int](0)

so why can't it infer Int instead of Nothing on that line when
no explicit type is given? I don't know the answer to that.

By the way, Scala will infer the "String" type for T in your example,
so you can skip the [String] and write

val c = new Container("1" :: "2" :: Nil)

> If there is some reason you don't want to use Erik's suggestion,
> your
> current approach should work if you add the second type parameter
> to your Container class rather than on its get method and specify
> that type explicitly when instantiating it:
> class Container[T,U](list: List[T]) {
> def get(pos: Int)(implicit c: Convert[T, U]): U = {
> c.cvt(list(pos))
> }
> }
>
> Yes but if I do that I'm loosing the whole point. I wanted the
> resulting type to be inferred by compiler and with the help of
> implicits to have the correct conversion.

Scala does not do Hindley-Milner global type inference as does
Haskell. Sometimes it needs a bit of assistance to figure out
things that are obvious to us humans.

--
Jim

marius
Joined: 2008-08-31,
User offline. Last seen 3 years 19 weeks ago.
Re: A bit of help with implicits please ...


On Sat, Jan 3, 2009 at 9:06 PM, Jim McBeath <scala [at] j [dot] jimmc [dot] org> wrote:
On Sat, Jan 03, 2009 at 12:54:36AM +0200, Marius Danciu wrote:
>    On Fri, Jan 2, 2009 at 11:20 PM, Jim McBeath <scala [at] j [dot] jimmc [dot] org> wrote:
>
>    >          val c = new Container[String]("1" :: "2" :: Nil)
>
>      I believe Scala is not inferring the Int part when instantiating
>      your Container object because the get(0) is a separate statement
>      from
>      the creation of the Container object, so it is not using that
>      information when calculating the type of var c.
>
>    Yes I kind of figured that out but why it is inferring Nothing and not
>    Int?

Ah, I see what you are saying:  It works if you use this line:

   val i = c.get[Int](0)

Right .. although I expected the same thing with var i: Int = c.get(0) since compiler knows the type of i.
 


so why can't it infer Int instead of Nothing on that line when
no explicit type is given?  I don't know the answer to that.

By the way, Scala will infer the "String" type for T in your example,
so you can skip the [String] and write

   val c = new Container("1" :: "2" :: Nil)

Yup I know that.
 


>      If there is some reason you don't want to use Erik's suggestion,
>      your
>      current approach should work if you add the second type parameter
>      to your Container class rather than on its get method and specify
>      that type explicitly when instantiating it:
>        class Container[T,U](list: List[T]) {
>          def get(pos: Int)(implicit c: Convert[T, U]): U = {
>            c.cvt(list(pos))
>          }
>        }
>
>    Yes but if I do that I'm loosing the whole point. I wanted the
>    resulting type to be inferred by compiler and with the help of
>    implicits to have the correct conversion.

Scala does not do Hindley-Milner global type inference as does
Haskell.  Sometimes it needs a bit of assistance to figure out
things that are obvious to us humans.

Right ... Many thanks Jim !
 


--
Jim

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland