top | item 17515630

(no title)

speedkills | 7 years ago

I agree with many of your points, but on that specific example code you pointed to which I have never used or looked at before, I find it plenty readable if you know how to read it. That doesn’t mean I find it “obvious” if you have never been trained on how to read those signatures or come from another language that does not support these concepts.

Here is how I read this in case it helps anyone

  Def zip[A, B](a: ⇒ F[A], b: ⇒ F[B]): F[(A, B)]
F[Something] is a “container type” or a type constructor, a type that is parameterized by another type, for example F[Int] could be List[Int] or Future[Int].

The method zip has two parameters, one a by name reference to something of type F[A] and the other a by name reference to something of type F[B] and returns an F of the pair of input types (A, B)

Looks like this deals with three total types, a container type F that must be the same for input param a, input param b, and the result.

I know nothing about F other than the fact that it contains another type. I know nothing about A and nothing about B which means I can’t be looking them up somewhere or using defaults, so the only way I could possibly return an F[(A, B)] is if I combined the two inputs.

Also, code written in this style is meant to follow the substitution principle, so I’ll think through a couple of substitutions to see how this method could be useful to me.

For: F = List, A = Int, B = String

  Def zip[Int, String](a: => List[Int], b: => List[String]): List[(Int, String)]
Since I can’t invent new members of list and the two members of the list may be different lengths, this must return a list whose length is the minimum of a.length and b.length containing and int -> string mapping. Handy.

F = Future, A = User, B = LastLoginDate

  Def zip[User, LastLoginDate](a: => Future[User], b: => Future[LastLoginDate]): Future[(User, LastLoginDate)]
This can only return me a User if they have logged in at least once. The fact that it is two Futures makes me think I am probably querying two remote servers for this information and that I can’t resolve the new Future until both of the input Futures have resolved. By the time this Future is ready I will have a User with their last login date or the Future will fail.

F = Option, A = IpAddress, B = Port

  Def zip[IpAddress, Port](a: => Option[IpAddress], b: => Option[Port]): Option[(IpAddress, Port)]
I may have an IpAddress, I may have a Port, either return to me both the IpAddress and the Port, or neither. This one is really easy to map out as their are only 4 possible code paths

  Some(IpAddress), Some(Port) => Some(IpAddress, Port)
  Some(IpAddress), None => None
  None, Some(Port) => None
  None, None => None
I agree some documentation explaining this would be nice, but there is considerable information I can pull from this signature.

discuss

order

pas|7 years ago

Thanks for the detailed response and concrete examples!

Reading signatures is one thing (and zip is "easy"), but without examples it doesn't really help in most of the cases. Because even if I know the concept, I don't know when it comes handy. (For example I just looked at Forall ( https://static.javadoc.io/org.scalaz/scalaz_2.12/7.2.24/scal... ... Forall is an endofunctor in an endofunctor category ... ah yes!), and then there are tools that come with labels (DList's doc page: nice for append heavy workloads, eg. logging; yes, great, what's Forall good for? What's Strong? Why Alpha exists? What's =>: ? That's not even documented :( It cannot be searched for https://github.com/scalaz/scalaz/search?utf8=%E2%9C%93&q=%3D... ... It's not on the cheatsheet http://eed3si9n.com/scalaz-cheat-sheet .. .I mean it's on it, but it is only used, not defined. etc.)

That's not a type reading thing. That's the bad documentation, and the learner-unfriendly naming convention.