Best Scala style?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Best Scala style?

Stefan Matthias Aust
I preparation for a small talk about Scala, I currently try to port a
simple Haskell program (not really knowing Haskell though) to Scala.
I'm looking for advice how to make the Scala program as expressive and
impressive as possible :)

I assume that something like

 data Op = Add | Sub | Mul | Div

should be expressed as

 trait Op
 case object Add extends Op
 case object Sub extends Op
 case object Mul extends Op
 case object Div extends Op

and cannot be abbreviated.

While Haskell's list comprehension can often be translated to Scala's
for-comprehension, I assume that this expression cannot really be
translated directly:

 aslist  :: Int -> [Int]
 asList n = [n | n > 0]

I used

 if (n > 0) List(n) else List()

instead.   I could have used

 for (val n <- List(n); n > 0) yield n

but that's even longer than the if-variant.

If I need to call a function f for every element of a collection, I can use

 collection.map(f)

which is a nice abbreviation for

 collection.map(x => f(x))

however, can I create an anonymous function for x => f(x, c) or more
specific, for x => x + c?

What's better?

 case Nil => List(Nil)

vs.

 case List() => List(List())

What's better in this case?

 case List(n) => f(n)

vs.

 case n :: Nil => f(n)

It seems that all variants work.

If I need an accumulator function, is there a better way that using the
following inner function?

 def split[a](as: List[a]): List[Pair[List[a], List[a]]] = {
   def split0(xs: List[a], ys: List[a]): List[Pair[List[a], List[a]]] =
xs match {
     case Nil      => Nil
     case x :: Nil => Nil
     case x :: xs  => Pair((x :: ys).reverse, xs) :: split0(xs, x :: ys)
   }
   split0(as, Nil)
 }

In that method, I also dislike, that I compute "x :: ys" twice.
However, introducing a local variable is even more verbose:

     case x :: xs =>
       val zs = x :: ys; Pair(zs.reverse, xs) :: split0(xs, zs)

Is that an acceptable style?

This Haskell function definition

 type Result = (Expr, Int)

 combine'            :: Result -> Result -> ...
 combine' (l,x) (r,y) = ...

auto-expands pair parameters.  In Scala, this seems not be possible.
What's the best way to express this?  I used:

 def combine1(r1: Result, r2: Result) = {
   val Pair(l, x) = r1
   val Pair(r, y) = r2
   ...

Regards,

--
Stefan Matthias Aust



Reply | Threaded
Open this post in threaded view
|

Re: Best Scala style?

Martin Odersky
Stefan Matthias Aust wrote:

> I preparation for a small talk about Scala, I currently try to port a
> simple Haskell program (not really knowing Haskell though) to Scala.
> I'm looking for advice how to make the Scala program as expressive and
> impressive as possible :)
>
> I assume that something like
>
>  data Op = Add | Sub | Mul | Div
>
> should be expressed as
>
>  trait Op
>  case object Add extends Op
>  case object Sub extends Op
>  case object Mul extends Op
>  case object Div extends Op
>
> and cannot be abbreviated.
>
Another possibility would be to use an enumeration (see class
scala.Enumeration):

   object Op extends Enumeration {
     val Add, Sub, Mul, Div = Value
   }

The individual values are then called Op.Add, Op.Sub, and so on, unless
you choose to import Op._ . This scheme has the advantage that it is
more efficient; fewer classes will be generated.

> While Haskell's list comprehension can often be translated to Scala's
> for-comprehension, I assume that this expression cannot really be
> translated directly:
>
>  aslist  :: Int -> [Int]
>  asList n = [n | n > 0]
>
> I used
>
>  if (n > 0) List(n) else List()
>
> instead.  

I would have done the same.

>
> If I need to call a function f for every element of a collection, I can use
>
>  collection.map(f)
>
> which is a nice abbreviation for
>
>  collection.map(x => f(x))
>
> however, can I create an anonymous function for x => f(x, c) or more
> specific, for x => x + c?

Yes: either (c +) or (.+(c)) would work.

>
>  case Nil => List(Nil)
>
> vs.
>
>  case List() => List(List())
>
Purely a matter of taste. I usually use List(), as it's easier to parse
for beginners.

> What's better in this case?
>
>  case List(n) => f(n)
>
> vs.
>
>  case n :: Nil => f(n)
>
I like the List(n) pattern better.

> If I need an accumulator function, is there a better way that using the
> following inner function?
>
>  def split[a](as: List[a]): List[Pair[List[a], List[a]]] = {
>    def split0(xs: List[a], ys: List[a]): List[Pair[List[a], List[a]]] =
> xs match {
>      case Nil      => Nil
>      case x :: Nil => Nil
>      case x :: xs  => Pair((x :: ys).reverse, xs) :: split0(xs, x :: ys)
>    }
>    split0(as, Nil)
>  }
>
I am not sure. What is split supposed to do?

> In that method, I also dislike, that I compute "x :: ys" twice.
> However, introducing a local variable is even more verbose:
>
>      case x :: xs =>
>        val zs = x :: ys; Pair(zs.reverse, xs) :: split0(xs, zs)
>
> Is that an acceptable style?
>
Yes, why not?

> This Haskell function definition
>
>  type Result = (Expr, Int)
>
>  combine'            :: Result -> Result -> ...
>  combine' (l,x) (r,y) = ...
>
> auto-expands pair parameters.  In Scala, this seems not be possible.
> What's the best way to express this?  I used:
>
>  def combine1(r1: Result, r2: Result) = {
>    val Pair(l, x) = r1
>    val Pair(r, y) = r2
>    ...
>
Yes, that's fine. or use projections r1._1 for l, r1._2 for x, but
that's probably less legible.

Cheers

  -- Martin
Reply | Threaded
Open this post in threaded view
|

Re: Best Scala style?

Stefan Matthias Aust
Martin,

thanks for your reply.

Martin Odersky schrieb:

> Another possibility would be to use an enumeration (see class
> scala.Enumeration):
>
>   object Op extends Enumeration {
>     val Add, Sub, Mul, Div = Value
>   }

That's nice...  Seems to be a new feature.  The "Programming in Scala"
from 2006-01-19 doesn't mention it, I think.  Are there any other
undocumented features worth mentioning? ;)

>>  collection.map(x => f(x))
>>
>> however, can I create an anonymous function for x => f(x, c) or more
>> specific, for x => x + c?
>
> Yes: either (c +) or (.+(c)) would work.

Okay, my problem was that I didn't realize that x :: y is y.::(x) and
therefore, if I want to abbreviate y => x :: y, I have to use .::(x)

>>  case Nil => List(Nil)
>> vs.
>>  case List() => List(List())
>>
> Purely a matter of taste. I usually use List(), as it's easier to parse
> for beginners.

I did some profiling (which works just great with Scala created class
files) and noticed that Nil is better as it doesn't create an array, an
iterator and a list buffer.  Actually, creating cons cells ("::"
objects) needs quite some time.  Probably because List.<init> has to
call three static functions.

>>  def split[a](as: List[a]): List[Pair[List[a], List[a]]] = {
>>    def split0(xs: List[a], ys: List[a]): List[Pair[List[a], List[a]]] =
>> xs match {
>>      case Nil      => Nil
>>      case x :: Nil => Nil
>>      case x :: xs  => Pair((x :: ys).reverse, xs) :: split0(xs, x :: ys)
>>    }
>>    split0(as, Nil)
>>  }
>>
> I am not sure. What is split supposed to do?

It computes a list of all non-empty sublist combinations of a given
list. That is for [1,2,3] it returns [([1], [2, 3]), ([1, 2], [3])].

I just wondered whether there's some other syntax for my local
accumulator method, some kind of "named let" or whatever.

Thanks again,

--
Stefan Matthias Aust

Reply | Threaded
Open this post in threaded view
|

Re: Best Scala style?

Martin Odersky
Stefan Matthias Aust wrote:

> Martin,
>
> thanks for your reply.
>
> Martin Odersky schrieb:
>
>
>>Another possibility would be to use an enumeration (see class
>>scala.Enumeration):
>>
>>  object Op extends Enumeration {
>>    val Add, Sub, Mul, Div = Value
>>  }
>
>
> That's nice...  Seems to be a new feature.  The "Programming in Scala"
> from 2006-01-19 doesn't mention it, I think.  Are there any other
> undocumented features worth mentioning? ;)
>
Too many to list them all, I'm afraid ;-). No, class Enumeration has
been in the standard Scala library for a long time. I realize
we so far have not done a good job in writing intros to everything
that's in the Scala libraries. There is the ScalaDoc of course, but that
helps only if you know what youy are looking for.

> I did some profiling (which works just great with Scala created class
> files) and noticed that Nil is better as it doesn't create an array, an
> iterator and a list buffer.  

Good point. In the first version of Scala, varargs were passed as lists,
so List() and Nil were almost the same. In version 2, they are passed as
arrays which is generally faster except when you pass them to the `List'
constructor. We should probably optimize that case.

> Actually, creating cons cells ("::"
> objects) needs quite some time.  Probably because List.<init> has to
> call three static functions.
>
You mean, a simple (x :: xs) is slow? Which static functions do you mean?

>>> def split[a](as: List[a]): List[Pair[List[a], List[a]]] = {
>>>   def split0(xs: List[a], ys: List[a]): List[Pair[List[a], List[a]]] =
>>>xs match {
>>>     case Nil      => Nil
>>>     case x :: Nil => Nil
>>>     case x :: xs  => Pair((x :: ys).reverse, xs) :: split0(xs, x :: ys)
>>>   }
>>>   split0(as, Nil)
>>> }
>>>
>>
>>I am not sure. What is split supposed to do?
>
>
> It computes a list of all non-empty sublist combinations of a given
> list. That is for [1,2,3] it returns [([1], [2, 3]), ([1, 2], [3])].
>
If efficiency is not important, I would write `split' as follows:

   for (val i <- List.range(1, xs.length - 1)) yield xs.splitAt(i)

or, using map:

   List.range(1, xs.length - 1) map xs.splitAt

(Note there is a method splitAt in List which splits the list at a given
position).

> I just wondered whether there's some other syntax for my local
> accumulator method, some kind of "named let" or whatever.
>
No, you need to write a `def'.

Cheers

  -- Martin