No curry by default leads to limitation in point-free style coding?

194 views
Skip to first unread message

Amitava Shee

unread,
May 2, 2011, 4:38:52 PM5/2/11
to scala-l...@googlegroups.com
Last week at Philly ETE conference, I was having a discussion with Daniel Speiwak regarding currying in Scala vs. Haskell.

Daniel postulated that scala is no different than haskell as far as currying.

In Haskell, all multi-parameter functions are ultimately wrapped into a series of curried functions each taking a single parameter. E.g.
foo :: Int -> String -> String
foo i s = ....

Haskell transforms that into a function taking an Int and returning another "function" taking yet another Int to finally return an Int

I think, the scala equivalent is
def foo(i: Int)(s: String) : String = ....

Most scala code I have seen, tends not to define methods in this form. The default is

def foo(i: Int, s: String) : String = ...

But as suggested by Daniel, this signature can be expressed in Haskell as:

foo:: (Int, String) -> String -- notice the type signature - arguments in tuple now
foo (i,s) = ....

Is this truly what's happening in Scala? Is Scala passing parameters in the 2nd form - as tuples behind the scene?

In any case, it seems like
1. The default in Scala is to pass arguments in "tuple-like" syntax.
2. The default in Haskell is 1-argument curried form.

What are the implications for the choice of default here? Is it making it harder to code in a "point-free" style in Scala?

Consider the code below

Haskell
=======
mul :: Num a => a -> a -> a -- takes 2 Int's and returns an Int
mul i j = i * j -- i,j passed explicitly aka point-full
or
mul = * -- implicit parameters aka point-free style

dbl :: Num a => a -> a
dbl i = i * 2 -- point-full
or
dbl = (*) 2 -- point-free

Now I can compose these functions in a point-free style
*Pointfree> let dblAndMul = mul.dbl

*Pointfree> :type dblAndMul
dblAndMul :: Integer -> Integer -> Integer
*Pointfree> dblAndMul 2 3
12

Scala
=====
scala> def mul(x: Int, y: Int) = x * y
mul: (x: Int,y: Int)Int

scala> def dbl(x: Int) = x * 2
dbl: (x: Int)Int

scala> val m : Int => Int = mul(2,_) // I don't really want to bind the arg '2' yet.
m: (Int) => Int = <function1>

scala> val d  = dbl _
d: (Int) => Int = <function1>

scala> val dm = d compose m
dm: (Int) => Int = <function1>

scala> dm(3)
res6: Int = 12


I must be missing something here! Scala gurus, please set me right if there is a better way.

Similar discussions
===================
http://pchiusano.blogspot.com/2009/08/scala-success-story-commercial-usage-of.html
Functions aren't curried by default, and I find working with curried functions kind of cumbersome in Scala. There are also some language warts that make it pretty much impossible to program in a point-free style (namely, the somewhat arbitrary distinction between methods, defined using def, and function values which implement one of the FunctionN traits). This might not be so terrible if you didn't have to fully annotate parameter types in method definitions, but you do, and it can get kind of annoying. Again, we are still much better off than we are in Java, but it's possible to do better (for instance, Haskell).

http://scala-programming-language.1934581.n4.nabble.com/Point-free-td1943691.html

I enjoyed Daniel's talk on type inference very much and I hope this does not offend him or anyone else in the community.

Thanks & Regards,
Amitava Shee
twtr: @amitavashee



Daniel Spiewak

unread,
May 3, 2011, 12:22:37 AM5/3/11
to scala-l...@googlegroups.com
I think there are some deeper issues with point-free in Scala that come from Scala's fundamental nature as an object-functional language.  I think that no signature better exemplifies these issues than the foldl/foldLeft function/method:

Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a

trait TraversableOnce[+A] {
  …
  def foldLeft[B](b: B)(f: (B, A) => B): B
}

Notice what is going on here.  In Haskell, the order of parameters follows from least- to most-specific:
  • reduce function
  • seed value
  • list
In Scala, we do the opposite:
  • collection
  • seed value
  • reduce function
Put another way: in Scala, all of the parameter ordering is backwards!  Take a look around some of the other functions in the standard library and compare them to their Haskell/SML equivalents (map is a good one).  Scala is very consistently inverted.

This follows almost immediately from the fact that the method receiver is effectively (and literally) the first argument of the function.  Method receivers are, by definition, the most specific parameter, and thus we have our ordering reversed.  Scala exacerbates this slightly by giving trailing, curried function parameters a special syntax (obviating the need for parentheses).  Not that this special syntax is a bad thing per se, but it does cause some major problems for point-free composability.

The issue is this: how do you compose together a progressively more specialized version of a function when the first parameter you specify must always be the least general?  Consider the following Haskell snippet:

Prelude> let sum = foldl (+)
Prelude> :t sum
sum :: Integer -> [Integer] -> Integer

Ignoring the fact that the type inferencer gets this wrong…  Haskell makes it very easy to build up partial functions like this.  I can take this sum function and then further specialize it against any seed value, to be used on any collection.  However, if we try to do that with Scala, we immediately get stuck specializing against the collection value, without ever getting the chance to leave it generic.

Oh, and this whole situation is made a hundred times worse by the fact that Scala doesn't have rank-2 types, so we can't just get a value for TraversableOnce#foldLeft which we can then pass to some utility function which might reverse the parameter order.

