Fluent style support

112 views
Skip to first unread message

Mick Epstrewce

unread,
Jan 21, 2017, 7:35:29 PM1/21/17
to scala-debate
Calling methods in a fluent style is nice, but declaring and implementing the methods of a fluent API is not elegant in Scala because it is necessary:

  1. to return 'this' as the last expression in the methods body and
  2. to declare the result type of the operation as 'this.type' in the API (instead of the conceptually correct type Unit) - a pollution of the conceptual API with technical details

In Smalltalk there is a special syntax construct for cascaded messages:

receiver message1 ; message 2

allowing to use a fluent style without the necessity to prepare any method for this purpose.
But the Smalltalk syntax used for cascaded messages is hard to parse (by readers) when the receiver is itself the result of a message send, e.g. like

receiver message1 message 2 ; message 3

which is equivalent to

(receiver message1) message 2 ; message 3


My proposal for a syntax exension of Scala to express cascaded messages is:

receiver.{message1; message 2}

The compiler can translate this to something like:

 {import receiver; message1; message2}

The compiler should complain and reject this cascaded message expression when there is another object imported (in the context of this expression) which understands some of the messages, because this would lead to ambiguities - and would again be hard to parse (by readers).
Also the dot between receiver and opening curly brace is required to ensure proper interpretation (by the compiler).

Vlad Patryshev

unread,
Jan 21, 2017, 8:41:53 PM1/21/17
to Mick Epstrewce, scala-debate
I got a feeling that this fluent approach is only good in OOP with mutable data.
Alternatively, loops in Scala allow a lot of similar expressiveness.

Thanks,
-Vlad

--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Naftoli Gugenheim

unread,
Jan 22, 2017, 4:38:08 AM1/22/17
to Mick Epstrewce, scala-debate
You can do something like this:

implicit class FluentExtensionMethod[A](private val self: A) {
  def fluently(actions: (A => Unit)*) = actions.foreach(_(self))
}

person.fluently(
  _.setName("Jack"),
  _.setAge(55),
  _.setCountry("US")
)


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.

Mick Epstrewce

unread,
Jan 22, 2017, 4:29:30 PM1/22/17
to scala-debate, mick5...@gmail.com
The solution with an implicit method ('fluently') is good enough in my opinion, so no language extension is needed :-)
Maybe here the use of an operator symbol is justified, e.g. I would rename 'fluently'  to '!!'.

Nils Kilden-Pedersen

unread,
Feb 2, 2017, 1:00:31 PM2/2/17
to Vlad Patryshev, Mick Epstrewce, scala-debate
On Sat, Jan 21, 2017 at 7:41 PM, Vlad Patryshev <vpatr...@gmail.com> wrote:
I got a feeling that this fluent approach is only good in OOP with mutable data.

Why? The signature is mutable/immutable agnostic. For mutable you return this, for immutable you return new.

Vlad Patryshev

unread,
Feb 2, 2017, 2:07:30 PM2/2/17
to Nils Kilden-Pedersen, Mick Epstrewce, scala-debate
Hmm, can you show an example of this?

Nils Kilden-Pedersen

unread,
Feb 2, 2017, 3:56:28 PM2/2/17
to Vlad Patryshev, Mick Epstrewce, scala-debate
trait Foo {
  def name: String
  def name(n: String): this.type

  def limit: Int
  def limit(l: Int): this.type
}

class MutableFoo 
    extends Foo {
  private var _name: String = _
  private var _limit: Int = _

  def name: String = _name
  def name(n: String) = {
    _name = n
    this
  }

  def limit: Int = _limit
  def limit(l: Int) = {
    _limit = l
    this
  }
}

case class ImmutableFoo(name: String, limit: Int) 
    extends Foo {

  def name(n: String) = this.copy(name = n).asInstanceOf[this.type]
  def limit(l: Int) = this.copy(limit = l).asInstanceOf[this.type]
}

Naftoli Gugenheim

unread,
Feb 2, 2017, 4:41:57 PM2/2/17
to Nils Kilden-Pedersen, Vlad Patryshev, Mick Epstrewce, scala-debate

There's a reason you need the asInstanceOf, you aren't really conforming to the singleton type


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.

Nils Kilden-Pedersen

unread,
Feb 4, 2017, 8:02:25 PM2/4/17
to Naftoli Gugenheim, Vlad Patryshev, Mick Epstrewce, scala-debate
On Thu, Feb 2, 2017 at 3:41 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:

There's a reason you need the asInstanceOf, you aren't really conforming to the singleton type

True, but it was sort of a contrived example anyways. My original point still stands, I believe.

 

To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate+unsubscribe@googlegroups.com.

Jasper-M

unread,
Feb 6, 2017, 3:46:56 AM2/6/17
to scala-debate
Well, in your opening post you said the correct type of the methods you have in mind is `Unit`. That sort of implies mutability.

Kind regards,
Jasper

Jasper-M

unread,
Feb 6, 2017, 3:48:15 AM2/6/17
to scala-debate
I'm sorry, it wasn't you :-p But you get my point.

kr,
Jasper

Reply all
Reply to author
Forward
0 new messages