Using a polymorphic function to extract an object from options

The formless documentation explains how to use polymorphic functions to create a function that maps objects in one container view to another, but what about when you want to unpack things from your container?

I have hlist options

val options = Some(1) :: Some("A") :: Some(3.5) :: HNil 

I want the polymorphic function to be able to extract the contents of each of the parameters.

 // This is incorrect: object uuu extends (Option ~> Any) { def apply[T](l:Option[T]):T = { l.get } } 

If this function was correct, I would like the following behavior:

 options.map(uuu) // I want: 1 :: "A" :: 3.5 :: HNil 

How can I fix this so that my polymorphic function really works?

+3
scala shapeless
source share
1 answer

There are a couple of issues here. Firstly, the static type of your hlist has Some in it instead of Option , so Mapper proof that you need to prove that uuu can be displayed on top of options will not be found. The best way to fix this is to define the Some smart constructor, which returns Option :

 def some[A](a: A): Option[A] = Some(a) val options = some(1) :: some("A") :: some(3.5) :: HNil 

You can also add type annotations to the original options or change uuu to work with Some instead of Option (which would be safer, but apparently less useful for what you intend to do).

Now your code compiles and does something, but only because of some bizarre fact that Any is kind-polymorphic in Scala. In the general case, when you have F ~> G , both F and G should be type constructors that take one type parameter - for example. Option ~> List . Any does not accept a type parameter, and yet it works, due to this strange fact about the Scala language (that Any along with Nothing is kind-polymorphic and will correspond to any slot where you need a type, a type constructor with one parameter, a type constructor with a dozen parameters, etc.).

Therefore, it compiles, but it is pretty useless, since it returns Any :: Any :: Any :: HNil . You can fix this by replacing Any with a natural conversion with shapeless.Id :

 import shapeless._, shapeless.poly.~> def some[A](a: A): Option[A] = Some(a) val options = some(1) :: some("A") :: some(3.5) :: HNil object uuu extends (Option ~> Id) { def apply[T](l: Option[T]): T = l.get } options.map(uuu) 

Id defined as type Id[+T] = T -ie, this is an identity type constructor that provides you with an expanded type.

This version compiles and returns a useful result, but it is still not very safe, because if you match the hlist with None elements (at run time), you will get a NoSuchElementException . In fact, this has nothing to do with changing Option ~> Id to Some ~> Id , which somehow provides default values, etc., All of which significantly change the nature of the operation.

+2
source share

All Articles