Implicit conversion to list

View: New views
3 Messages — Rating Filter:   Alert me  

Implicit conversion to list

by andrew cooke :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

This is the first time I have written an implicit conversion, so I am
not sure if I am making some basic implementation error, or whether
the problem is conceptual.

What I am trying to do is provide a uniform interface to iterating
over some data in a database.  So the natural approach seems to be to
use list comprehensions (the flatMap monad things).  In the code below
you can see how this lets me easily iterate over all the tags
associated with all the tracks associated with a particular artist.

This works just fine while I am dealing with collections.  But some
methods return a single value.  For example, there is only one artist
for a given name.  I would like to use the same list comprehension
approach in this case but, of course, it doesn't work.

So, I thought, why not add an implicit conversion that converts a
single instance to a list?  Then the single return value will be
converted and everything will transparently work.

However, in the code below, this does not work.

class Record {
  implicit def asList[R <: Record](record: R): List[R] = record :: Nil
}

class Artist extends Record
class Track extends Record
class Tag extends Record

class Database {
  def tracksForArtist(artist: Artist) = new Track :: new Track :: Nil
  def tagsForTrack(track: Track) = new Tag :: Nil
  // this is unusual, does not return a collection
  def artistForName(name: String) = new Artist
}

object M extends Application {
  val database = new Database
  // this works just fine
  for (track <- database.tracksForArtist(new Artist);
       tag <- database.tagsForTrack(track))
    yield (println(tag))
  // this fails to compile - no implicit conversion to a list
  for (artist <- database.artistForName("the beatles");
       track <- database.tracksForArtist(new Artist);
       tag <- database.tagsForTrack(track))
    yield (println(tag))
}

Sorry for asking yet another question, but can anyone please explain why?

Thanks,
Andrew

Re: Implicit conversion to list

by Arjan Blokzijl :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Your implicit conversion is not in scope of object M. This compiles and runs:

sealed trait Record

case class Artist extends Record
case class Track extends Record
case class Tag extends Record

class Database {
 def tracksForArtist(artist: Artist) = new Track :: new Track :: Nil
 def tagsForTrack(track: Track) = new Tag :: Nil
 // this is unusual, does not return a collection
 def artistForName(name: String) = new Artist
}

object M extends Application {
 implicit def asList[R <: Record](record: R): List[R] = record :: Nil
 val database = new Database
 // this works just fine
 def showTracks() = {

 for (track <- database.tracksForArtist(new Artist);
      tag <- database.tagsForTrack(track))
   yield (println(tag))
 // this fails to compile - no implicit conversion to a list
 for (artist <- database.artistForName("the beatles");
      track <- database.tracksForArtist(new Artist);
      tag <- database.tagsForTrack(track))
   yield (println(tag))
 }
}


But if you want a single optional value to be returned, you better use Scala's Option class, like so:

class Database {
  def artistForName(name: String) = Some(new Artist)
}

scala> for (track <- db.artistForName("the beatles")) yield println(track)
res0: Option[Unit] = Some(())

That should give you what you want.

Arjan



On Sat, Oct 31, 2009 at 3:09 PM, andrew cooke <andrew@...> wrote:
Hi,

This is the first time I have written an implicit conversion, so I am
not sure if I am making some basic implementation error, or whether
the problem is conceptual.

What I am trying to do is provide a uniform interface to iterating
over some data in a database.  So the natural approach seems to be to
use list comprehensions (the flatMap monad things).  In the code below
you can see how this lets me easily iterate over all the tags
associated with all the tracks associated with a particular artist.

This works just fine while I am dealing with collections.  But some
methods return a single value.  For example, there is only one artist
for a given name.  I would like to use the same list comprehension
approach in this case but, of course, it doesn't work.

So, I thought, why not add an implicit conversion that converts a
single instance to a list?  Then the single return value will be
converted and everything will transparently work.

However, in the code below, this does not work.

class Record {
 implicit def asList[R <: Record](record: R): List[R] = record :: Nil
}

class Artist extends Record
class Track extends Record
class Tag extends Record

class Database {
 def tracksForArtist(artist: Artist) = new Track :: new Track :: Nil
 def tagsForTrack(track: Track) = new Tag :: Nil
 // this is unusual, does not return a collection
 def artistForName(name: String) = new Artist
}

object M extends Application {
 val database = new Database
 // this works just fine
 for (track <- database.tracksForArtist(new Artist);
      tag <- database.tagsForTrack(track))
   yield (println(tag))
 // this fails to compile - no implicit conversion to a list
 for (artist <- database.artistForName("the beatles");
      track <- database.tracksForArtist(new Artist);
      tag <- database.tagsForTrack(track))
   yield (println(tag))
}

Sorry for asking yet another question, but can anyone please explain why?

Thanks,
Andrew


Re: Implicit conversion to list

by andrew cooke :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

ah!  thanks.  i have too little faith in scala - i was thinking it
likely that what i wanted was impossible for some reason.... :o)

2009/10/31 Arjan Blokzijl <arjanblokzijl@...>:

> Your implicit conversion is not in scope of object M. This compiles and
> runs:
>
> sealed trait Record
>
> case class Artist extends Record
> case class Track extends Record
> case class Tag extends Record
>
> class Database {
>  def tracksForArtist(artist: Artist) = new Track :: new Track :: Nil
>  def tagsForTrack(track: Track) = new Tag :: Nil
>  // this is unusual, does not return a collection
>  def artistForName(name: String) = new Artist
> }
>
> object M extends Application {
>  implicit def asList[R <: Record](record: R): List[R] = record :: Nil
>  val database = new Database
>  // this works just fine
>  def showTracks() = {
>
>  for (track <- database.tracksForArtist(new Artist);
>       tag <- database.tagsForTrack(track))
>    yield (println(tag))
>  // this fails to compile - no implicit conversion to a list
>  for (artist <- database.artistForName("the beatles");
>       track <- database.tracksForArtist(new Artist);
>       tag <- database.tagsForTrack(track))
>    yield (println(tag))
>  }
> }
>
>
> But if you want a single optional value to be returned, you better use
> Scala's Option class, like so:
>
> class Database {
>   def artistForName(name: String) = Some(new Artist)
> }
>
> scala> for (track <- db.artistForName("the beatles")) yield println(track)
> res0: Option[Unit] = Some(())
>
> That should give you what you want.
>
> Arjan
>
>
>
> On Sat, Oct 31, 2009 at 3:09 PM, andrew cooke <andrew@...> wrote:
>>
>> Hi,
>>
>> This is the first time I have written an implicit conversion, so I am
>> not sure if I am making some basic implementation error, or whether
>> the problem is conceptual.
>>
>> What I am trying to do is provide a uniform interface to iterating
>> over some data in a database.  So the natural approach seems to be to
>> use list comprehensions (the flatMap monad things).  In the code below
>> you can see how this lets me easily iterate over all the tags
>> associated with all the tracks associated with a particular artist.
>>
>> This works just fine while I am dealing with collections.  But some
>> methods return a single value.  For example, there is only one artist
>> for a given name.  I would like to use the same list comprehension
>> approach in this case but, of course, it doesn't work.
>>
>> So, I thought, why not add an implicit conversion that converts a
>> single instance to a list?  Then the single return value will be
>> converted and everything will transparently work.
>>
>> However, in the code below, this does not work.
>>
>> class Record {
>>  implicit def asList[R <: Record](record: R): List[R] = record :: Nil
>> }
>>
>> class Artist extends Record
>> class Track extends Record
>> class Tag extends Record
>>
>> class Database {
>>  def tracksForArtist(artist: Artist) = new Track :: new Track :: Nil
>>  def tagsForTrack(track: Track) = new Tag :: Nil
>>  // this is unusual, does not return a collection
>>  def artistForName(name: String) = new Artist
>> }
>>
>> object M extends Application {
>>  val database = new Database
>>  // this works just fine
>>  for (track <- database.tracksForArtist(new Artist);
>>       tag <- database.tagsForTrack(track))
>>    yield (println(tag))
>>  // this fails to compile - no implicit conversion to a list
>>  for (artist <- database.artistForName("the beatles");
>>       track <- database.tracksForArtist(new Artist);
>>       tag <- database.tagsForTrack(track))
>>    yield (println(tag))
>> }
>>
>> Sorry for asking yet another question, but can anyone please explain why?
>>
>> Thanks,
>> Andrew
>
>