In short, there are a lot of reasons Scala isn't particularly amenable to point free that have nothing to do with tupled vs curried formal parameters in the "default" (as in: customarily applied) syntax.  Having worked with Scala for quite a while now and consistently tried to compose using point-free combinators, I'm pretty certain that the reason I outlined (parameter ordering) is the biggest stumbling block in this area.

Daniel

P.S. I know this doesn't exactly address your main point, but I think it's important to see that the syntax isn't really the main issue here.  It's all about the fundamental semantics of the underlying language paradigm, and that paradigm is not particularly amenable to point-free.

martin odersky

unread,
May 3, 2011, 3:00:35 AM5/3/11
to scala-l...@googlegroups.com
Very good analysis, Daniel. I think it's fair to say that point-free style is not idiomatic Scala. If you ask me, it's not particularly missed. If I could choose just one syntax for closures, I'd pick the one with underscores: I.e. something like:
 
  xs.map(_)  or
  _  + _   or
  _.fold(f, _)

In my mind, the underscores give important visual clues. Sure, pointfree is more concise, but except in very specialized fields, also more obscure. At least to my eyes. YMMV.

Cheers

 -- Martin
--
----------------------------------------------
Martin Odersky
Prof., EPFL and CEO, Scala Solutions
PSED, 1015 Lausanne, Switzerland


Tony Morris

unread,
May 3, 2011, 5:27:13 PM5/3/11
to scala-language
The folds are in this order for a more specific reason.

The foldr function forms the catamorphism for [] such that passing the
constructors produces an identity function.

foldr (:) [] = id

* The same can be said for maybe and Maybe:

maybe Nothing Just = id

* either and Either:

either Left Right = id

* if/else and Boolean:

Just kidding. It is generally accepted that these arguments are around
the wrong way.

I'd do it this way for Scala:
def ifelse[F[_]: Functor, X](t: => X, f: => X): F[Boolean] => F[X]

PS: The type inferencer doesn't get foldl (+) wrong -- it's just
defaulting. Turn it off.

On May 3, 2:22 pm, Daniel Spiewak <djspie...@gmail.com> wrote:
> I think there are some deeper issues with point-free in Scala that come from
> Scala's fundamental nature as an object-functional language.  I think that
> no signature better exemplifies these issues than the foldl/foldLeft
> function/method:
>
> Prelude> :t foldl
> foldl :: (a -> b -> a) -> a -> [b] -> a
>
> trait TraversableOnce[+A] {
>   …
>   def foldLeft[B](b: B)(f: (B, A) => B): B
>
> }
>
> Notice what is going on here.  In Haskell, the order of parameters follows
> from least- to most-specific:
>
>    - reduce function
>    - seed value
>    - list
>
> In Scala, we do the opposite:
>
>    - collection
>    - seed value
>    - reduce function
>
> Put another way: in Scala, all of the parameter ordering is backwards!  Take
> a look around some of the other functions in the standard library and
> compare them to their Haskell/SML equivalents (map is a good one).  Scala is
> very consistently inverted.
>
> This follows almost immediately from the fact that the method receiver is
> effectively (and literally) the first argument of the function.  Method
> receivers are, by definition, the most specific parameter, and thus we have
> our ordering reversed.  Scala exacerbates this slightly by giving trailing,
> curried function parameters a special syntax (obviating the need for
> parentheses).  Not that this special syntax is a bad thing per se, but it
> does cause some major problems for point-free composability.
>
> The issue is this: how do you compose together a progressively more
> specialized version of a function when the *first* parameter you specify
> must always be the least general?  Consider the following Haskell snippet:
>
> Prelude> let sum = foldl (+)
> Prelude> :t sum
> sum :: Integer -> [Integer] -> Integer
>
> Ignoring the fact that the type inferencer gets this wrong…  Haskell makes
> it very easy to build up partial functions like this.  I can take this sum
> function and then further specialize it against any seed value, to be used
> on any collection.  However, if we try to do that with Scala, we *
> immediately* get stuck specializing against the collection value, without
> ever getting the chance to leave it generic.
>
> Oh, and this whole situation is made a hundred times worse by the fact that
> Scala doesn't have rank-2 types, so we can't just get a value for
> TraversableOnce#foldLeft which we can then pass to some utility function
> which might reverse the parameter order.
>
> In short, there are a lot of reasons Scala isn't particularly amenable to
> point free that have nothing to do with tupled vs curried formal parameters
> in the "default" (as in: customarily applied) syntax.  Having worked with
> Scala for quite a while now and consistently *tried* to compose using
> point-free combinators, I'm pretty certain that the reason I outlined
> (parameter ordering) is the biggest stumbling block in this area.
>
> Daniel
>
> P.S. I know this doesn't *exactly* address your main point, but I think it's
> important to see that the syntax isn't really the main issue here.  It's all
> about the fundamental semantics of the underlying language paradigm, and
> that paradigm is not particularly amenable to point-free.
>
> >http://pchiusano.blogspot.com/2009/08/scala-success-story-commercial-...
> > Functions aren't curried by default, and I find working with curried
> > functions kind of cumbersome in Scala. There are also some language warts
> > that make it pretty much impossible to program in a point-free style
> > (namely, the somewhat arbitrary distinction between methods, defined using
> > def, and function values which implement one of the FunctionN traits). This
> > might not be so terrible if you didn't have to fully annotate parameter
> > types in method definitions, but you do, and it can get kind of annoying.
> > Again, we are still much better off than we are in Java, but it's possible
> > to do better (for instance, Haskell).
>
> >http://scala-programming-language.1934581.n4.nabble.com/Point-free-td...
Reply all
Reply to author
Forward
0 new messages