What LSP essentially states is that because a BitSet is-a Set, then it should be transparently usable everywhere that a Set is valid.
One possible operation on a Set[T] is to convert all members to strings, yielding a Set[String]. A naive implementation of BitSet would only support a map(fn: Int => Int) operation, thus breaking the LSP. The pre-2.8 version of the Scala collection library achieved this by overloading the method and calling the Set or BitSet version as appropriate, the problem with this approach is that it led to a LOT of code duplication and rapidly became a testing/maintenance nightmare. The current design allows for behaviour to be defined in a very generic form at a high level in the collections hierarchy, which means that all collections can benefit from the rich set of operations available without having to extensively copy+paste code. There's an alternative approach in which you state that a BitSet is implicitly convertible to a Set, and therefore there's no subclassing relationship between the two. It has different trade-offs and can lead to cleaner designs, but unfortunately can't be made to work in Scala; an implicit conversion will only kick in where a method doesn't exist at all, not just because it doesn't exist with a given signature. Such logic *could* be implemented, but would be sub-optimal on the JVM and would carry a certain performance cost. It also doesn't play along very nicely in the presence of mutable objects. On 24 November 2011 10:39, Paul King <[email protected]> wrote: > Very nice examples but perhaps better to lose the reference to "LSP" > or make it clearer? > If the next line after setting res2 is: assert(res2.isInstanceOf[BitSet]) > then I don't quite get substitutable behavior if I am expecting a BitSet. > LSP within certain bounds for sure but not blanket LSP. > > Also, I am not sure what you mean by "100% safe". Perhaps you mean > statically type-checked according to the scala type system (which is what > you kind of say later in the same sentence - which I think sums it up > nicely > and adequately). > > Not trying to nit-pick. I just find type-safety to be a small subset of the > potentially desirable properties of programs. I would hate someone to > think that any program I wrote was "100% safe" just because it compiled > or met some type systems constraints. > > Cheers, Paul. > > On Thu, Nov 24, 2011 at 7:28 PM, Kevin Wright <[email protected]> > wrote: > > I guess I missed the point in my (rather terse) example. > > What I was demonstrating is that the expression doesn't just change the > > contents of the collection, but also the type of the collection itself. > For > > example: > > val bitSet = BitSet(1,2,3) //a BitSet can only hold integers, it > subclasses > > Set[Int] > > val res1 = bitSet map {2*} //BitSet(2,4,6) > > val res2 = bitSet map {_.toString} //Set("1","2","3") BitSets can't hold > > strings, so returns a Set[String], as per the Liskov Substitution > > Principle. > > val myMap = Map(1 -> "x", 2 -> "y", 3 -> "z") //a Map[K,V] is also a > > collection of K->V pairs > > val res3 = myMap map {x => x._1.toString -> > > x._2} //Map("1"->"x","2"->"y","3"->"z"), a Map[String, String] > > val res4 = myMap map {case (a,b) => a.toString -> b} //more readable via > > pattern matching > > val res5 = myMap map {_._1} //List(1,2,3), a List[Int] > > val res6 = myMap map {_._2} //List("x","y","z"), a List[String] > > This is all 100% safe and statically typed, and is made possible by the > > "scary" CanBuildFrom implicit parameter that Stephen mention in his > article. > > I don't believe that Fantom can do this, not least because it doesn't > have > > highly-optimised collection types such as BitSet, it doesn't consider > Maps > > to subclass collections of pairs, and it doesn't support creation of your > > own paramaterised collection types. So far as I'm aware, Scala is the > only > > language in existence that directly supports such a scheme (though I > imagine > > it would be totally possible to duplicate using something like Lisp > macros) > > It looks threatening in the documentation, but when you actually use this > > stuff it's really quite effortless. The best part is that these > collections > > are defined entirely in library code using completely standard syntax > > without a single line of compiler support required. Anybody could have > > implemented them without touching the compiler, and it's completely > possible > > to implement your own collection types that fit transparently into this > > hierarchy (although you *do* have to understand the full method > signatures > > at that point) > > 2011/11/24 Cédric Beust ♔ <[email protected]> > >> > >> On Wed, Nov 23, 2011 at 3:05 PM, Kevin Wright <[email protected] > > > >> wrote: > >>> > >>> They're also core to the mechanism that allows collections to just do > the > >>> Right Thing(tm). For example: > >>> someBitSet map {_.toString} > >> > >> Right, but you can achieve the same result without needing implicits. > I'll > >> just show how Fantom does it but I'm sure the same applies to > >> Gosu/Ceylon/Kotlin: > >> // a set of A instances, where A has a toUppercase method > >> aSet := [ A("foo"), A("bar") ] > >> l := aSet.map | v->Str | { v.toUppercase } > >> echo(l) > >> echo(l.typeof) > >> [FOO, BAR] > >> sys::Str[] > >> The signature of map() is exactly what you would expect: it takes a > >> function that takes an element of the collection and returns > "something", > >> and the type of your expression is "list of something" (here we > transformed > >> a list of A's into a list of strings). > >> It's really not rocket science, and the fact that you need implicits to > >> achieve this result is an implementation detail that's very specific to > >> Scala. > >> Sure, you can handwave it with "You don't need to understand the exact > >> details of the API", but the truth is... you really do. Not a week goes > by > >> without me having to go check out a Javadoc or the signature of > java.util or > >> Guava method. And saying that only library authors need to know about > >> implicits, CanBuildFrom and its triple generic signature is creating a > rift > >> between the Scala developers (people who know and people who don't), > which > >> is worrisome. > > > -- You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.
