This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Re: functional abstract class managing data

15 replies
Avi Pfeffer
Joined: 2009-02-23,
User offline. Last seen 42 years 45 weeks ago.

That's what I would like to do, but I can't see how to do it. The problem with

def add(i: Int): Super = new Super(i :: data) def

is that it creates an instance of Super, not of the correct subclass.

On Fri, Mar 27, 2009 at 5:10 PM, Tony Morris wrote:
> OK, but is there a benefit to implementing add in each subclass? Can
> you implement it in Super then leave other functionality to the
> subclasses?
>
>
> Avi Pfeffer wrote:
>> The subclasses all provide additional functionality and structure
>> not present in the superclass. In my application I have a rich
>> class hierarchy of models, which can further be extended by users.
>> Models have constraints, and it so happens that the Model
>> superclass can take care of the constraints on its own, and can
>> save the subclasses from having to worry about them. But the
>> subclasses extend Model in a variety of useful and unforeseen ways.
>> So I think I do need the subclassing.
>>
>> On Fri, Mar 27, 2009 at 4:53 PM, Tony Morris
>> wrote:
>>> Hello Avi, Have you considered implementing add?
>>>
>>> abstract class Super protected (data: List[Int]) { def this() =
>>> this(Nil) def add(i: Int): Super = new Super(i :: data) def
>>> doSomethingWithData = { // omitted } }
>>>
>>> Why the need for subclassing?
>>>
>>>
>>> Avi Pfeffer wrote:
>>>> Hi,
>>>>
>>>> I'm struggling with a basic problem in combining FP and OO
>>>> styles. I have a class hierarchy, and the root of the hierarchy
>>>> manages some data for all its subclasses. Only the superclass
>>>> ever has to do anything with the data. I want it to be
>>>> functional. I can write something like:
>>>>
>>>> abstract class Super protected (data: List[Int]) { def this() =
>>>>  this(Nil) def add(i: Int): Super def doSomethingWithData = {
>>>> // omitted } }
>>>>
>>>> class Sub1 private (data: List[Int]) extends Super(data) { def
>>>> this() = this(Nil) def add(i: Int) = new Sub1(i :: data) ... }
>>>>
>>>> class Sub2 private (data: List[Int]) extends Super(data) { def
>>>> this() = this(Nil) def add(i: Int) = new Sub2(i :: data) ... }
>>>>
>>>> This is bad. The definition of add is exactly the same in all
>>>> the subclasses, except that the class being constructed is
>>>> always the same as the class on which the method is invoked.
>>>> Worse, since only Super needs to access data, the subclasses
>>>> should not even have to know about the existence of data. Data
>>>> should be private in Super, and the subclasses should not need
>>>> their private constructors. With mutable state this would be
>>>> easy:
>>>>
>>>> abstract class Super { private var data: List[Int] = Nil def
>>>> add(i: Int) { data = i :: data } def doSomethingWithData = { //
>>>> omitted } }
>>>>
>>>> class Sub1 extends Super { ... }
>>>>
>>>> class Sub2 extends Super { ... }
>>>>
>>>> There must be a simple solution that I'm missing. Any ideas?
>>>>
>>>> Thanks, Avi
>>>>
>>> -- Tony Morris http://tmorris.net/
>>>
>>>
>>>
>>
>
> --
> Tony Morris
> http://tmorris.net/
>
>
>

Seth Tisue
Joined: 2008-12-16,
User offline. Last seen 34 weeks 3 days ago.
Re: functional abstract class managing data

>>>>> "Martin" == Martin S Weber writes:

Chris> The problem is that you either have to delay specifying the actual
Chris> type until the end types in a heirarchy (not great for
Chris> extensibility) or use factory functions, which add substantial
Chris> boilerplate.

Martin> I wonder if this is java heritage/compat (not a java guy
Martin> here). This are the moments where I just think ruby.. class A;
Martin> def A.getme ; new ; end ; end class B < A ; end B.getme =>
Martin> #

In your Ruby code getme is a factory function, same as Chris suggested
using in Scala. I'm not convinced this pattern looks any worse in Scala
than it does in Ruby.

Seth Tisue
Joined: 2008-12-16,
User offline. Last seen 34 weeks 3 days ago.
Re: functional abstract class managing data

>>>>> "Avi" == Avi Pfeffer writes:

Avi> There must be a simple solution that I'm missing. Any ideas?

I got kind of lost in all the replies to this. Did your question ever
get answered to your satisfaction? (I don't recall seeing it getting
answered to my own satisfaction, but maybe that's just because I got
lost.)

Martin S. Weber
Joined: 2008-12-23,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

Quoting Seth Tisue :

>>>>>> "Martin" == Martin S Weber writes:
>
> Chris> The problem is that you either have to delay specifying the actual
> Chris> type until the end types in a heirarchy (not great for
> Chris> extensibility) or use factory functions, which add substantial
> Chris> boilerplate.
>
> Martin> I wonder if this is java heritage/compat (not a java guy
> Martin> here). This are the moments where I just think ruby.. class A;
> Martin> def A.getme ; new ; end ; end class B < A ; end B.getme =>
> Martin> #
>
> In your Ruby code getme is a factory function, same as Chris suggested
> using in Scala. I'm not convinced this pattern looks any worse in Scala
> than it does in Ruby.

Yeah, "new" is a factory method (surprise! A constructor is a
factory!), that's true. But compare the above to all the different
attempts to make a "virtual" factory. This "pattern" doesn't "look" at
all in ruby. You do the minimal amount of code possible at all to say,
"I want a constructor inside this type hierarchy". It's length equals
one (!) keyword.

Why is it so hard for you to realize that you have to write a whole
lot more in scala? (I don't care about "ruby is better" or anything
like that. Imo you can learn something from every language. And step
#1 in that is admitting to self that other languages do certain things
better. template'd constructors in C++ come to mind. Uniformity of
syntax in Lisp-languages. Ease of use in type-hierarchies inside
dynamic languages. and so on. You can "heal" this with either a lot of
code or a bit of insight.)

-Martin

Seth Tisue
Joined: 2008-12-16,
User offline. Last seen 34 weeks 3 days ago.
Re: functional abstract class managing data

>>>>> "Martin" == Martin S Weber writes:

>> In your Ruby code getme is a factory function, same as Chris
>> suggested using in Scala. I'm not convinced this pattern looks any
>> worse in Scala than it does in Ruby.

Martin> Why is it so hard for you to realize that you have to write a
Martin> whole lot more in scala?

Sorry, I misunderstood your Ruby example. Just me being dense -- not
closed-minded.

You seem to have the impression I'm a Scala zealot or static typing
zealot. I'm not. My other favorite language right now is Clojure. For
my current project, I chose Scala over Clojure because at the time I
chose (early 2008), Clojure seemed too new, and I had lots of existing
Java code and Scala offered a smoother path for gradually transitioning.
If I were starting a brand new project today, I'm not sure what I would
choose. I'm happy coding in Scala, but I was also happy the years I
spent coding in Common Lisp.

Ricky Clarkson
Joined: 2008-12-19,
User offline. Last seen 3 years 2 weeks ago.
Re: functional abstract class managing data
It is common for untyped languages to have better syntax than typed languages.  It takes a lot more work to make syntax work well in typed languages, but when it's right, it's damn right. (compare QuickCheck to any equivalent in Ruby).  Scala makes a good effort, but while comparisons with Ruby are interesting sometimes, and syntax can be leeched, they don't really mean anything.
Let's keep looking at Ruby, Groovy, Clojure, Smalltalk and Python, but don't expect the syntax to be as easy without losing type safety.

2009/3/29 Seth Tisue <seth@tisue.net>
>>>>> "Martin" == Martin S Weber <martin.weber@nist.gov> writes:

 >> In your Ruby code getme is a factory function, same as Chris
 >> suggested using in Scala.  I'm not convinced this pattern looks any
 >> worse in Scala than it does in Ruby.

 Martin> Why is it so hard for you to realize that you have to write a
 Martin> whole lot more in scala?

Sorry, I misunderstood your Ruby example.  Just me being dense -- not
closed-minded.

You seem to have the impression I'm a Scala zealot or static typing
zealot.  I'm not.  My other favorite language right now is Clojure.  For
my current project, I chose Scala over Clojure because at the time I
chose (early 2008), Clojure seemed too new, and I had lots of existing
Java code and Scala offered a smoother path for gradually transitioning.
If I were starting a brand new project today, I'm not sure what I would
choose.  I'm happy coding in Scala, but I was also happy the years I
spent coding in Common Lisp.

--
Seth Tisue / http://tisue.net
lead developer, NetLogo: http://ccl.northwestern.edu/netlogo/

Martin S. Weber
Joined: 2008-12-23,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

Quoting Ricky Clarkson :

> It is common for untyped languages to have better syntax than typed
> languages. It takes a lot more work to make syntax work well in typed
> languages, but when it's right, it's damn right. (compare QuickCheck to any
> equivalent in Ruby). Scala makes a good effort, but while comparisons with
> Ruby are interesting sometimes, and syntax can be leeched, they don't really
> mean anything.
> Let's keep looking at Ruby, Groovy, Clojure, Smalltalk and Python, but don't
> expect the syntax to be as easy without losing type safety.

I see your point, but otoh imo the compiler could also deduce type
parameters itself, i.e. if you do need a [A <: B, C-, D], it could
insert it on its own. I haven't thought much about how doable things
like these are, but actually I expect a "modern" statically typed
language to be as syntax-lean and noise-less as a "usual" dynamic
"scripting" language. After all, to me at least, that is the point of
type inference. And "somewhen" (for whatever value of some) I do
believe we're gonna be there.

-Martin

Ricky Clarkson
Joined: 2008-12-19,
User offline. Last seen 3 years 2 weeks ago.
Re: functional abstract class managing data
For Scala, subtyping hampers type inference quite seriously.  You might want to look at ML or Haskell for complete type inference.
It has little to do with how modern a language is.  "I would expect a modern language to solve the halting problem".

2009/3/29 Martin S. Weber <martin.weber@nist.gov>
Quoting Ricky Clarkson <ricky.clarkson@gmail.com>:

It is common for untyped languages to have better syntax than typed
languages.  It takes a lot more work to make syntax work well in typed
languages, but when it's right, it's damn right. (compare QuickCheck to any
equivalent in Ruby).  Scala makes a good effort, but while comparisons with
Ruby are interesting sometimes, and syntax can be leeched, they don't really
mean anything.
Let's keep looking at Ruby, Groovy, Clojure, Smalltalk and Python, but don't
expect the syntax to be as easy without losing type safety.

I see your point, but otoh imo the compiler could also deduce type parameters itself, i.e. if you do need a [A <: B, C-, D], it could insert it on its own. I haven't thought much about how doable things like these are, but actually I expect a "modern" statically typed language to be as syntax-lean and noise-less as a "usual" dynamic "scripting" language. After all, to me at least, that is the point of type inference. And "somewhen" (for whatever value of some) I do believe we're gonna be there.

-Martin

Chris Twiner
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

On Sun, Mar 29, 2009 at 8:36 PM, Martin S. Weber wrote:
> Quoting Ricky Clarkson :
>
>> It is common for untyped languages to have better syntax than typed
>> languages.  It takes a lot more work to make syntax work well in typed
>> languages, but when it's right, it's damn right. (compare QuickCheck to
>> any
>> equivalent in Ruby).  Scala makes a good effort, but while comparisons
>> with
>> Ruby are interesting sometimes, and syntax can be leeched, they don't
>> really
>> mean anything.
>> Let's keep looking at Ruby, Groovy, Clojure, Smalltalk and Python, but
>> don't
>> expect the syntax to be as easy without losing type safety.
>
> I see your point, but otoh imo the compiler could also deduce type
> parameters itself, i.e. if you do need a [A <: B, C-, D], it could insert it
> on its own. I haven't thought much about how doable things like these are,
> but actually I expect a "modern" statically typed language to be as
> syntax-lean and noise-less as a "usual" dynamic "scripting" language. After
> all, to me at least, that is the point of type inference. And "somewhen"
> (for whatever value of some) I do believe we're gonna be there.
>
> -Martin
>

Its worth noting that in the attempt for the sip I made I got to the
point of being able to generate the entire virtual constructor factory
including type parameters based on the declaration of the factory
function alone. Its not particularly troublesome, unfortunately other
issues with integrating the plugin and the compiler phases stopped it
being useful :<

I was also short before being able to figure out the generating of the
type member itself, but there are some more difficult issues there.

Martin S. Weber
Joined: 2008-12-23,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

Quoting Ricky Clarkson :

> For Scala, subtyping hampers type inference quite seriously. You might want
> to look at ML or Haskell for complete type inference.
> It has little to do with how modern a language is. "I would expect a modern
> language to solve the halting problem".
>

Oh? So this problem has been proven unsolvable?

-Martin

Martin S. Weber
Joined: 2008-12-23,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

Quoting "Martin S. Weber" :

> Quoting Ricky Clarkson :
>
>> For Scala, subtyping hampers type inference quite seriously. You might want
>> to look at ML or Haskell for complete type inference.
>> It has little to do with how modern a language is. "I would expect a modern
>> language to solve the halting problem".
>>
>
> Oh? So this problem has been proven unsolvable?

Or in other words: If (e.g. Objective Ca)ML & Haskell can do it, it
probably seems solvable, even in the face of subtyping. I don't
exactly understand why type inference stops at function parameters
either. Why there aren't more existential types inferenced. I still
have a lot to read though, and sometime it will become obvious, I
hope. My problem though is that (some) experience with other languages
show things possible.

-Martin

ijuma
Joined: 2008-08-20,
User offline. Last seen 22 weeks 3 days ago.
Re: functional abstract class managing data

On Sun, 2009-03-29 at 17:30 -0400, Martin S. Weber wrote:
> Quoting Ricky Clarkson :
>
> > For Scala, subtyping hampers type inference quite seriously. You might want
> > to look at ML or Haskell for complete type inference.
> > It has little to do with how modern a language is. "I would expect a modern
> > language to solve the halting problem".
> >
>
> Oh? So this problem has been proven unsolvable?

Assuming you're asking about the type inference with subtyping comment,
it's an area of active research. It's not just a matter of sitting down
and implementing it, but it's not unsolvable either. At least that's
what I have heard, I am not an expert by any means. :)

Best,
Ismael

Seth Tisue
Joined: 2008-12-16,
User offline. Last seen 34 weeks 3 days ago.
Re: functional abstract class managing data

>>>>> "Martin" == Martin S Weber writes:

>>> For Scala, subtyping hampers type inference quite seriously. You
>>> might want to look at ML or Haskell for complete type inference.
>>> It has little to do with how modern a language is. "I would expect
>>> a modern language to solve the halting problem".

Martin> Oh? So this problem has been proven unsolvable?
Martin> Or in other words: If (e.g. Objective Ca)ML & Haskell can do
Martin> it, it probably seems solvable, even in the face of
Martin> subtyping.

Well, you were cautious enough to say "probably seems". I agree, it
*probably seems* solvable... as long as you don't think about it very
much or try to implement it ;-)

Haskell and Standard ML aren't object-oriented. OCaML is. I don't
actually know how OCaML handles the intersection of type inference and
object orientation. Can anyone point me at something good I can read
that explains it briefly...?

James Iry
Joined: 2008-08-19,
User offline. Last seen 1 year 23 weeks ago.
Re: functional abstract class managing data


On Sun, Mar 29, 2009 at 2:35 PM, Martin S. Weber <martin.weber@nist.gov> wrote:

Or in other words: If (e.g. Objective Ca)ML & Haskell can do it,

Neither of which has a type system like Scala's.  It's not the lack of modernity in the language, it's the nature of the type system that presents the challenge to full type inference.  For what it's worth, F# is another language with subtyping and type inference.  Their approach seems to be to try to ignore subtyping as much as they can, but when they reach a point where they can't ignore it they require the user to annotate types explicitly.  Unfortunately, I don't think full details of F#'s type inference algorithm have ever been published. But I suspect it only works under an F# assumption that you'll mostly use algebraic datatypes rather than full power of subtypes except when interfacing with other .NET libraries.  If so, that obviously wouldn't fit Scala's design.

Avi Pfeffer 2
Joined: 2009-02-25,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

On Sat, Mar 28, 2009 at 7:43 PM, Seth Tisue wrote:
> I got kind of lost in all the replies to this.  Did your question ever
> get answered to your satisfaction?  (I don't recall seeing it getting
> answered to my own satisfaction, but maybe that's just because I got
> lost.)

Not really. The proposed solutions address the problem of having to
redefine add in all the subclasses in a nice way. But as far as I can
tell, unless I'm misunderstanding, they don't solve the problem that
data should be private in Super, and the subclasses shouldn't even
have a constructor that takes data as an argument.

I'd like to see something like:

trait WithData {
val data: List[Int] = Nil

def add(x: Int) = renew {
override val data: List[Int] = x :: data
}
}

class Super extends WithData {
def doSomethingWithData { println(data) }
}

class Sub1 extends Super

class Sub2 extends Super

Here "renew" means create a new instance of whatever class the object
happens to be, but with data overridden as specified.

I don't know if something like this is at all feasible.

Avi

Florian Hars
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
Re: functional abstract class managing data

Martin S. Weber schrieb:
> Oh? So this problem has been proven unsolvable?

In general, yes. But there are many interesting
not-quite-as-general cases where something is possible, and the challenge
is to push the boundaries as far as possible without losing decidability
or human readable error messages. This is, as they say, an open research
problem.

Basic Hindley-Milner type inference for something like core ML is almost
trivial, IIRC there is an implementation in Scala by Examples. But if you want
to have for example Generalized Algebraic Datatypes, type inference becomes
undecidable.

GADTs are the feature that makes the following code compile without a
pattern match warning in scala:

--------------->
def foo(x: Option[Int]) =
x match {
case s@Some(_) =>
s match {
case Some(1) => 1
case Some(_) => 2
/* Look, no warning for missing case None! */
}
case None => 0
}
<---------------

while the equivalent code in ocaml, which has type inference but no
GADTs, gives an incorrect warning:

--------------->
let foo x =
match x with
| Some(_) as s -> (
match s with
| Some(1) -> 1
| Some(_) -> 2
)
| None -> 0

Warning P: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
None
....................(
match s with
| Some(1) -> 1
| Some(_) -> 2
)
<---------------

People are trying to push the envelope here, see for example all the
Haskell luminaries Microsoft has gobbled up:
http://research.microsoft.com/apps/pubs/default.aspx?id=79812

Ocaml can infer object types too,
but again there are limits: you must use type annotations if you use features like
named and optional arguments (and type errors can easily print a few hundred lines).
I'd guess you will run into similar problems if your language supports java-style
method overriding (it is usually a safe bet to assume that adding anything
non-trivial to an extension of Hindley-Milner will make the whole thing undecidable).

- Florian.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland