Hacker News new | ask | show | jobs
by bad_user 5318 days ago
Consider this method signature:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

All this complexity to allow for automatic conversions (the following will return a Set[String]) ...

BitSet(1, 2, 3).map { _.toString + "!" }

In a dynamic language, the signature of map is simply this: (a → b) → [a] → [b]

The big difference is that the language doesn't care about types "a" and "b" until runtime. You're the one that cares about it, only on a need-to-know basis. Hence the implicit conversions become explicit (although conversions are less necessary) and code correctness is guarded by unit tests, which you need with Scala anyway. Also, the implementation of "map" in a dynamic language is something that a junior developer can come up with.

So the biggest problems I have with Scala:

    1) it is not readable
    2) it separates developers in two camps:
       library designers and users
    3) it does not provide clear benefits 
       over dynamic languages (Haskell does)
1 comments

Not only dynamically typed languages, the signature would also look remarkably close to (a → b) → [a] → [b] in ML and Haskell. Your ML and Haskell compiler would also make check that it was called correctly at compile time without extra unit tests.
The difference is that Scala also abstracts over the "container" not just the contents.

If Scala only did what ML and Haskell do, the type signatures would be exactly that.

> The difference is that Scala also abstracts over the "container" not just the contents.

Haskell is perfectly able to do that as well if desired. `[]` is a monadic type.

Haskell has no subtyping as far as I know Haskell.
Haskell has typeclasses which would probably correspond to structural typing in Scala. This type signature:

Functor f => (a -> b) -> f a -> f b

Defines map for any type that implements the type class functor. Lots of types will implement functor and it is easy to implement it yourself.

> Haskell has typeclasses which would probably correspond to structural typing in Scala.

Nah, typeclasses are nominative typing but added post-facto (you can define a typeclass instance for a third party's type). Typeclasses are similar to Scala's traits I think (I don't know if you can add traits to a library's types though).

It does, but it does not support downcasting (it does support upcasting)

For instance, the Ord typeclass extends the Eq typeclass, so any instance of Ord can be used as an instance of Eq. But there is no way to cast an instance of Eq to an instance of Ord.

Are you hinting at Haskell's fmap? The type signature is significantly simpler:

fmap :: Functor f => (a -> b) -> f a -> f b

That doesn't capture the type of the original example.

    BitSet(1, 2, 3).map { _.toString + "!" } // returns Set[String]
The Scala code here starts with one Functor (BitSet) and returns a different Functor (Set), depending on the type of the function (in this case, Int => String). If the function had type (Int => Int), it would still return a BitSet.

The signature for fmap does not encode this flexibility.

Thanks for explaining. I now understand what the OP meant by "automatic conversions".
Can you explain the scala type signature for map? I see no container declared and it is less than obvious to me how it actually represents map across a container. I guess it is the implicit?
I'm a Scala newbie, and it did not take me long to actually understand the type signature here. Of course, when I first saw it I was confused, but it's not magical.

There are probably map-methods elsewhere, but let's take the map defined on TraversableLike[0]. TraversableLike is a base trait for all kinds of Scala collections (let's just pretend trait is like an interface for now, look it up if you're interested in the details).

TraversableLike has two definitions of map in 2.9.1:

  def map [B] (f: (A) ⇒ B): Traversable[B]

  def map [B, That] (f: (A) ⇒ B)(implicit bf: CanBuildFrom[Repr, B, That]): That
The first one isn't so bad, is it? The first one takes a function converting objects of A to objects of B, and returns something that is traversable of B (i.e. perhaps a List[B] or Set[B]).

The second one can return anything you want, as long as you have a function that describes how the objects can be converted. That is what the implicit is for. For instance, if you map a collection over a function that returns a list of integers, but you actually wanted a set of strings - you can define a function that converts your List[Int] to Set[String]. I recently found a description of canBuildFrom on Stack Overflow that explains this in a bit more detail[1].

As a Scala beginner I find that I often can't expect to understand everything at once - a lot of times it's like other languages, but when it's not I usually can't just guess what it does, but actually have to take time to learn it. I don't think this is a bad thing.

[0]: http://www.scala-lang.org/api/current/index.html#scala.colle...

[1]: http://stackoverflow.com/questions/1721356/scala-2-8-canbuil...

     it did not take me long to actually understand 
     the type signature here
Bullshit. You just followed somebody else's public explanation. Not a problem per se, but this doesn't strike me as beginner friendly ... Dr. Brian Beckman also has a great description of monads on Channel9 that doesn't involve category theory. Does that mean that monad comprehensions are also beginner friendly?

     if you map a collection over a function 
     that returns a list of integers, but you 
     actually wanted a set of strings
Here's some Ruby code for you:

     [1,2,3].map{|x| x * 2}.map{|x| x.to_s}.to_set
Ironically I find this code written in a dynamic language to be much more readable. Also, give me first-class functions and recursion in a dynamic language and I can implement all of the above by my own (lists, sets and map).
> Bullshit. You just followed somebody else's public explanation.

If you're referring to the stack overflow answer, then no. I understood the signature by learning Scala (by reading a book). Is that cheating?

> Does that mean that monad comprehensions are also beginner friendly?

No, not necessarily. Do you have to understand what that even means to be able to use Scala?

How about acting less aggressive? It certainly dosen't help you to get your point across.

> Here's some Ruby code for you

You just tried very hard to not understand the problem at hand, right?

Because the equivalent Scala code looks like this:

    Array(1,2,3).map(_*2).map(_.toString).toSet
> I can implement all of the above by my own

Sure, if you OK with your code running magnitudes slower than the built-in stuff.

That's probably the main difference between general purpose languages and scripting languages today: General-purpose languages allow you to implement the appropriate data structures yourself, while scripting languages force you to use the built-in stuff.