Re: [scala-functional] use the class keyword?

174 views
Skip to first unread message

Tony Morris

unread,
Jan 25, 2013, 5:57:31 PM1/25/13
to scala-fu...@googlegroups.com
On 26/01/13 05:34, aidy lewis wrote:
> I have been reading Brian Marick's "Functional Programming for the
> Object Oriented Programmer". He states that one of the key elements of
> FP, is that functions operate on basic data types, and we shouldn't
> increase types through classes.
>
> Are there any FP Scala programmers who are not using the class keyword
> and using functions within functions?
>
> Aidy
> --
> You received this message because you are subscribed to the Google
> Groups "scala-functional" group.
> To unsubscribe from this group, send email to
> scala-function...@googlegroups.com.
>
>
Hi aidy, this is an intriguing question with regard to what the book
says. Are you able to provide the original quote? I don't have that book
and neither does anybody with whom I have spoken so far.

--
Tony Morris
http://tmorris.net/

Stephen Compall

unread,
Jan 25, 2013, 5:59:06 PM1/25/13
to scala-fu...@googlegroups.com

On Jan 25, 2013 2:35 PM, "aidy lewis" <aidy....@gmail.com> wrote:
> I have been reading Brian Marick's "Functional Programming for the Object Oriented Programmer". He states that one of the key elements of FP, is that functions operate on basic data types, and we shouldn't increase types through classes. 

There may be a subtlety missing in this paraphrasing.  Could you post the original quote from the book, with enough context so we know what was originally said?

--
Stephen Compall
If anyone in the MSA is online, you should watch this flythrough.

aidy lewis

unread,
Jan 26, 2013, 4:19:12 AM1/26/13
to scala-fu...@googlegroups.com
On 25 January 2013 22:57, Tony Morris <tonym...@gmail.com> wrote:
> On 26/01/13 05:34, aidy lewis wrote:
>> I have been reading Brian Marick's "Functional Programming for the
>> Object Oriented Programmer". He states that one of the key elements of
>> FP, is that functions operate on basic data types, and we shouldn't
>> increase types through classes.
>>

> Are you able to provide the original quote? I don't have that book
> and neither does anybody with whom I have spoken so far.


"""
The functional style has three characteristics:

• Functions that create other functions. More narrowly, functions that
take arguments that they use to parameterize the functions they
create.

• The use of basic datatypes, notably maps, rather than the creation
of classes. In the object-
oriented style, you have many types (classes). Limiting the number of
methods in a class is
seen as a good thing. In the functional style, you have few types and
a larger number and
variety of general-purpose functions that act on them.

• Trying to solve problems by setting up data that can be seen as
flowing through functions.
This involves abstracting away control flow, and also carefully
isolating code that might need
to change state.

""" (Source fp-oo)
--
Aidy
Developer-Artist

Stephen Compall

unread,
Jan 26, 2013, 12:06:35 PM1/26/13
to scala-fu...@googlegroups.com
On Sat, 2013-01-26 at 09:19 +0000, aidy lewis wrote:
> • The use of basic datatypes, notably maps, rather than the creation of
> classes. In the object- oriented style, you have many types (classes).
> Limiting the number of methods in a class is seen as a good thing. In
> the functional style, you have few types and a larger number and
> variety of general-purpose functions that act on them.

Okay, this is rather Clojure-oriented, so I can see the problem. In
untyped Clojure, and with Typed Clojure's record types and unions, this
is a reasonable characterization. It is less useful in the context of
most well-typed functional languages, for which working with maps for
heterogenous data is very inconvenient and not at all in the spirit of
things.

However, it is true that functional programming eschews class
*hierarchies* in favor of general-purpose types with many operations.
(I don't classify the Scala encoding of algebraic data types as yielding
a hierarchy, though Scala's type system does.)

Here is an approximation of a type I wrote for a method's result
recently.

String \/ F[(RPA, State[Int, RPB])] // for which Monad[F]

which means "either a string or some monadic F of an RPA and an
Int-state transformer yielding an RPB". RPA and RPB are domain-specific
classes defined elsewhere, incidentally.

This would be a complicated result to describe without preexisting
general-purpose types like \/ "either" and (,) "pair". But as it is,
you can just read the type left-to-right to see what it means, and I
have access to all the operations already defined for \/, State, (,),
and F:Monad.

Assuming the book is as Clojure-focused as it seems, it probably won't
be much use describing how types fit into functional programming. As a
diversion, I suggest thinking about \/, (,), how they can be put
together in different combinations, and the well-typed meaning of each
combination.

As a further diversion, you might also consider how well untagged unions
as used in Clojure substitute for \/. Given a Clojure value shaped like
my example type, how can you tell which side of the \/ you're on? Since
writing general functions is good, how far can you get to the fully
generalized "A \/ B" before your approach breaks down?

--
Stephen Compall
^aCollection allSatisfy: [:each|aCondition]: less is better

Ben Hutchison

unread,
Jan 30, 2013, 6:25:34 AM1/30/13
to scala-fu...@googlegroups.com
I came across to functional programming after years of mainstream OO
and had object-oriented values pretty deeply ingrained when I started
learning (Haskell/Scala flavored-) FP. It took me a while to see with
new eyes, and make my peace with this issue.

Going purely by Brain's quote, I think I have reached a different
formulation. To me, Haskellers and OOers as pursuing the same basic
and worthy goal, but employing different implementation techniques to
get there. The goal is "abstraction over data", that is, to write
common code (for example, a sort function) that works over different
"sortable" data.

OO uses classes & inheritance: Dispatch is at runtime based upon the
special asymmetric first argument (ie the "this" pointer), class
pointers are maintained for every object in in the heap, an object
cannot change its class , nor re-implement the same interface
differently, for different code contexts.

Haskell (and potentially Scala) use Typeclasses: Dispatch is at
compile time, based on the static types. It's Symmetric - ie the first
argument has no special status (no "this" pointer). Class pointers are
not required in the heap. Data can be operated on by different
instances of a type class in different contexts The same interface can
be re-implemented multiple times, for example, Monoid[Int] can be Max,
Min, Sum, Product. Unlike OO, the user of a data type is not locked
into using the implementation provided by the creator of the data
item; it's bring-your-own if you wish. On the downside, where type
information is "forgotten", eg a heterogeneous collection of unknown
types of sortable data, existential types are needed to encode the
equivalent of OO dispatch over the elements.

Overall, I've become convinced that the typeclass model is superior,
for most use cases.

I now see OO dispatch as a way of getting some abstraction over data,
that could be implemented with what was available back in the 80's
when it was developed. But around this "implementation trick", such a
self-reinforcing body of folklore has sprung up, that if all the books
of "object-oriented wisdom" ever printed were stacked together, they'd
stretch to the moon and back twice.

Whereas, to work well, typeclasses need a much more sophisticated
compiler and type-system than OO does, hence their later evolution in
the 90s. Being type-driven, they're also not an option for
"dynamically typed" FP languages like Clojure or Erlang.

But, you might reasonably wonder, if typeclasses are basically
equivalent to classes (but with a different dispatch mechanism), why
does Haskell's typeclass hierarchy look so different to say, Java's?
Monads and Monoids, vs Hashtables and ArrayLists..?

First, it reflect's the different programmer demographics. The high
abstraction of Haskell reflects its origin among academic and research
programmers.

Secondly, it reflects an advantages of typeclasses over OO: the
ability to support multiple different implementations in different
contexts. That in turn enables, and indeed rewards, focused
abstractions such as Monoids. Whereas on the OO side, such
abstractions provoke discomfort by revealing the flaws in the OO
paradigm, requiring the publication of more dogma to banish the
uncomfortable questions from discussion. Questions like "why do we
need both Comparable and Comparator in Java?"

-Ben

On Sat, Jan 26, 2013 at 6:34 AM, aidy lewis <aidy....@gmail.com> wrote:
> I have been reading Brian Marick's "Functional Programming for the Object
> Oriented Programmer". He states that one of the key elements of FP, is that
> functions operate on basic data types, and we shouldn't increase types
> through classes.
>

John Nilsson

unread,
Jan 30, 2013, 11:39:36 AM1/30/13
to scala-fu...@googlegroups.com

I'm curious. Do you see static dispatch as contributing to the type class superiority, or just a unfortunate tradeoff?

Btw, I believe protocols in clojure are considered to be comparable to type classes with dynamic dispatch.

BR
John

To unsubscribe from this group and stop receiving emails from it, send an email to scala-function...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Ben Hutchison

unread,
Feb 3, 2013, 3:34:54 AM2/3/13
to scala-fu...@googlegroups.com
On Thu, Jan 31, 2013 at 3:39 AM, John Nilsson <jo...@milsson.nu> wrote:
> I'm curious. Do you see static dispatch as contributing to the type class
> superiority, or just a unfortunate tradeoff?

Yes, IMO it's part of why type classes can be better, for example:

- No runtime overhead for "multi-methods" (dispatching based upon all
parameters not just first)
- Can refine the static return type as part of dispatch process, as
done with the CanBuildFrom pattern used in Scala collections

Dynamic (ie runtime) dispatch will/can only be better if/when there is
some contextual info available at runtime that isn't present at
compile time. When our types have the context we need to dispatch to
the desired method, then it seems extra overhead to defer the decision
to runtime (I accept that's not always, but mostly).

>
> Btw, I believe protocols in clojure are considered to be comparable to type
> classes with dynamic dispatch.
>

In Clojure, how is the protocol "married up" with the data it operates
over, at runtime? Surely it must be by some tag or pointer, associated
with the data being operated on, as in object-oriented dispatch..? Is
the protocol impl. a mutable "slot" that the programmer can update?

-Ben

Haoyi Li

unread,
Jul 1, 2013, 2:59:14 AM7/1/13
to scala-fu...@googlegroups.com
I think the most important thing about the dispatch technique chosen is that it determines the directionality of your dependencies. I'm using dependencies loosely in a 'can A work unchanged if B is modified' way.

Polymorphic Dispatch: 
prototypical/class-based, static/dynamic, doesn't matter
- Data Types depend on Operations
- Implementations are grouped per-data-type (e.g. in the class-bodies)

Conditional Dispatch: 
if-elses, pattern-matching, switch-statement, dictionary-of-functions (python-style), java-style visitor-pattern
- Operations depend on Data Types
- Implementations are grouped per-operation (e.g. in the big switch statement)

Generic Dispatch: 
Haskell&Scala typeclasses/Clojure multimethods/Python single-dispatch-generics
- Implementations depend on both Data Types and Operations, but neither depends on/is aware of each other
- Implementations can go anywhere

Viewed this way, the idea of generic dispatch isn't something limited to statically typed languages, and I think the bulk of people who want this sort of thing don't do it to avoid the vtable lookup. It's done in order to control the dependencies of the program, or to work within a set of dependencies which are imposed upon you by external factors. Furthermore, you can use generic dispatch to simulate virtual dispatch if you just group all the implementations on a per-object bases, and to simulate conditional dispatch if you group all the implementations on a per-operation basis (albeit generally with much more boilerplate)..

For example, the "neither is aware of each other" using typeclasses is used to great effect in e.g. the bunch of scala JSON serializers, which are in the business of letting a framework by person A serialize data types from person B where neither person A nor person B are going to change their code to let it work. In general, generic dispatch is the ultimate flexibility, allowing you to structure your dependencies however you like, while conditional/polymorphic dispatch each provide one facet of that flexibility at a lower boilerplate-cost (at least in the languages I know).

-Haoyi
Reply all
Reply to author
Forward
0 new messages