- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Idiom idiocy
Tue, 2009-02-17, 02:58
Hi,
So a few days ago I defined this:
object Lang { def nullable[T] (value:T) :Option[T] = if (value == null) None else Some(value) implicit def optionToConvertable[T](option:Option[T]) = new ConvertableOption(option) class ConvertableOption[T](val option:Option[T]) { def convert[S] (convert: (T) => S) :Option[S] = option match { case None => None case Some(value) => Some(convert(value)) } def convertOption[S] (convert: (T) => Option[S]) :Option[S] = option match { case None => None case Some(value) => convert(value) } } }
My original intention was just to simplify dealing with java libraries - i.e. make it easy to turn things that return null into Options, and to convert a java class into something more scalaesque in a quick and easy step e.g.:
def fromCache (key:String) :Option[String] = nullable(cache.get(cacheKey(key))) convert { _.getValue.asInstanceOf[String] }
However since writing it I find myself using it a lot to create chains of operations on the contents of an option:
def value (key:String) :Option[S3Value] = s3Object (key) convert (o => new S3Value(o)) def apply (key:String) :Option[String] = value (key) convert (v => v.toString)
I don't hail from a strongly FP background. Is there an obvious way that people normally do this that I'm missing?
--
-Robin
Robin Barooah
http://www.sublime.org
So a few days ago I defined this:
object Lang { def nullable[T] (value:T) :Option[T] = if (value == null) None else Some(value) implicit def optionToConvertable[T](option:Option[T]) = new ConvertableOption(option) class ConvertableOption[T](val option:Option[T]) { def convert[S] (convert: (T) => S) :Option[S] = option match { case None => None case Some(value) => Some(convert(value)) } def convertOption[S] (convert: (T) => Option[S]) :Option[S] = option match { case None => None case Some(value) => convert(value) } } }
My original intention was just to simplify dealing with java libraries - i.e. make it easy to turn things that return null into Options, and to convert a java class into something more scalaesque in a quick and easy step e.g.:
def fromCache (key:String) :Option[String] = nullable(cache.get(cacheKey(key))) convert { _.getValue.asInstanceOf[String] }
However since writing it I find myself using it a lot to create chains of operations on the contents of an option:
def value (key:String) :Option[S3Value] = s3Object (key) convert (o => new S3Value(o)) def apply (key:String) :Option[String] = value (key) convert (v => v.toString)
I don't hail from a strongly FP background. Is there an obvious way that people normally do this that I'm missing?
--
-Robin
Robin Barooah
http://www.sublime.org
Tue, 2009-02-17, 03:27
#2
Re: Idiom idiocy
Oops, that should be
val value = new S3Value(obj)
In which case you're not really chaining lots of maps and flatMaps, but you get the idea.
--j
On Mon, Feb 16, 2009 at 6:09 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
val value = new S3Value(obj)
In which case you're not really chaining lots of maps and flatMaps, but you get the idea.
--j
On Mon, Feb 16, 2009 at 6:09 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
It looks to me like convert and convertOption are just map and flatMap, available in the standard library.
You can chain lots of calls to map and flatMap together with for-comprehensions.
for {
obj <- s3Object(key)
value <- new S3Value(obj)
} yield value.toString // returns Option[String]
--j
On Mon, Feb 16, 2009 at 5:58 PM, Robin <robin@sublime.org> wrote:
Hi,
So a few days ago I defined this:
object Lang { def nullable[T] (value:T) :Option[T] = if (value == null) None else Some(value) implicit def optionToConvertable[T](option:Option[T]) = new ConvertableOption(option) class ConvertableOption[T](val option:Option[T]) { def convert[S] (convert: (T) => S) :Option[S] = option match { case None => None case Some(value) => Some(convert(value)) } def convertOption[S] (convert: (T) => Option[S]) :Option[S] = option match { case None => None case Some(value) => convert(value) } } }
My original intention was just to simplify dealing with java libraries - i.e. make it easy to turn things that return null into Options, and to convert a java class into something more scalaesque in a quick and easy step e.g.:
def fromCache (key:String) :Option[String] = nullable(cache.get(cacheKey(key))) convert { _.getValue.asInstanceOf[String] }
However since writing it I find myself using it a lot to create chains of operations on the contents of an option:
def value (key:String) :Option[S3Value] = s3Object (key) convert (o => new S3Value(o)) def apply (key:String) :Option[String] = value (key) convert (v => v.toString)
I don't hail from a strongly FP background. Is there an obvious way that people normally do this that I'm missing?
--
-Robin
Robin Barooah
http://www.sublime.org
Tue, 2009-02-17, 03:37
#3
Re: Idiom idiocy
Ok, that was the worst explanation ever. Let me try again. Let's say I'm working with Scala Maps, which have a method with signature: get(key: K): Option[V]
val result = for {
value1 <- map1.get(key1)
value2 <- map2.get(key2)
value3 <- map3.get(key3)
} yield operation(value1, value2, value3)
If operation has method signature: operation(key1: Key1, key2: Key2, key3: Key3): Result
Then val 'result' will be of type Option[Result].
If any of the calls to 'get' returns None, then 'result' will be None as well. If all of the calls to 'get' succeed, then 'result' will be Some(x), where x is whatever got returned by 'operation'.
The code above basically gets translated to something like:
val result = map1.get(key1).flatMap(value1 =>
map2.get(key2).flatMap(value2 =>
map3.get(key3).map(value3 =>
operation(value1, value2, value3))))
Now, If I want the computation to continue regardless of whether map2 contains key2 or not, then I have to provide a default value for value2:
val result = for {
value1 <- map1.get(key1)
val value2 = map2.getOrElse(key2, default)
value3 <- map3.get(key3)
} yield operation(value1, value2, value3)
This uses the getOrElse method defined on Maps, but Option also has its own getOrElse method.
--j
On Mon, Feb 16, 2009 at 6:10 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
val result = for {
value1 <- map1.get(key1)
value2 <- map2.get(key2)
value3 <- map3.get(key3)
} yield operation(value1, value2, value3)
If operation has method signature: operation(key1: Key1, key2: Key2, key3: Key3): Result
Then val 'result' will be of type Option[Result].
If any of the calls to 'get' returns None, then 'result' will be None as well. If all of the calls to 'get' succeed, then 'result' will be Some(x), where x is whatever got returned by 'operation'.
The code above basically gets translated to something like:
val result = map1.get(key1).flatMap(value1 =>
map2.get(key2).flatMap(value2 =>
map3.get(key3).map(value3 =>
operation(value1, value2, value3))))
Now, If I want the computation to continue regardless of whether map2 contains key2 or not, then I have to provide a default value for value2:
val result = for {
value1 <- map1.get(key1)
val value2 = map2.getOrElse(key2, default)
value3 <- map3.get(key3)
} yield operation(value1, value2, value3)
This uses the getOrElse method defined on Maps, but Option also has its own getOrElse method.
--j
On Mon, Feb 16, 2009 at 6:10 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
Oops, that should be
val value = new S3Value(obj)
In which case you're not really chaining lots of maps and flatMaps, but you get the idea.
--j
On Mon, Feb 16, 2009 at 6:09 PM, Jorge Ortiz <jorge.ortiz@gmail.com> wrote:
It looks to me like convert and convertOption are just map and flatMap, available in the standard library.
You can chain lots of calls to map and flatMap together with for-comprehensions.
for {
obj <- s3Object(key)
value <- new S3Value(obj)
} yield value.toString // returns Option[String]
--j
On Mon, Feb 16, 2009 at 5:58 PM, Robin <robin@sublime.org> wrote:
Hi,
So a few days ago I defined this:
object Lang { def nullable[T] (value:T) :Option[T] = if (value == null) None else Some(value) implicit def optionToConvertable[T](option:Option[T]) = new ConvertableOption(option) class ConvertableOption[T](val option:Option[T]) { def convert[S] (convert: (T) => S) :Option[S] = option match { case None => None case Some(value) => Some(convert(value)) } def convertOption[S] (convert: (T) => Option[S]) :Option[S] = option match { case None => None case Some(value) => convert(value) } } }
My original intention was just to simplify dealing with java libraries - i.e. make it easy to turn things that return null into Options, and to convert a java class into something more scalaesque in a quick and easy step e.g.:
def fromCache (key:String) :Option[String] = nullable(cache.get(cacheKey(key))) convert { _.getValue.asInstanceOf[String] }
However since writing it I find myself using it a lot to create chains of operations on the contents of an option:
def value (key:String) :Option[S3Value] = s3Object (key) convert (o => new S3Value(o)) def apply (key:String) :Option[String] = value (key) convert (v => v.toString)
I don't hail from a strongly FP background. Is there an obvious way that people normally do this that I'm missing?
--
-Robin
Robin Barooah
http://www.sublime.org
You can chain lots of calls to map and flatMap together with for-comprehensions.
for {
obj <- s3Object(key)
value <- new S3Value(obj)
} yield value.toString // returns Option[String]
--j
On Mon, Feb 16, 2009 at 5:58 PM, Robin <robin@sublime.org> wrote: