どのようにしてプログラムでメソッドをチェーンブルにすることができますか? -- scala フィールド 関連 問題

How can I programmatically make methods chainable?












7
vote

問題

日本語

私はクラスを持っていると言ってみましょう、そして私はそのメソッドをかなりのものにしたい、私はこのようなことをすることができました:

<事前> <コード> class MyClass { def methodOne(arg1: Any): MyClass = { println("doing stuff") this } def methodTwo(arg1: Any, arg2: Any): MyClass = { println("doing other stuff") this } }

私が探している機能を達成している間は、私の意見ではそれほど優雅ではありません。これをするより良い方法はありますか?

それが可能であると仮定すると、次のようなことができるが、 makeChainable 関数に近づく方法はわかりません。

<事前> <コード> class MyClass { val methodOne: Any => MyClass = makeChainable((arg1: Any) => println("doing stuff")) val methodTwo: (Any, Any) => MyClass = makeChainable((arg1: Any, arg2: Any) => println("doing other stuff")) }
英語

Let's say I have a class and I want to make its methods chainable, I could do something like this:

class MyClass {    def methodOne(arg1: Any): MyClass = {     println("doing stuff")     this   }    def methodTwo(arg1: Any, arg2: Any): MyClass = {     println("doing other stuff")     this   } } 

While that would achieve the functionality I'm looking for, it's not very elegant in my opinion. Is there a better way of doing this?

Assuming it's possible, I'd like to be able to do something like the following, but I'm not sure how to approach the makeChainable function.

class MyClass {    val methodOne: Any => MyClass =      makeChainable((arg1: Any) => println("doing stuff"))    val methodTwo: (Any, Any) => MyClass =      makeChainable((arg1: Any, arg2: Any) => println("doing other stuff"))  } 
</div
  
 
 

回答リスト

4
 
vote
vote
ベストアンサー
 

最初のオプションは最も効率的なもので、もう1つはコードをFunctionオブジェクトにラッピングすることによってオーバーヘッドを導入します。しかし、そのようなラッパーを作成することは確かに可能です。

を定義しましょう <事前> <コード> trait Chainable { final def mkChain(f: () => Any): () => this.type = () => { f(); this; } final def mkChain[A](f: (A) => Any): (A) => this.type = (x: A) => { f(x); this; } final def mkChain[A,B](f: (A,B) => Any): (A,B) => this.type = (x: A, y: B) => { f(x, y); this; } // etc. for other arities }

通知<コード> this.type では、私たちの関数の結果は彼らが定義されているクラスのタイプです。今それをクラス

に混ぜると <事前> <コード> class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); }

methodTwo の結果は MyClass です。


アップデート:暗黙の変換を使用するための別のオプションがあります:

<事前> <コード> trait ToChain { implicit class AsThis(val _underlying: Any) { def chain: ToChain.this.type = ToChain.this } } class MyClass2 extends ToChain { def methodOne(arg1: Any): Unit = println("Doing something") def methodTwo(arg1: String): Unit = println("Doing something else " + arg1) methodOne(3).chain.methodTwo("x"); }

呼び出し chain は、 this.type に何かを変換します。ただし、クラス内でのみ機能するだけで、 new MyClass2.methodOne(3).chain.methodTwo("x") 外部のようなものを呼び出すことはできません。


更新: Unit から<コード> 998876610

への暗黙的な変換に基づく、まだ別のソリューション <事前> <コード> this.type1
 

The first option is the most efficient one, the other one introduces overhead by wrapping code into function object. But it's certainly possible to create such a wrapper. Let's define

trait Chainable {   final def mkChain(f: () => Any): () => this.type =     () => { f(); this; }   final def mkChain[A](f: (A) => Any): (A) => this.type =     (x: A) => { f(x); this; }   final def mkChain[A,B](f: (A,B) => Any): (A,B) => this.type =     (x: A, y: B) => { f(x, y); this; }   // etc. for other arities } 

Notice this.type, it says the result of our functions is the type of the class they're defined in. So now when we mix it into our class

class MyClass extends Chainable {   val methodTwo =     mkChain((x: Any, y: String) => println("Doing something " + y)); } 

the result of methodTwo will be MyClass.


Update: There is another option, to use implicit conversions:

trait ToChain {   implicit class AsThis(val _underlying: Any) {     def chain: ToChain.this.type = ToChain.this   } }  class MyClass2 extends ToChain {   def methodOne(arg1: Any): Unit =     println("Doing something")   def methodTwo(arg1: String): Unit =     println("Doing something else " + arg1)    methodOne(3).chain.methodTwo("x"); } 

Calling chain converts anything to this.type. However it only works inside the class, you can't call something like new MyClass2.methodOne(3).chain.methodTwo("x") outside.


Update: Yet another solution, based on implicit conversion from Unit to this:

import scala.language.implicitConversions  class Chain[A](val x: A) {   implicit def unitToThis(unit: Unit): A = x; } implicit def unchain[A](c: Chain[A]): A = c.x;  // Usage:  val r: MyClass = new Chain(new MyClass) {   x.methodOne(1).methodTwo(2,3); } 
</div
 
 
         
         
2
 
vote

単項関数のための this.type2 を実装するのは簡単ですが、上昇をサポートしたい場合は毛深いです。メソッド2を実行できる唯一の方法は、すべての存在について個別の<コード> this.type3 を作成しない限り、メソッドをタプル化し、 this.type4 を渡してください。それを外にします。

<事前> <コード> this.type5

しかし、オピニングのために私を許してください - 呼び出しチェーンは一般的にあなたがScalaよりも表現性が低い他の言語で取るショートカットです。 JavaユーザーにAPIを作るためにこれを行っていない限り、これは本当に悪い考えであると思います。

これは私がまだ行っていない一つの選択肢です。

<事前> <コード> chain16

編集:

よりエレガントな方法は「Kestrel Combinator」を定義することです。私はこのアプローチは合法的なものだと思います:)

<事前> <コード> this.type7
 

It's easy to implement makeChainable for unary function, but it gets hairy if you want to support higher arity. The only way I can see to do method two, unless you want to write a separate makeChainable for every arity, is to tuple the method, pass it through makeChainable, and then untuple it.

class MyClass {    def methodOne: Any => MyClass = makeChainable {     (arg1: Any) => println("doing stuff")   }    def methodTwo: (Any, Any) => MyClass = Function untupled makeChainable {(     (arg1: Any, arg2: Any) => println("doing other stuff")   ).tupled}    def makeChainable[A](f: (A) => Unit): (A => MyClass) = { a: A => f(a); this }  }  new MyClass().methodOne("a").methodTwo("b", "c") 

But - and please forgive me for opining - invocation chaining is generally a shortcut you take in other languages that are less expressive than Scala. Unless you're doing this to make an API for Java users, I think this is a really bad idea.

Here's one alternative, which I still would never do, to accomplish roughly the style you're going for in a way that's less invasive:

class MyClass {   def methodOne(a: Any) { println("doing stuff") }   def methodTwo(a: Any, b: Any) { println("doing other stuff") }   def apply(fs: (MyClass => Unit)*) { fs.foreach(f => f(this)) } }  new MyClass()(_.methodOne("a"), _.methodTwo("b", "c")) 

Edit:

A more elegant way would be to define a "kestrel combinator". I do think this approach is legit :)

class MyClass {   def methodOne(a: Any) { println("doing stuff") }   def methodTwo(a: Any, b: Any) { println("doing other stuff") } }  implicit class Kestrel[A](x: A) {   def ~(f: A => Unit): A = { f(x); x } }  new MyClass() ~ (_.methodOne("a")) ~ (_.methodTwo("b", "c")) 
</div
 
 
1
 
vote

私はこれがおそらくあなたが探しているものではないことを知っていますが、あなたの説明は私に多くの <コード> this.type8 clojure 。

私は、<コード> this.type9 をScalaに移植するさまざまな方法について議論した2つのスレッドを見つけました。

clojureの「doto」のようなもの?

Re:Clojureの「doto」のようなもの?<これは実際にはAだと思いますどういうわけか個別のスレッドとして終わった最初のスレッドへの返信)

これらのスレッドを通して見る最も簡単な方法は、 class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); } 0 を短い名前で短い名前で、繰り返しステートメントの受信側として使用することです。

または暗黙的値クラスの作成(Scala 2.10で利用可能):

<事前> <コード> class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); } 1

他の答えはあなたが探しているものははるかに多くありますが、私は別の言語を指摘したいと思っていました。

 

I know this isn't probably exactly what you're looking for, but your description reminds me a lot of the doto construct in Clojure.

I found a couple of threads discussing the different ways of porting doto to Scala:

something like Clojure's "doto"?

Re: something like Clojure's "doto"? (I think this was actually a reply to the first thread that somehow ended up as a separate thread)

Looking through those threads, it looks like the easiest way is just to make a val with a short name and use that as the receiver of repeated statements.

Or create an implicit value class (available in Scala 2.10):

implicit class Doto[A](val value: A) extends AnyVal {   def doto(statements: (A => Any)*): A = {     statements.foreach((f: A => Any) => f(value))     value   } } new MyClass2().doto(_.methodOne(3), _.methodTwo("x")); 

The other answers are much more what you're looking for, but I just wanted to point out an alternate approach another language took for working around non-chainable method calls.

</div
 
 
0
 
vote

これが最初の場所にある賢明な問題の問題を残すことは、形状のない:

<事前> <コード> class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); } 2

、次に

<事前> <コード> class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); } 3

これは class MyClass extends Chainable { val methodTwo = mkChain((x: Any, y: String) => println("Doing something " + y)); } 4

のために機能します。
 

Leaving aside the question of how wise this is in the first place, it's pretty easy to implement in a type-safe and boilerplate-free way with Shapeless:

import shapeless._  trait ChainableUtils {   def makeChainable[F, Args <: HList](f: F)(implicit     in: FnHListerAux[F, Args => Unit],     out: FnUnHLister[Args => this.type]   ) = out((a: Args) => { in(f)(a); this }) } 

And then:

scala> class MyClass extends ChainableUtils {      |   def func1 = makeChainable((i: Int) => println("Doing stuff."))      |   def func2 = makeChainable((a: Any, b: Any) =>       |     println("Doing other stuff."))      | } defined class MyClass  scala> val myInstance = new MyClass myInstance: MyClass = MyClass@6c86b570  scala> myInstance.func1(1).func2('a, "a").func1(42) Doing stuff. Doing other stuff. Doing stuff. res0: myInstance.type = MyClass@6c86b570 

This will work for any FunctionN.

</div
 
 

関連する質問

32  スカラユニットタイプ  ( Scala unit type ) 
CSVファイルの解析にOpenCSVを使用し、コードはです。 <事前> <コード> while( (line = reader.readNext()) != null ) { .... } 私はコンパイラの警告を言った: <事前> <コード> comp...

2  Mavenを使ったエレベータープロジェクトのために私のWebアプリケーションフォルダにHTMLファイルをどこに置きますか?  ( Where do i put html files in my web app folder for a lift project with maven ) 
スカラのためのフレームワークを持ち上げるのは新しいです。何らかの理由で、index.htmlはWeb-Appディレクトリにあり、jettyを起動するとき、 http:// localhost:8080 / はそのindex.htmlファイルを正しく表します。た...

6  Scalaでは、Javaアプリケーションのどの部分を書いてください。  ( What parts of a java application should be written in scala ) 
Struts 2を使用してJavaアプリケーションを書いていますが、今はハイブリッドJava&AMPにしたいと思います。代わりにScalaプロジェクト。私はスカラで多くの経験がありませんが、私は大学でHaskellを学びました - 私は本当に機能的なプログラ...

17  Scalaのプレースホルダを持つ文字列に置き換えます  ( Substitute values in a string with placeholders in scala ) 
スカラを使い始め、問題解決への機能的アプローチをよりよく理解したいと思います。 最初にパラメータのプレースホルダーがある文字列のペアがあり、ペアに代わる値があります。例えば 「ID&GT、$ 1と$ 2のような名前のCOL1を選択します。 "パラメータ:$ 1...

3  Spark DataFrameの単一エントリに複数の個々のエントリをマージする  ( Merge multiple individual entries to single entry in spark dataframe ) 
こののように見えるパーティションを持っているとします <事前> <コード> part1: {"customerId":"1","name":"a"} {"customerId":"2","name":"b"} これのスキーマをのようなものに変更したいとし...

-3  ScalaでMongoDBデータベースにアクセスする方法  ( How to access mongodb database in scala ) 
Scala SwingアプリケーションからMongoDBデータベースにアクセスする方法を知りたいのですが。 私はScalaの新機能です.I MongoDBのMyDBという名前のデータベースとStudenという名前のコレクション(Rollno、Name、Age...

0  Scalaの現在の状態/値に対する継続的に実行されている操作を照会する  ( Querying a continously running operation for its current state value in scala ) 
私は値を継続的に更新する手順を持っています。現在の値の操作を定期的に問い合わせることができます。私の特定の例では、すべてのアップデートを改善と見なすことができ、プロシージャは最終的な最良の答えに収束しますが、私は中間結果へのアクセスが必要です。ループが実行され...

34  良いClojureベンチマークはありますか?  ( Are there any good clojure benchmarks ) 
編集: Clojureベンチマークはベンチマークゲーム 。 私はこの質問コミュニティWikiを作り、他の人を更新し続けるように勧めました。 は、Clojureのパフォーマンスのベンチマークを認識していますか? 私は自分のものをいくつかしました(正式な...

4  Scalaの並行処理  ( Concurrent processing in scala ) 
Scalaで同時プログラミングを使用しようとしています。 この例 StackOverFlow、プロジェクトeulerの問題1 に基づいてプログラムを作りました。 私は3つの方法を試してみました:最初のものは単純な実行で、パラレリズムはありません。 NS ex...

37  休止状態とスカラ[閉じた]  ( Hibernate and scala ) 
閉鎖。この質問はもっと焦点を絞ったにする必要があります。現在答えを受け付けていません。 この質問を改善したいですか? ...




© 2022 cndgn.com All Rights Reserved. Q&Aハウス 全著作権所有