|
|
|
|
|
by eli_gottlieb
5191 days ago
|
|
I'm not talking about importing the generic functions themselves at all. I'm talking about importing the modules that export those generic functions, so that I'll have the modules in my namespace to be able to name the generic functions. Again: dot-notation lets me write: import scala.collection.immutable.List
def addn(n: Int) = (list: List[Int]) => list.map(_ + n)
Instead of: import scala.collection.immutable.List
def addn(n: Int) = (list: List[Int]) => List.map(list,_ + n)
See the difference? And then, remember that map() is not a multimethod. It doesn't actually do dynamic dispatch on its first argument, or on any other argument. It's a type-class method; it does static dispatch on the functor type List[].You cannot define map() as a generic function, at least not in the sense that Common Lisp and Clojure use the term "generic function". It can be conveniently expressed as a type-class method of a class 'Functor f :: * -> * ' or a trait method for a Functor[A]. Not a generic function. |
|
> I'm talking about importing the modules that export those generic functions,
> so that I'll have the modules in my namespace to be able to name
> the generic functions.
I don't see how this is either here or there. I can import modules and then qualify functions within the modules, or I can import functions directly from the modules, renaming then if I want to. There's no difference, between these options, other than a minor difference in what name I use to refer to the function once its been imported.
> Again: dot-notation lets me write:
> import scala.collection.immutable.List
> def addn(n: Int) = (list: List[Int]) => list.map(_ + n)
> Instead of:
> import scala.collection.immutable.List
> def addn(n: Int) = (list: List[Int]) => List.map(list,_ + n)
It isn't dot notation that lets you do that; it's the semantics of how functions are located that lets you do that. Many languages support statically overloaded functions using functional notation. It's also quite easy to imagine a language which uses the syntax f(a) to mean what a.f() means in Scala.
Also, why is this to be considered so desirable? For the performance gain that comes from eliminating dynamic dispatch? In that case, it is certainly possible for a language that supports multiple dispatch to dispatch statically when it can infer that it is safe to do.
> See the difference?
Yes, I quite understand the concept, but it has nothing intrinsically to do with dot notation.
> And then, remember that map() is not a multimethod.
> It doesn't actually do dynamic dispatch on its first argument,
> or on any other argument. It's a type-class method; it does static dispatch
> on the functor type List[].
In Scala? I don't believe that this is correct. As far as I understand, there's a single implementation of map for the entire collection library in TraversableLike, which uses virtual functions provided by the concrete class (and a builder object which gets passed in implicitly) to construct the new container, loop over the old container, and add elements to the new container.
This is similar to how map() works in Python and Clojure, where there is a single implementation of map() which uses dynamic dispatch to loop over the elements of the container. The downside to the way that Python and Clojure doing things is that you get iterators or streams back, not the original container type. But they could return the original container type if containers in Python and Clojure were to have virtual constructors. Then map() could call these virtual constructors to return a container of the same type.
> You cannot define map() as a generic function, at least not in the sense that
> Common Lisp and Clojure use the term "generic function".
Clearly in a statically typed language, you also need some form of parametric polymorphism in addition to what Common Lisp and Clojure provide in order to preserve types. I see no reason why this should be insurmountable.
I've often done things in C++ that wrapped dynamic lookup in a generic function or class, to achieve the types of things that you say require type classes. Another thing you can easily do in C++ is to write template adapter classes to provide something similar to Scala's structural types.
Also, type classes surely don't require dot notation. Haskell is from where type classes originate, and Haskell doesn't have dot notation.